; docformat = 'rst'
;
; NAME:
;   cgBarPlot
;
; PURPOSE:
;   Provides a device-independent and color-model-independent way to create a bar
;   plot or to overplot on an existing bar plot.
;
;******************************************************************************************;
;                                                                                          ;
;  Copyright (c) 2011, by Fanning Software Consulting, Inc. All rights reserved.           ;
;                                                                                          ;
;  Redistribution and use in source and binary forms, with or without                      ;
;  modification, are permitted provided that the following conditions are met:             ;
;                                                                                          ;
;      * Redistributions of source code must retain the above copyright                    ;
;        notice, this list of conditions and the following disclaimer.                     ;
;      * Redistributions in binary form must reproduce the above copyright                 ;
;        notice, this list of conditions and the following disclaimer in the               ;
;        documentation and/or other materials provided with the distribution.              ;
;      * Neither the name of Fanning Software Consulting, Inc. nor the names of its        ;
;        contributors may be used to endorse or promote products derived from this         ;
;        software without specific prior written permission.                               ;
;                                                                                          ;
;  THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY        ;
;  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES    ;
;  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT     ;
;  SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,             ;
;  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED    ;
;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         ;
;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND             ;
;  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT              ;
;  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           ;
;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                            ;
;******************************************************************************************;
;
;+
; Provides a device-independent and color-model-independent way to create a bar
; plot or to overplot on an existing bar plot.
; 
; The program requires the `Coyote Library <http://www.idlcoyote.com/documents/programs.php>`
; to be installed on your machine.
;
; :Categories:
;    Graphics
;    
; :Params:
;    values: in, required
;        A vector containing the values to be represented by bars. Each element in
;        the vector will be represented by a bar. 
;       
; :Keywords:
;     addcmd: in, optional, type=boolean, default=0
;        Set this keyword to add the command to a cgWindow resizeable graphics window.
;     axiscolor: in, optional, type=string/integer, default='black'
;        If this keyword is a string, the name of the axis color. By default, 'black'.
;        Otherwise, the keyword is assumed to be a color index into the current color table.
;     background: in, optional, type=string/integer, default='white'
;        If this keyword is a string, the name of the background color. By default, 'white'.
;        Otherwise, the keyword is assumed to be a color index into the current color table.
;     barcoords: out, optional, type=float
;        A vector of values for the center point of the bars.
;     barnames: in, optional, type=string, default=""
;        A string array, containing one string label per bar. If the bars are vertical, 
;        the labels are placed beneath them.  If horizontal (rotated) bars are specified, 
;        the labels are placed to the left of the bars.
;     baroffset: in, optional, type=float, default=barspace/barwidth*1.5
;        A scalar that specifies the offset to be applied to the first bar, in units of 
;        "nominal bar width".  This keyword allows, for example, different groups of bars 
;        to be overplotted on the same graph.  
;     barspace: in, optional, type=float, default=barwidth*0.20
;        A scalar that specifies, in units of "nominal bar width", the spacing between bars.  
;        For example, if BARSPACE is 1.0, then all bars will have one bar-width of space 
;        between them. If not specified, the bars are spaced apart by 20% of the bar width.
;     barthick: in, optional, type=float, default=1.0
;        A scalar that specifies the thickness of the bar outlines. 
;     barwidth: in, optional, type=float
;         A floating-point value that specifies the width of the bars in units of "nominal bar 
;         width".  The nominal bar width is computed so that all the bars (and the space 
;         between them will fill the available space (optionally controlled with the BASERANGE 
;         keyword).
;     baselines: in, optional, type=float, default=0.0
;         A vector, the same size as VALUES, that contains the base value associated 
;         with each bar.  If not specified, a base value of zero is used for all bars.
;     baserange: in, optional, type=float, default=1.0
;         A floating-point scalar in the range 0.0 to 1.0, that determines the fraction of 
;         the total available plotting area (in the direction perpendicular to the bars) to 
;         be used. If not specified, the full available area is used.
;     colors: in, optional, type=varies
;         A vector of color values, the same size as VALUES, containing either the color
;         names, 24-bit color values, or color index numbers (byte values) of the colors to
;         be used for the bars. If not specified, the colors are selected based on the 
;         available colors in the current color table.
;     labelskip: in, optional, type=integer, default=1
;         Normally, every bar is labelled. IDL has a maximum number of 60 labels. This keyword
;         allows you to control how bars are labelled and to work around the 60 label limit.
;         Set this keyword the number of labels to skip. For example, if you had 90 bars, you
;         could set Labelskip=3 and have 30 labels. If undefined, Labelskip will always be set 
;         so there are less than 60 labels on the plot.
;     layout: in, optional, type=intarr(3)
;         This keyword specifies a grid with a graphics window and determines where the
;         graphic should appear. The syntax of LAYOUT is three numbers: [ncolumns, nrows, location].
;         The grid is determined by the number of columns (ncolumns) by the number of 
;         rows (nrows). The location of the graphic is determined by the third number. The
;         grid numbering starts in the upper left (1) and goes sequentually by column and then
;         by row.
;     noerase: in, optional, type=boolean, default=0
;         Set this keyword to draw the plot without erasing the display first.
;     outfilename: in, optional, type=string
;        If the `Output` keyword is set, the user will be asked to supply an output
;        filename, unless this keyword is set to a non-null string. In that case, the
;        value of this keyword will be used as the filename and there will be no dialog
;        presented to the user.
;     outline: in, optional, type=boolean, default=0
;         Set this keyword to draw an outline around each bar in the OPLOTCOLORS.
;     output: in, optional, type=string, default=""
;        Set this keyword to the type of output desired. Possible values are these::
;            
;            'PS'   - PostScript file
;            'EPS'  - Encapsulated PostScript file
;            'PDF'  - PDF file
;            'BMP'  - BMP raster file
;            'GIF'  - GIF raster file
;            'JPEG' - JPEG raster file
;            'PNG'  - PNG raster file
;            'TIFF' - TIFF raster file
;            
;        Or, you can simply set this keyword to the name of the output file, and the type of
;        file desired will be determined by the file extension. If you use this option, the
;        user will not be prompted to supply the name of the output file.
;            
;        All raster file output is created through PostScript intermediate files (the
;        PostScript files will be deleted), so ImageMagick and Ghostview MUST be installed 
;        to produce anything other than PostScript output. (See cgPS2PDF and cgPS_Close for 
;        details.) And also note that you should NOT use this keyword when doing multiple 
;        plots. The keyword is to be used as a convenient way to get PostScript or raster 
;        output for a single graphics command. Output parameters can be set with cgWindow_SetDefs.
;     oplotcolors: in, optional, type=varies, default='charcoal'
;         A vector of color values, similar to colors for overplot outlines on the bars.
;         If a scalar value (e.g., "charcoal") the same value is used for all outlines.
;     overplot: in, optional, type=boolean, default=0
;         Set this keyword if you wish to overplot data on an already exisiting set of
;         axes. It is like calling the IDL OPLOT command.
;     position: in, optional, type=fltarr(4)
;         The usual four-element normalized position vector for the Plot comamnd. 
;     rotate: in, optional, type=boolean, default=0
;         If set, this keyword indicates that horizontal rather than vertical bars should 
;         be drawn.  The bases of horizontal bars are on the left, "Y" axis and the bars 
;         extend to the right.
;     title: in, optional, type=string, default=""
;         The title of the plot, if supplied.
;     window: in, optional, type=boolean, default=0
;         Set this keyword if you want to display the plot in a resizable graphics window.
;     xstyle: in, optional, type=boolean, default=1
;         The normal XSTYLE keyword. See "Graphics Keywords" in on-line help.
;     xtitle: in, optional, type=string, default=""
;         The X title of the plot, if supplied.
;     ystyle: in, optional, type=boolean, default=1
;         The normal YSTYLE keyword. See "Graphics Keywords" in on-line help.
;     ytitle: in, optional, type=string, default=""
;         The Y title of the plot, if supplied.
;     _ref_extra: in, optional, type=any
;        Any keyword appropriate for the IDL Plot command is allowed in the program.
;          
; :Examples:
;    To create bar plots of four different bar classes. Example uses AL_Legend from
;    the NASA Astronomy Library. Comment out if not available.::
;       
;    Create data::
;    
;       seed = -5L
;       data1 = RandomU(seed, 5) * 10.0
;       data2 = RandomU(seed, 5) * 7.5
;       data3 = RandomU(seed, 5) * 7.5
;       data4 = RandomU(seed, 5) * 10.0
;    
;    Display as four different plots::
;    
;       !P.Multi=[0,2,2]
;       cgDisplay, WID=0
;       cgBarPlot, data1, /OUTLINE, OPLOTCOLOR='charcoal', YRANGE=[0, 12], COLORS='red', XTITLE='Class 1'
;       cgBarPlot, data2, /OUTLINE, OPLOTCOLOR='charcoal', YRANGE=[0, 12], COLORS='blue', XTITLE='Class 2'
;       cgBarPlot, data3, /OUTLINE, OPLOTCOLOR='charcoal', YRANGE=[0, 12], COLORS='gold', XTITLE='Class 3'
;       cgBarPlot, data4, /OUTLINE, OPLOTCOLOR='charcoal', YRANGE=[0, 12], COLORS='dark green', XTITLE='Class 4'
;       !P.Multi=0
;
;    Display as accumulated bar plots::
;    
;       cgDisplay, WID=1
;       barnames = ['Exp 1', 'Exp 2', 'Exp 3', 'Exp 4', 'Exp 5']
;       cgBarPlot, data1, YRANGE=[0, 35], COLORS='red', BARNAMES=barnames
;       cgBarplot, data2, /OVERPLOT, BASELINE=data1, COLORS='blue'
;       cgBarplot, data3, /OVERPLOT, BASELINE=data1+data2, COLORS='gold'
;       cgBarplot, data4, /OVERPLOT, BASELINE=data1+data2+data3, COLORS='dark green'
;       colors = ['red', 'blue', 'gold', 'dark green']
;       items = ['Class 1', 'Class 2', 'Class 3', 'Class 4']
;       Al_Legend, items, /FILL, PSYM=Replicate(15,4), COLORS=colors, SYMSIZE=Replicate(1.75,4), $
;           POSITION=[0.20, 0.92], /NORMAL, CHARSIZE=cgDefCharSize()
;
;    Display all on same plot::
;    
;       cgDisplay, WID=2
;       cgBarPlot, data1, YRANGE=[0, 12], BAROFFSET=2.5, BASERANGE=0.20, COLORS='red'
;       cgBarplot, data2, /OVERPLOT, BAROFFSET=9.5, BASERANGE=0.20, COLORS='blue'
;       cgBarplot, data3, /OVERPLOT, BAROFFSET=17.0, BASERANGE=0.20, COLORS='gold'
;       cgBarplot, data4, /OVERPLOT, BAROFFSET=24.5, BASERANGE=0.20, COLORS='dark green'
;       colors = ['red', 'blue', 'gold', 'dark green']
;       items = ['Class 1', 'Class 2', 'Class 3', 'Class 4']
;       Al_Legend, items, /FILL, PSYM=Replicate(15,4), COLORS=colors, SYMSIZE=Replicate(1.75,4), $
;           POSITION=[0.45, 0.92], /NORMAL, CHARSIZE=cgDefCharSize()
;
; .. image:: cgbarplot.png
; 
; :Author:
;       FANNING SOFTWARE CONSULTING::
;           David W. Fanning 
;           1645 Sheely Drive
;           Fort Collins, CO 80526 USA
;           Phone: 970-221-0438
;           E-mail: david@idlcoyote.com
;           Coyote's Guide to IDL Programming: http://www.idlcoyote.com
;
; :History:
;     Change History::
;        Written, 3 October 2011. DWF. Written as a straight-forward translation of
;            the IDL Bar_Plot program. In retrospect, maybe not the best idea, as I 
;            don't think the Bar_Plot program actually works and the interface is clunky.
;            But, this is a start. The interface may change to something more elegant over
;            time.
;         Updated the BACKGROUND color selection from lessons learned in 27 Oct 2011 cgContour 
;             corrections. 27 Oct 2011. DWF.
;         Added the ability to send the output directly to a file via the OUTPUT keyword. 9 Dec 2011, DWF.
;         PostScript, PDF, and Imagemagick parameters can now be tailored with cgWindow_SetDefs. 14 Dec 2001. DWF.
;         Modified to use cgDefaultColor for default color selection. 24 Dec 2011. DWF.
;         Fixed a problem with keyword inheritance. Added XStyle and YStyle keywords. 25 Jan 2012. DWF.
;         Added BARCOORDS keyword to return bar plot locations. 25 Jan 2012.
;         Changes to allow better default colors, based on changes to cgColor and cgDefaultColor. 1 Feb 2012. DWF.
;         Small problem with setting range keywords and with extraneous marks on the bar plot axis fixed. 6 Feb 2012. DWF.
;         Added the ability to use escape characters in plot titles to specify cgSymbol symbols. 27 July 2012. DWF.
;         Fixed a typo that was interfering with the YTITLE keyword. 3 Oct 2012. DWF.
;         Fixed a bug in the interaction of the NOERASE and OVERPLOT keywords. 14 Jan 2013. DWF.
;         Added a BARTHICK keyword to change the thickness of the bar outlines. 28 Feb 2013. DWF.
;         Further work checking for NANs in the display of the data. NAN data set to length of 0. 3 Sept 2013. DWF.
;         Added LABELSKIP keyword and make sure number of labels doesn't exceed 60. 8 Feb 2014. DWF.
;         Somehow the BARCOORDS coordinates have gone on walk-about. Restored. 15 April 2014. DWF.
;         
; :Copyright:
;     Copyright (c) 2011-2014, Fanning Software Consulting, Inc.
;-
PRO cgBarPlot, values, $
    ADDCMD=addcmd, $
    AXISCOLOR=saxiscolor, $
    BACKGROUND=sbackground, $
    BARCOORDS=barcoords, $
    BARNAMES=barnamesIn, $
    BAROFFSET=baroffsetIn, $
    BARSPACE=barspaceIn, $
    BARTHICK=barthickIn, $
    BARWIDTH=barwidthIn, $
    BASELINES=baselines, $
    BASERANGE=baserange, $
    COLORS=scolors, $
    LABELSKIP=labelskip, $
    LAYOUT=layout, $
    NOERASE=noerase, $
    OUTFILENAME=outfilename, $
    OUTLINE=outline, $
    OUTPUT=output, $
    OPLOTCOLORS=oplotcolors, $
    OVERPLOT=overplot, $
    POSITION=position, $
    RANGE=range, $
    ROTATE=rotate,  $
    TITLE=title,  $
    WINDOW=window, $
    XSTYLE=xstyle, $
    XTITLE=xtitle, $
    YSTYLE=ystyle, $
    YTITLE=ytitle, $
    _REF_EXTRA=extra
    
    Compile_Opt idl2
    
    Catch, theError
    IF theError NE 0 THEN BEGIN
       Catch, /CANCEL
       void = cgErrorMsg()
        IF N_Elements(thisMulti) NE 0 THEN !P.Multi = thisMulti
        IF N_Elements(currentState) NE 0 THEN cgSetColorState, currentState
        RETURN
    ENDIF
    
    ; Set up PostScript device for working with colors.
    IF !D.Name EQ 'PS' THEN Device, COLOR=1, BITS_PER_PIXEL=8
    
    ; Check parameters.
    IF (N_Elements(values) EQ 0) THEN BEGIN
        Print, 'USE SYNTAX: cgBarPlot, values'
        RETURN
    ENDIF
    
    ; Pay attention to !P.Noerase in setting the NOERASE kewyord. This must be
    ; done BEFORE checking the LAYOUT properties.
    IF !P.NoErase NE 0 THEN noerase = !P.NoErase ELSE noerase = Keyword_Set(noerase)
    
    ; Do they want this plot in a resizeable graphics window?
    IF Keyword_Set(addcmd) THEN window = 1
    IF Keyword_Set(window) AND ((!D.Flags AND 256) NE 0) THEN BEGIN
    
        ; If you are using a layout, you can't ever erase.
        IF N_Elements(layout) NE 0 THEN noerase = 1
        
        ; Special treatment for overplotting or adding a command.
        IF Keyword_Set(overplot) OR Keyword_Set(addcmd) THEN BEGIN
            cgWindow, 'cgBarPlot', values, $
                ADDCMD=1, $
                AXISCOLOR=saxiscolor, $
                BACKGROUND=sbackground, $
                BARCOORDS=barcoords, $
                BARNAMES=barnamesIn, $
                BAROFFSET=baroffsetIn, $
                BARSPACE=barspaceIn, $
                BARTHICK=barthickIn, $
                BARWIDTH=barwidthIn, $
                BASELINES=baselines, $
                BASERANGE=baserange, $
                COLORS=scolors, $
                LABELSKIP=labelskip, $
                LAYOUT=layout, $
                NOERASE=noerase, $
                OPLOTCOLORS=oplotcolors, $
                OUTLINE=outline, $
                OVERPLOT=overplot, $
                POSITION=position, $
                RANGE=range, $
                ROTATE=rotate,  $
                XSTYLE=xstyle, $
                XTITLE=xtitle, $
                YSTYLE=ystyle, $
                YTITLE=ytitle, $
               _EXTRA=extra
             RETURN
       ENDIF
        
        ; Open a new window or replace the current commands, as required.
        currentWindow = cgQuery(/CURRENT, COUNT=wincnt)
        IF wincnt EQ 0 THEN replaceCmd = 0 ELSE replaceCmd=1
            cgWindow, 'cgBarPlot', values, $
                AXISCOLOR=saxiscolor, $
                BACKGROUND=sbackground, $
                BARCOORDS=barcoords, $
                BARNAMES=barnamesIn, $
                BAROFFSET=baroffsetIn, $
                BARSPACE=barspaceIn, $
                BARTHICK=barthickIn, $
                BARWIDTH=barwidthIn, $
                BASELINES=baselines, $
                BASERANGE=baserange, $
                COLORS=scolors, $
                LABELSKIP=labelskip, $
                LAYOUT=layout, $
                NOERASE=noerase, $
                OPLOTCOLORS=oplotcolors, $
                OUTLINE=outline, $
                OVERPLOT=overplot, $
                POSITION=position, $
                RANGE=range, $
                ROTATE=rotate,  $
                REPLACECMD=replaceCmd, $
                TITLE=title,  $
                XSTYLE=xstyle, $
                XTITLE=xtitle, $
                YSTYLE=ystyle, $
                YTITLE=ytitle, $
                _EXTRA=extra
            
         RETURN
    ENDIF
    
    ; Are we doing some kind of output?
    IF (N_Elements(output) NE 0) && (output NE "") THEN BEGIN
    
       ; If the output string has a dot character, then this must be a
       ; filename, and we will determine the type of file from the filename extension.
       IF StrPos(output, '.') NE -1 THEN BEGIN
             root_name = cgRootName(output, DIRECTORY=theDir, EXTENSION=ext)
             IF theDir EQ "" THEN CD, CURRENT=theDir
             outfilename = output
             outputSelection = StrUpCase(ext)
       ENDIF
    
       IF N_Elements(outputSelection) EQ 0 THEN outputSelection = StrUpCase(output)
       typeOfOutput = ['PS','EPS','PDF','BMP','GIF','JPEG','JPG','PNG','TIFF', 'TIF']
       void = Where(typeOfOutput EQ outputSelection, count)
       IF count EQ 0 THEN Message, 'Cannot find ' + outputSelection + ' in allowed output types.'
       
       ; Set things up.
       CASE outputSelection OF
          'PS': BEGIN
              ext = '.ps'
              delete_ps = 0
              END    
          'EPS': BEGIN
              ext = '.eps'
              encapsulated = 1
              delete_ps = 0
              END
          'PDF': BEGIN
              ext = '.pdf'
              pdf_flag = 1
              delete_ps = 1
              END     
          'BMP': BEGIN
              ext = '.bmp'
              bmp_flag = 1
              delete_ps = 1
              END      
          'GIF': BEGIN
              ext = '.gif'
              gif_flag = 1
              delete_ps = 1
              END
          'JPEG': BEGIN
              ext = '.jpg'
              jpeg_flag = 1
              delete_ps = 1
              END      
          'JPG': BEGIN
              ext = '.jpg'
              jpeg_flag = 1
              delete_ps = 1
              END
          'PNG': BEGIN
              ext = '.png'
              png_flag = 1
              delete_ps = 1
              END      
          'TIFF': BEGIN
              ext = '.tif'
              tiff_flag = 1
              delete_ps = 1
              END
          'TIF': BEGIN
              ext = '.tif'
              tiff_flag = 1
              delete_ps = 1
              END    
       ENDCASE
              
       ; Do you need a filename?
       IF ( (N_Elements(outfilename) EQ 0) || (outfilename EQ "") ) THEN BEGIN 
            filename = 'cgplot' + ext
            outfilename = cgPickfile(FILE=filename, TITLE='Select Output File Name...', $
                FILTER=ext, /WRITE)
            IF outfilename EQ "" THEN RETURN
       ENDIF
       
       ; We need to know the root name of the file, because we have to make a PostScript
       ; file of the same name. At least we do if the type is not PS or EPS.
       IF (outputSelection NE 'PS') && (outputSelection NE 'EPS') THEN BEGIN
           root_name = cgRootName(outfilename, DIRECTORY=theDir)
           IF theDir EQ "" THEN CD, CURRENT=theDir
           ps_filename = Filepath(ROOT_DIR=theDir, root_name + '.ps')
       ENDIF ELSE ps_filename = outfilename
       
       ; Get the output default values.
       cgWindow_GetDefs, $
         PS_Charsize = ps_charsize, $          ; The PostScript character size.
         PS_FONT = ps_font, $                  ; Select the font for PostScript output.
         PS_Decomposed = ps_decomposed, $      ; Sets the PostScript color mode.
         PS_Delete = ps_delete, $              ; Delete PS file when making IM raster.
         PS_Metric = ps_metric, $              ; Select metric measurements in PostScript output.
         PS_Scale_factor = ps_scale_factor, $  ; Select the scale factor for PostScript output.
         PS_TT_Font = ps_tt_font               ; Select the true-type font to use for PostScript output.   
       
       ; Set up the PostScript device.
       cgPS_Open, $
          CHARSIZE=ps_charsize, $
          DECOMPOSED=ps_decomposed, $
          FILENAME=ps_filename, $
          FONT=ps_font , $
          ENCAPSULATED=encapsulated, $
          METRIC=ps_metric, $
          SCALE_FACTOR=ps_scale_factor, $
          TT_FONT=ps_tt_font, $
          QUIET=1
    
    ENDIF
    
  ; The number of bars to draw.
    nbars = N_Elements(values)

    ; Get the current color table vectors.
    TVLCT, rr, gg, bb, /GET
    
    ; Going to do this in decomposed color, if possible.
    cgSetColorState, 1, CURRENTSTATE=currentState
    
    ; If current state is "indexed color" and colors are represented as long integers then "fix" them.
    IF (currentState EQ 0) THEN BEGIN
      IF Size(sbackground, /TNAME) EQ 'LONG' THEN sbackground = Fix(sbackground)
      IF Size(saxiscolor, /TNAME) EQ 'LONG' THEN saxiscolor = Fix(saxiscolor)
      IF Size(scolors, /TNAME) EQ 'LONG' THEN scolors = Fix(scolors)
    ENDIF
    
    ; Set up the layout, if necessary.
    IF N_Elements(layout) NE 0 THEN BEGIN
       thisMulti = !P.Multi
       totalPlots = layout[0]*layout[1]
       !P.Multi = [0,layout[0], layout[1], 0, 0]
       IF layout[2] EQ 1 THEN BEGIN
            noerase = 1
            !P.Multi[0] = 0
       ENDIF ELSE BEGIN
            !P.Multi[0] = totalPlots - layout[2] + 1
       ENDELSE
    ENDIF

    ; Check the keywords.
    background = cgDefaultColor(sbackground, /BACKGROUND, TRADITIONAL=traditional)
    axisColor = cgDefaultColor(saxisColor, TRADITIONAL=traditional)
    color = cgDefaultColor(sColor, DEFAULT=axisColor, TRADITIONAL=traditional)
        
    ; Load the drawing colors. If needed create a window first, so the drawing
    ; colors are correct for the window you want to draw into.
    IF ((!D.Flags AND 256) NE 0) && (!D.Window LT 0) THEN cgDisplay

    ; If colors are identical, do something about it.
    IF ColorsAreIdentical(background, axiscolor) THEN BEGIN
        IF ((!D.Flags AND 256) NE 0) THEN BEGIN
           IF (!P.Multi[0] EQ 0) && (~Keyword_Set(overplot) && ~noerase) THEN cgErase, background
        ENDIF
        axiscolor = 'OPPOSITE'
    ENDIF
    
    ; Check other keywords.
    addcmd = Keyword_Set(addcmd)
    barnames = (N_Elements(barnamesIn) EQ 0) ? StrArr(nbars) + ' ' : barnamesIn
    IF N_Elements(barnames) NE nbars THEN Message, 'Number of bar names does not equal the number of bars.'
    barspace = (N_Elements(barspaceIn) EQ 0) ? 0.2 : Float(barspaceIn)
    barthick = (N_Elements(barthickIn) EQ 0) ? 1.0 : Float(barthickIn)
    barwidth = (N_Elements(barwidthIn) EQ 0) ? 1.0 - barspace - (barspace / nbars) : barwidthIn    
    baroffset = (N_Elements(baroffsetIn) EQ 0) ? barspace/barwidth*1.5 : baroffsetIn
    IF N_Elements(baselines) EQ 0 THEN baselines = IntArr(nbars)
    IF N_Elements(baserange) EQ 0 THEN baserange = 0.95
    IF N_Elements(scolors) EQ 0 THEN BEGIN
        scolors=String(Byte((256.0/nbars)*(Indgen(nbars)+0.5)), FORMAT='(i0)')
    ENDIF
    colors = cgDefaultColor(scolors)
    IF N_Elements(colors) EQ 1 THEN colors = Replicate(colors, nbars)
    IF N_Elements(colors) NE nbars THEN $
       Message, 'There is a mismatch between the number of bars and number of bar colors.'
    outline = Keyword_Set(outline)
    overplot = Keyword_Set(overplot)
    IF N_Elements(oplotcolors) EQ 0 THEN oplotColors = Replicate('charcoal', nbars)
    IF N_Elements(oplotColors) EQ 1 THEN oplotColors = Replicate(oplotColors, nbars)
    IF N_Elements(oplotColors) NE nbars THEN $
       Message, 'There is a mismatch between the number of bars and number of overplot colors.'
    noerase = Keyword_set(noerase)
    rotate = Keyword_Set(rotate)
    IF N_Elements(title) EQ 0 THEN title = "" ELSE title = cgCheckForSymbols(title)
    IF rotate THEN BEGIN
        IF N_Elements(ystyle) EQ 0 THEN ystyle = 1 ELSE ystyle = 1 XOR ystyle
    ENDIF ELSE BEGIN
        IF N_Elements(xstyle) EQ 0 THEN xstyle = 1 ELSE xstyle = 1 XOR xstyle    
    ENDELSE
    window = Keyword_Set(window)
    IF N_Elements(xtitle) EQ 0 THEN xtitle = "" ELSE xtitle = cgCheckForSymbols(xtitle)
    IF N_Elements(ytitle) EQ 0 THEN ytitle = "" ELSE ytitle = cgCheckForSymbols(ytitle)
    
    
    ; Find the min and max of the data range. Comparing baselines and values.
    mnB = Min(baselines, MAX=mxB, /NAN)
    mnV = Min(values, MAX=mxV, /NAN)
    IF N_Elements(range) EQ 0 THEN range=[mnB < mnV, mxB > (mxV + 0.1*mxV)]      
    
    ; Do you want horizontal bars, rather than vertical?
    IF (rotate) THEN BEGIN                
       xrange = range             ;Or, use range specIFied
       yrange = [0, N_Elements(values)]        ;Axis perpend. to bars
       yticks = 1                    ;Suppress ticks in plot
       yticklen = 0.001
       ytickname = strarr(2)+' '
       xticks = 0
       xtickname = strarr(1)+''
    ENDIF ELSE BEGIN                   ;Vertical bars
       yrange = range 
       xrange = [0, N_Elements(values)]                 ;Axis perpend. to bars
       xticks = 1                    ;Suppress ticks in plot
       xticklen=0.001
       xtickname = strarr(2)+' '
       yticks = 0
       ytickname = strarr(1)+''
    ENDELSE

   ; If you are creating your own plot, do it here without data.
    IF (overplot EQ 0) || (noerase EQ 1) THEN BEGIN  ;Create new plot, no data
        cgPlot,[values],/nodata,title=title,xtitle=xtitle,ytitle=ytitle, $
           noerase=noerase,xrange=xrange,yrange=yrange,xticks=xticks, $
           xtickname=xtickname,yticks=yticks,ytickname=ytickname, $
           xstyle=xstyle,ystyle=ystyle,/data,position=position, $
           background=background, axiscolor=axiscolor,_strict_extra=extra, $
           xticklen=xticklen, yticklen=yticklen
   ENDIF
    IF (rotate) THEN BEGIN               ; Horizontal bars
       base_win = !y.window              ; Window range in Y
       scal_fact = !x.s                  ; Scaling factors
       tick_scal_fact = !y.s             ; Tick scaling factors
    ENDIF ELSE BEGIN                     ; Vertical bars
       base_win = !x.window              ; Window range in X
       scal_fact = !y.s                  ; Scaling factors
       tick_scal_fact = !x.s             ; Tick scaling factors
    ENDELSE
    winrange = baserange*(base_win[1]-base_win[0])   ; Normal. window range
    barsize = barwidth*winrange/nbars                ; Normal. bar width
    winoffset = base_win[0]+(baroffset*barsize)      ; Normal. first offset
    bases = scal_fact[0]+(scal_fact[1]*baselines)    ; Baselines, in normal coordinates
    normal = scal_fact[0]+(scal_fact[1]*values)      ; Values, in normal coordinates
    barstart = Lindgen(nbars)*(barsize+barspace*(winrange/nbars)) ; Coor. at left edges
    tickv = winoffset+barstart+(0.5*barsize)         ; Tick coordinates. (centered)
    
    FOR i=0,nbars-1 do BEGIN               ; Draw the bars
       width = winoffset+[barstart[i],barstart[i], $     ; Compute bar width
         (barstart[i]+barsize),(barstart[i]+barsize)]
       IF Finite(values[i]) EQ 0 THEN BEGIN
          length = 0.0
       ENDIF ELSE BEGIN
          length = [baselines[i], baselines[i]+values[i], baselines[i]+values[i], baselines[i]]
       ENDELSE
       IF (rotate) THEN BEGIN              ; Horizontal bars
          xy = Convert_Coord(length, [0,1,1,0], /DATA, /TO_NORMAL)  ; Compute bar length
          length=Transpose(xy[0,*])
          x = length                       ; X-axis is "length" axis
          y = width                        ; Y-axis is "width" axis
       ENDIF ELSE BEGIN                    ; Vertical bars
          xy = Convert_Coord([0,1,1,0], length, /DATA, /TO_NORMAL)  ; Compute bar length
          length=Transpose(xy[1,*])
          x = width                        ; X-axis is "width" axis
          y = length                       ; Y-axis is "length" axis
       ENDELSE
       cgColorFill, x, y, COLOR=colors[i], /NORMAL                    ; Polyfill with color
       IF (outline) THEN cgPlots, x, y, /NORMAL, COLOR=OplotColors[i], THICK=barThick ; Outline using !p.color
    ENDFOR
    
    ; Locations of the tick marks.
    tickv = (tickv-tick_scal_fact[0])/tick_scal_fact[1]  
    
    ; Return the center of the bar coordinates.
    barcoords = tickv
    
    ; Make sure you have no more than 60 labels.
    IF N_Elements(labelSkip) EQ 0 THEN BEGIN
        labelSkip = Ceil(nbars / 60.0)
        ticks = nbars - 1 
    ENDIF ELSE labelSkip = Fix(labelSkip)
    IF labelSkip GT 1 THEN BEGIN
        skipLabelVector = Indgen((nbars+labelSkip)/labelSkip)*labelSkip
        ticks = (Ceil(nbars/Float(labelSkip)-1))
        tickv = tickv[skipLabelVector]
        barnames = barnames[skipLabelVector]
    ENDIF
        
    ; Label the bar axis.
    IF (rotate) THEN BEGIN ; Label the bars (Y-axis)
       cgAxis, YAxis=0, YStyle=ystyle, YTickS=ticks, YTickV=tickv, $
          YTickName=barnames, YTickLen=0.0, _Extra=extra
    ENDIF ELSE BEGIN       ; Label the bars (X-axis)
       cgAxis, XAxis=0, XStyle=xstyle, XTickS=ticks, XTickV=tickv, $
          XTickName=barnames, XTickLen=0.0, _Extra=extra
    ENDELSE

    ; Are we producing output? If so, we need to clean up here.
    IF (N_Elements(output) NE 0) && (output NE "") THEN BEGIN
    
       ; Get the output default values.
       cgWindow_GetDefs, $
           IM_Density = im_density, $                      ; Sets the density parameter on ImageMagick convert command.
           IM_Options = im_options, $                      ; Sets extra ImageMagick options on the ImageMagick convert command.
           IM_Resize = im_resize, $                        ; Sets the resize parameter on ImageMagick convert command.
           IM_Transparent = im_transparent, $              ; Sets the "alpha" keyword on ImageMagick convert command.
           IM_Width = im_width, $                          ; Sets the width of raster file output created with ImageMagick.
           PDF_Unix_Convert_Cmd = pdf_unix_convert_cmd, $  ; Command to convert PS to PDF.
           PDF_Path = pdf_path                             ; The path to the Ghostscript conversion command.
    
        ; Close the PostScript file and create whatever output is needed.
        cgPS_Close, DELETE_PS=delete_ps, $
             ALLOW_TRANSPARENT=im_transparent, $
             BMP=bmp_flag, $
             DENSITY=im_density, $
             GIF=gif_flag, $
             GS_PATH=pdf_path, $
             IM_OPTIONS=im_options, $
             JPEG=jpeg_flag, $
             PDF=pdf_flag, $
             PNG=png_flag, $
             RESIZE=im_resize, $
             TIFF=tiff_flag, $
             UNIX_CONVERT_CMD=pdf_unix_convert_cmd, $
             WIDTH=im_width

         basename = File_Basename(outfilename)
         dirname = File_Dirname(outfilename)
         IF dirname EQ "." THEN CD, CURRENT=dirname
         Print, 'Output File: ' + Filepath(ROOT_DIR=dirname, basename)
    ENDIF
    
END
