; docformat = 'rst' ; ; NAME: ; cgDCBar ; ; PURPOSE: ; The purpose of this routine is to add a discrete color bar to ; a graphics plot. A "discrete" color bar is one with a handful ; of colors. Labels are centered beneath or beside the color fields. ; ;******************************************************************************************; ; ; ; 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. ; ;******************************************************************************************; ; ;+ ; The purpose of this routine is to add a discrete color bar to ; a graphics plot. A "discrete" color bar is one with a handful ; of colors. Labels are centered beneath or beside the color fields. ; ; .. image:: cgcolorbar3.png ; ; .. image:: cgdcbar.png ; ; The program requires the `Coyote Library ` ; to be installed on your machine. ; ; :Categories: ; Graphics ; ; :Params: ; colors, in, required ; A vector of "colors" to be represented in the color bar. The vector can be a ; vector of color "names" that are known to cgColor. Or, it can be a vector of ; 24-bit color values that can be decomposed into color triples. Or, it can be a ; vector of byte or integer values that can be used as indices into the current ; color table. If both colors and NCOLORS (see below) are undefined, a 10-element ; color table will be loaded and used. ; ; :Keywords: ; addcmd: in, optional, type=boolean, default=0 ; Set this keyword to add the command to the resizeable graphics window cgWindow. ; barcolor: in, optional, type=string ; This is the name of a color known to cgCOLOR that can be used to draw the color ; bar outlines. By default, the same as specified with the COLOR keyword. ; bottom: in, optional, type=integer, default=0 ; The lowest color index of the colors to be loaded in the color bar. ; charpercent: in, optional, type=float, default=0.85 ; A value from 0.0 go 1.0 that is multiplied by the CHARSIZE to produce ; the character size for the color bar. This value is only used if CHARSIZE is ; undefined. This keyword is primarily useful for using color bars in resizeable ; graphics windows (cgWindow). ; charsize: in, optional, type=float ; The character size of the color bar annotations. Default is cgDefCharsize()*charPercent. ; color: in, optional, type=string, default="opposite" ; The name of the color to use for color bar annotations. ; filename: in, optional, type=string ; The name of a color table file that can be read by cgCOLOR. This allows you to ; specify your own color names for your own colors. ; fit: in, optional, type=boolean, default=0 ; If this keyword is set, the colorbar tries to "fit" itself to the normalized ; coordinates of the last graphics command executed. In other words, for ; a horizontal color bar, postition[[0,2]] = !X.Window, and for a vertical ; color bar, position[[1,3]] = !Y.Window. Other positions are adjusted ; to put the colorbar "reasonably" close to the plot. Because there are so many ; ways this colorbar can be displayed, the "fit" may not always be a good one. ; If you are fitting to an image, be sure to set the SAVE keyword on cgImage ; to establish a data coordinate system. ; font: in, optional, type=integer, default=!P.Font ; Sets the font of the annotation. Hershey: -1, Hardware:0, True-Type: 1. ; labels: in, optional, type=string ; An array of string labels that should annotate each color. Must be the same length ; as the colors vector. Colors are labelled consecutively by default. ; ncolors: in, optional, type=integer, default=256 ; An alternative way to specify the colors in the color bar is by ; using the NCOLORS and BOTTOM keywords to locate the colors in the ; current color table. The NCOLORS and BOTTOM keywords have the same ; meaning as in the LOADCT, XLOADCT, XCOLORS, or cgCOLORBAR programs. ; position: in, optional, type=float ; A four-element array of normalized coordinates in the same ; form as the POSITION keyword on a plot. Default is [0.88, 0.10, 0.95, 0.90] ; for a vertical bar and [0.10, 0.88, 0.90, 0.95] for a horizontal bar. ; See the FIT keyword, also. ; right: in, optional, type=boolean, default=0 ; This puts the title on the right-hand side of a vertical color bar. It applies ; only to vertical color bars. ; rotate: in, optional, type=float, default=0.0 ; Set this keyword to a value that will rotate the label text. ; Positive values between 0 and 180 degrees rotate in a counter-clockwise ; sense. Negative values between 0 and 180 degress rotate in a ; clockwise sense. ; spacing: in, optional, type=float, default=1.0 ; When labels are rotated, it is a little difficult to determine where, ; exactly, they should be located. This keyword gives the user some control ; over this location. The location "spacer" is multiplied by this amount. ; So, for example, to move the labels a little further away from the color bar, ; make this number greater than 1 (e.g, 1.25). To move the labels a little closer, ; use a number less than 1 (e.g, 0.75). ; tcharsize: in, optional, type=float ; The character size of the title. By default, set to cgDefCharsize(). ; treverse: in, optional, type=boolean, default=0 ; Set this keyword to reverse the direction of the title on a vertical color bar. ; title: in, optional, type=string, default="" ; This is title for the color bar. The default is to have no title. ; vertical: in, optional, type=boolean, default=0 ; Setting this keyword give a vertical color bar. The default is a horizontal color bar. ; window: in, optional, type=boolean, default=0 ; Set this keyword to display the plot in a resizeable graphics window (cgWindow). ; ; :Examples: ; To display a 12 color horizontal color bar, labels with a three-letter ; month abbreviation:: ; ; cgDisplay ; cgLoadCT, 5, NCOLORS=12, BOTTOM=1 ; cgDCBar, NCOLORS=12, BOTTOM=1, LABELS=cgMonths(/Abbreviation) ; ; To load a 5 color vertical color bar, with the labels rotated 45 degrees:: ; ; cgDisplay ; labels = StrArr(5) ; FOR j=0,4 DO labels[j] = 'City ' + StrTrim(j+1,2) ; colors = ['dodger blue', 'yellow', 'forest green', 'purple', 'tan'] ; cgDCBar, colors, LABELS=labels, ROTATE=45, /VERTICAL ; ; :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 by: David W. Fanning, 15 March 2009. ; Modification to code to avoid changing the colors vectors. 15 March 2009. ; Added FONT keyword. 1 April 2009. DWF. ; Code modified to support 24-bit PostScript printers. 23 September 2009. DWF. ; Fixed a problem with determining visual depth in Z-buffer. 15 January 2010. DWF. ; Added SPACING keyword and changed the default spacing on horizontal color bars slightly. 23 Apr 2010. DWF. ; Modified the spacing of the labels on the color bar, specifically for the PostScript device. 3 November 2010. DWF. ; Added Window and AddCmd keywords. 28 Jan 2011. DWF. ; Added a Right keyword and changed the title spacing a little bit for aesthetic reasons. 2 July 2011. DWF. ; Fixed a problem with assigning the color with the COLOR keyword in the Z-buffer. 30 Aug 2011. DWF. ; The default BOTTOM keyword value was incorrect. Fixed in this version. 5 December 2011. DWF. ; Modified to use cgDefaultColor for default color selection. 24 Dec 2011. DWF. ; Previous change incorrectly implemented for PS device. Fixed. 29 Dec 2011. DWF. ; Added CHARPERCENT, FIT, and TREVERSE keywords. Cleaned up documentation. 20 March 2012. DWF. ; Added the ability to use escape characters in plot titles to specify cgSymbol symbols. 27 July 2012. DWF. ; Fixed a problem in which the colors variable was set to long integers if the BOTTOM keyword was used with ; NCOLORS. 2 Oct 2013. DWF. ; ; :Copyright: ; Copyright (c) 2009-2012, Fanning Software Consulting, Inc. ;- PRO cgDCBar, colors, $ ADDCMD=addcmd, $ BARCOLOR=barcolor, $ BOTTOM=bottom, $ CHARPERCENT=charpercent, $ CHARSIZE=charsize, $ COLOR=color, $ FILENAME=file, $ FIT=fit, $ FONT=font, $ LABELS=labels, $ NCOLORS=ncolors, $ POSITION=position, $ RIGHT=right, $ ROTATE=rotate, $ SPACING=spacing, $ TCHARSIZE=tcharsize, $ TREVERSE=treverse, $ TITLE=title, $ VERTICAL=vertical, $ WINDOW=window ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Should this be added to a resizeable graphics window? IF (Keyword_Set(window) OR Keyword_Set(addcmd)) AND ((!D.Flags AND 256) NE 0) THEN BEGIN IF Keyword_Set(addcmd) THEN window = 0 void = cgQuery(COUNT=wincnt) IF wincnt EQ 0 THEN cgWindow cgWindow, 'cgDCBar', colors, $ BARCOLOR=barcolor, $ BOTTOM=bottom, $ CHARPERCENT=charpercent, $ CHARSIZE=charsize, $ COLOR=color, $ FILENAME=file, $ FIT=fit, $ FONT=font, $ LABELS=labels, $ NCOLORS=ncolors, $ POSITION=position, $ RIGHT=right, $ ROTATE=rotate, $ SPACING=spacing, $ TCHARSIZE=tcharsize, $ TREVERSE=treverse, $ TITLE=title, $ VERTICAL=vertical, $ REPLACECMD=Keyword_Set(window), $ ADDCMD=Keyword_Set(addcmd) RETURN ENDIF ; Check parameters and keywords. IF (N_Elements(charPercent) EQ 0) $ THEN charPercent = 0.85 $ ELSE charPercent = 0.0 > charPercent < 1.0 IF N_Elements(bottom) EQ 0 THEN bottom = 0 ELSE bottom = Fix(bottom) SetDefaultValue, charsize, cgDefCharsize() * charPercent IF N_Elements(colors) EQ 0 THEN BEGIN IF N_Elements(ncolors) EQ 0 THEN BEGIN cgLoadCT, 25, /Brewer, NCOLORS=10, BOTTOM=245 ncolors = 10S bottom = 245S ENDIF IF N_Elements(bottom) EQ 0 THEN bottom = 255S - Fix(ncolors) ncolors = (bottom + ncolors) - bottom TVLCT, r, g, b, /Get colors = Indgen(ncolors) + Fix(bottom) ENDIF ELSE ncolors = N_Elements(colors) cbar_colors = colors IF N_Elements(font) EQ 0 THEN font = !P.Font IF N_Elements(labels) EQ 0 THEN labels = StrTrim(SIndgen(ncolors)+1,2) IF N_Elements(labels) NE N_Elements(colors) THEN $ Message, 'LABELS vector must be the same length as the COLORS vector.' IF N_Elements(rotate) EQ 0 THEN rotate = 0 rotate = (-180) > rotate < 180 ; Restrict to -180 to 180 degrees. IF N_Elements(spacing) EQ 0 THEN spacing = 1.0 IF N_Elements(title) EQ 0 THEN title = "" ELSE title = cgCheckForSymbols(title) IF N_Elements(tcharsize) EQ 0 THEN tcharsize = cgDefCharsize() treverse = Keyword_Set(treverse) vertical = Keyword_Set(vertical) IF N_Elements(position) EQ 0 THEN BEGIN IF vertical THEN BEGIN position = [0.85, 0.1, 0.90, 0.9] ENDIF ELSE BEGIN position = [0.1, 0.88, 0.9, 0.93] ENDELSE ENDIF ; 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 ; Save the orginal color table so it can be restored later. TVLCT, rr, gg, bb, /Get ; I would prefer to draw in 24-bit color if I can, since this way I can ; avoid loading colors into the color table. I'll have to see where I am to ; see if I can do this in 24-bit color. cgSetColorState, 1, CURRENT=currentState cbar_colors = cgDefaultColor(cbar_colors) color = cgDefaultColor(color, DEFAULT='opposite') barcolor = cgDefaultColor(color, DEFAULT=color) IF Size(color, /TNAME) EQ 'STRING' THEN color = cgColor(color, FILE=file) IF Size(barcolor, /TNAME) EQ 'STRING' THEN barcolor = cgColor(barcolor, FILE=file) IF Size(cbar_colors, /TNAME) EQ 'STRING' THEN cbar_colors = cgColor(cbar_colors, FILE=file) ; Draw horizontal color bar. IF ~vertical THEN BEGIN ; Are we trying to fit the color bar? IF Keyword_Set(fit) THEN BEGIN position[[0,2]] = !X.Window distance = (position[3] - position[1]) position[1] = !Y.Window[1] + ((4*!D.Y_CH_SIZE*charsize) / !D.Y_Size) position[3] = position[1] + distance IF position[3] GT 1.0 THEN BEGIN diff = position[3]-1.0 position[3] = 1.0 - 0.015 IF (position[3] - position[1]) LT 0.015 THEN position[1] = (position[3] < position[1])-0.015 ENDIF ENDIF ; Calculate positions for the rectangles of the bar. x0 = position[0] step = (position[2]-position[0])/ncolors x1 = x0 + step y0 = position[1] y1 = position[3] ; Draw each rectangle. FOR j=0,ncolors-1 DO BEGIN x = [x0, x1, x1, x0, x0] y = [y0, y0, y1, y1, y0] Polyfill, x, y, /NORMAL, Color=cbar_colors[j] PlotS, x, y, /NORMAL, COLOR=barcolor x0 = x1 x1 = x0 + step ENDFOR ; Add the annotations of the bar. chardist = !D.Y_CH_SIZE / Float(!D.Y_Size) * $ ((StrUpCase(!Version.OS_Family) EQ 'WINDOWS') ? (0.9 * spacing) : (1.5 * spacing)) IF !D.Name EQ 'PS' THEN chardist = !D.Y_CH_SIZE / Float(!D.Y_Size) * (0.75 * spacing) step = (position[2]-position[0])/ncolors x = position[0] + step/2. y = y0 - (chardist * ((rotate NE 0) ? 1 : 2)) CASE 1 OF (rotate EQ 0): alignment = 0.5 (rotate GT 0) AND (rotate LE 180): alignment = 1.0 (rotate LT 0) AND (rotate GT -180):alignment = 0.0 ELSE: alignment = 0.5 ENDCASE FOR j=0,N_Elements(labels)-1 DO BEGIN XYOutS, x, y, /NORMAL, StrTrim(labels[j],2), COLOR=color, $ ORIENTATION=rotate, ALIGNMENT=alignment, CHARSIZE=charsize, FONT=font x = x + step ENDFOR XYOutS, (position[2]-position[0])/2.0 + position[0], y1+chardist, title, $ COLOR=color, /NORMAL, ALIGNMENT=0.5, CHARSIZE=tcharsize, FONT=font ENDIF ELSE BEGIN ; Draw the vertical color bar. ; Are we trying to fit the color bar? IF Keyword_Set(fit) THEN BEGIN position[[1,3]] = !Y.Window distance = position[2] - position[0] IF Keyword_Set(right) THEN BEGIN position[0] = !X.Window[1] + ((2*!D.X_CH_SIZE*charsize) / !D.X_Size) ENDIF ELSE BEGIN position[0] = !X.Window[1] + ((4*!D.X_CH_SIZE*charsize) / !D.X_Size) ENDELSE position[2] = position[0] + distance IF position[2] GT 1.0 THEN BEGIN diff = position[2]-1.0 position[2] = 1.0 - 0.015 IF (position[2] - position[0]) LT 0.015 THEN position[0] = (position[2] < position[0])-0.015 ENDIF ENDIF ; Draw each rectangle. x0 = position[0] x1 = position[2] step = (position[3]-position[1])/ncolors y0 = position[1] y1 = y0 + step FOR j=0,ncolors-1 DO BEGIN x = [x0, x1, x1, x0, x0] y = [y0, y0, y1, y1, y0] Polyfill, x, y, /NORMAL, Color=cbar_colors[j] PlotS, x, y, /NORMAL, COLOR=barcolor y0 = y1 y1 = y0 + step ENDFOR ; Add the annotations of the bar. chardist = !D.Y_CH_SIZE / Float(!D.Y_Size) * $ ((StrUpCase(!Version.OS_Family) EQ 'WINDOWS') ? (0.75 * spacing) : (1.25 * spacing)) step = (position[3]-position[1])/ncolors y = position[1] + step/2. - (!D.Y_CH_Size / Float(!D.Y_Size) * 0.5) CASE 1 OF (rotate EQ 0): x = x1 + chardist*1 (rotate GT 0): x = x1 + chardist*1.5 (rotate LT 0): x = x1 + chardist*1 ELSE: x = x1 + chardist ENDCASE CASE 1 OF (rotate EQ 90): BEGIN alignment = 0.5 x = x1 + chardist*2.5 END (rotate EQ -90): alignment = 0.5 ELSE: alignment = 0.0 ENDCASE FOR j=0,N_Elements(labels)-1 DO BEGIN XYOutS, x, y, /NORMAL, StrTrim(labels[j],2), COLOR=color, $ ORIENTATION=rotate, ALIGNMENT=alignment, CHARSIZE=charsize, FONT=font y = y + step ENDFOR IF Keyword_Set(right) THEN BEGIN IF treverse THEN BEGIN spacing = (rotate NE 0) ? !D.X_CH_SIZE*4.5/Float(!D.X_Size) : !D.X_CH_SIZE*4.0/Float(!D.X_Size) ENDIF ELSE BEGIN spacing = (rotate NE 0) ? !D.X_CH_SIZE*3.5/Float(!D.X_Size) : !D.X_CH_SIZE*3.0/Float(!D.X_Size) ENDELSE rotateFactor = Cos(Abs(rotate)*!DtoR) > 0.75 xstart = x1 + (spacing + ( Max(StrLen(labels)) * (rotateFactor * !D.X_CH_SIZE ))/Float(!D.X_Size)) IF treverse THEN BEGIN XYOutS, xstart, (position[3]-position[1])/2.0 + position[1], $ title, COLOR=color, /NORMAL, ALIGNMENT=0.5, $ ORIENTATION=(x GE 0.5) ? 90 : -90, CHARSIZE=tcharsize, FONT=font ENDIF ELSE BEGIN XYOutS, xstart, (position[3]-position[1])/2.0 + position[1], $ title, COLOR=color, /NORMAL, ALIGNMENT=0.5, $ ORIENTATION=(x GE 0.5) ? -90 : 90, CHARSIZE=tcharsize, FONT=font ENDELSE ENDIF ELSE BEGIN IF treverse THEN BEGIN IF treverse THEN BEGIN xstart = x0 - (chardist * ((x0 LT 0.5) ? 1.5 : 1.0)) ENDIF ELSE BEGIN xstart = x0 - (chardist * ((x0 LT 0.5) ? 2 : 1.5)) ENDELSE XYOutS, xstart, (position[3]-position[1])/2.0 + position[1], $ title, COLOR=color, /NORMAL, ALIGNMENT=0.5, $ ORIENTATION=(x GE 0.5) ? 90 : -90, CHARSIZE=tcharsize, FONT=font ENDIF ELSE BEGIN xstart = x0 - (chardist * ((x0 LT 0.5) ? 1.5 : 2)) XYOutS, xstart, (position[3]-position[1])/2.0 + position[1], $ title, COLOR=color, /NORMAL, ALIGNMENT=0.5, $ ORIENTATION=(x GE 0.5) ? -90 : 90, CHARSIZE=tcharsize, FONT=font ENDELSE ENDELSE ENDELSE ; Restore the orginal color table. TVLCT, rr, gg, bb ; Clean up. cgSetColorState, currentState END