#!/usr/bin/python -tt #======================================================================= # General Documentation """Single-procedure module. See procedure docstring for description. """ #----------------------------------------------------------------------- # Additional Documentation # # RCS Revision Code: # $Id: ocontour.py,v 1.3 2004/05/12 04:07:42 jlin Exp $ # # Modification History: # - 07 Feb 2004: Orig. by Johnny Lin, Computation Institute, # University of Chicago. Passed minimally passably reasonable # tests. # - 11 May 2004: Enable overplotting a fill contour on top of a # contour map. Passed minimally passably reasonable tests. # # Notes: # - Written for Python 2.2. # - For list of dependencies see import statement placed throughout # the file. # # Copyright (c) 2004 by Johnny Lin. For licensing, distribution # conditions, contact information, and additional documentation see # the URL http://www.johnny-lin.com/py_pkgs/IaGraph/Doc/. #======================================================================= #---------------- Module General Import and Declarations --------------- #- Set package version number: import IaGraph_version __version__ = IaGraph_version.version del IaGraph_version #------------------------ Procedure Declaration ------------------------ def ocontour( data, x, y \ , c_levels=None \ , colorbar=1 \ , continents=0 \ , ctindex=13 \ , plottype='line' \ ): """Interactively overplot 2-D contour plots. Overplots a contour plot onto the active VCS canvas. Syntax of method call is based on IDL conventions. However, defaults for input parameters are set to my personal preferences, not IDL defaults. Default is a generic line contour plot with no axis titles or overall title, and no colorbar legend plotted at the bottom. Method Arguments: * data: 2-D array of data to contour. Location of rows are described by y, and location of columns by x. Typically for plots on the earth, x is longitude and y is latitude so rows in data are bands of constant latitude. Numeric, MA, MV array. If data is Numeric only, there should be no missing values in data. * x: Vector of x-axis variable values, which corresponds to iterating through the columns of data. Numeric, MA, or MV vector. There should be no missing values in x. * y: Vector of y-axis variable values, which corresponds to iterating through the rows of data. Numeric, MA, or MV vector. There should be no missing values in y. NB: If an input argument is MV, only the MA portion of the variable is used; this procedure does not read the additional attributes of the MV class. Keyword Inputs: * c_levels: Vector of contour levels. Can be list or Numeric array. Default is None. * colorbar: If set true (i.e. not equal to 0), a color bar legend of the filled contour colors is plotted. Default is true, except when plottype is 'line' the colorbar keyword's value is ignored and no color bar is plotted. * continents: If set true (i.e. not equal to 0), fine modern continent outlines are plotted. Default is false. * ctindex: Index of color table to use, keyed into the values used by procedure loadct. The command loadct() will list all available values. Default is 13 (rainbow, violet to red, 390-680 nm). * plottype: If set to 'fill', smooth filled contours only are plotted; if set to 'line', contour lines only are plotted; if set to 'both', smooth filled contours plus an overlay of contour lines are plotted. String scalar. Default is 'line'. Output: * Overplot contour plot of data on screen of active canvas. Notes: * The font for the colorbar text are set to be the overall title font. * Color of colorbar text is forced to always be black. * The colorbar font height is set to 1/2 the overall title height. * The colorbar height is set to 0.03 (normalized units). * If contour lines are plotted, negative values are long dashed and positive values are solid. * For CDAT 3.3, the case of overplotting a fill contour on top of a contour map does not work properly. This is fixed in CDAT 4.0. If you are using CDAT 3.3, be aware that the plot you get if you do such overplotting will likely be wrong. Example to overplot a simple line contour plot on top of an x-y line plot: import Numeric as N from ocontour import ocontour x = N.arange(25) * 15.0 - 180.0 y = N.arange(13) * 15.0 - 90.0 data = N.outerproduct(N.sin(y*N.pi/360.), N.cos(x*N.pi/360.)) plot(x, 90*N.sin(x/20.)) ocontour(data, x, y) Example to overplot a contour plot with manually set contour levels (I assume the initial plot or contour command was execu- ted earlier): levs = [-0.3, -0.15, 0, 0.4, 0.6, 0.78] ocontour(data, x, y, c_levels=levs) """ #- Import modules and set system variable object: import MA import time import Numeric as N import cdms, vcs import IaGraph from IaGraph.loadct import loadct sysvar = IaGraph.Sysvar() #- Set height of font for the axis labels and the title. These # are used later on to set legend font heights, etc. Font # height is in arbitary units and is converted to normalized # units by multiplying by fontht2norm: title_fontht = sysvar.__class__.p_fontht xtitle_fontht = sysvar.__class__.x_fontht ytitle_fontht = sysvar.__class__.y_fontht #- Set conversion factor to multiply VCS font height coordinates # by to obtain the value in normalized coordinates: fontht2norm = sysvar.__class__.p_fontht2norm[0] #- Fonts: title_font = sysvar.__class__.p_font xaxis_font = sysvar.__class__.x_font yaxis_font = sysvar.__class__.y_font #- Local variables based on keyword input: Set to colorbar_loc to # protect the input keyword colorbar; set colorbar_loc to 0 if # plottype is line. Set continents_loc (a non-Boolean variable) # based on continents keyword being true or false. colorbar_loc # is false if 0 and true if non-zero: if plottype == 'line': colorbar_loc = 0 else: colorbar_loc = colorbar if (continents == 0): continents_loc = 0 else: continents_loc = 1 #- Make sure data is MA and call it dataMA for use in rest of the # procedure. Calculate the max and min of the non-missing data # in data: if MA.isMA(data) == 1: dataMA = data else: dataMA = MA.masked_array(data) data_min = MA.minimum(dataMA) data_max = MA.maximum(dataMA) #- Open VCS canvas to active canvas: if sysvar.__class__.active_canvas == []: raise ValueError, "ocontour: No pre-existing canvas" v = sysvar.__class__.active_canvas #- Make unique string based on system time. This provides # a way of giving a unique name to VCS objects that is # guaranteed to be unique if the procedure is called again # no sooner than 1 sec and no later than ~100 days from # the current call: uniq_str = ('%.2f' % time.time())[-11:].replace('.','') #- Read in active canvas base graphics method as temp_gm: if sysvar.__class__.active_canvas_base_gm[0:4] == 'xygm': temp_gm = v.getxvsy( sysvar.__class__.active_canvas_base_gm ) elif sysvar.__class__.active_canvas_base_gm[0:4] == 'fill': temp_gm = v.getisofill( sysvar.__class__.active_canvas_base_gm ) elif sysvar.__class__.active_canvas_base_gm[0:4] == 'line': temp_gm = v.getisoline( sysvar.__class__.active_canvas_base_gm ) else: raise ValueError, "ocontour: Bad base graphics method" #- Create graphics method object for isoline and fill plots # and template for isofill. Set applicable graphics method # parameters from base to current isoline and fill graphics # methods: my_fill_gm = v.createisofill( 'fillgm'+uniq_str, 'default' ) my_line_gm = v.createisoline( 'linegm'+uniq_str, 'default' ) my_fill_tpl = v.createtemplate( 'filltp'+uniq_str \ , sysvar.__class__.active_canvas_base_tpl ) my_fill_gm.xticlabels1 = my_line_gm.xticlabels1 = temp_gm.xticlabels1 my_fill_gm.datawc_x1 = my_line_gm.datawc_x1 = temp_gm.datawc_x1 my_fill_gm.datawc_x2 = my_line_gm.datawc_x2 = temp_gm.datawc_x2 my_fill_gm.yticlabels1 = my_line_gm.yticlabels1 = temp_gm.yticlabels1 my_fill_gm.datawc_y1 = my_line_gm.datawc_y1 = temp_gm.datawc_y1 my_fill_gm.datawc_y2 = my_line_gm.datawc_y2 = temp_gm.datawc_y2 del temp_gm #- Detect whether there is a base isofill legend. Set the local # flag oldfill_colorbar to 1 (yes) or 0 (no) accordingly. Delete # temp_gm: oldfill_colorbar = 0 if sysvar.__class__.active_canvas_base_gm[0:4] == 'fill': if my_fill_tpl.legend.priority != 0: oldfill_colorbar = 1 #- Legend formatting: A legend is only allowed to be written out # if plottype is not 'line' and there isn't a previously drawn # color bar. If so, follow the colorbar keyword: create text- # table object that has color set to black to use in making sure # the legend (color bar) has black lettering and set my isofill # template legend text-table to that object. Set locations of # legend; legendht is the height of the legend in normalized # units; legend_fontht is the height of the legend's text in font # units: if oldfill_colorbar == 0: if colorbar_loc == 0: my_fill_tpl.legend.priority = 0 else: my_fill_tpl.legend.priority = 1 else: my_fill_tpl.legend.priority = 0 legendht = 0.03 legend_fontht = 0.5 * title_fontht ttab_legend = v.createtexttable('ttlegd'+uniq_str, 'default') tori_legend = v.createtextorientation('tolegd'+uniq_str, 'default') ttab_legend.color = 241 ttab_legend.font = title_font tori_legend.height = legend_fontht my_fill_tpl.legend.texttable = ttab_legend my_fill_tpl.legend.textorientation = tori_legend my_fill_tpl.legend.x2 = my_fill_tpl.data.x2 if my_fill_tpl.xname.priority != 0: my_fill_tpl.legend.y2 = my_fill_tpl.xname.y else: my_fill_tpl.legend.y2 = my_fill_tpl.xlabel1.y my_fill_tpl.legend.y2 = my_fill_tpl.legend.y2 \ - (3.0 * xtitle_fontht * fontht2norm) \ - (3.0 * legend_fontht * fontht2norm) if my_fill_tpl.yname.priority != 0: my_fill_tpl.legend.x1 = my_fill_tpl.yname.x else: my_fill_tpl.legend.x1 = my_fill_tpl.ylabel1.x my_fill_tpl.legend.y1 = my_fill_tpl.legend.y2 - legendht #- Turn off all template labeling for the overplot that might be # turned on from plot or contour: my_fill_tpl.logicalmask.priority = 0 my_fill_tpl.transformation.priority = 0 my_fill_tpl.file.priority = my_fill_tpl.function.priority = 0 my_fill_tpl.source.priority = my_fill_tpl.units.priority = 0 my_fill_tpl.crdate.priority = my_fill_tpl.crtime.priority = 0 my_fill_tpl.title.priority = my_fill_tpl.dataname.priority = 0 my_fill_tpl.box1.priority = my_fill_tpl.box2.priority = 0 my_fill_tpl.box3.priority = my_fill_tpl.box4.priority = 0 my_fill_tpl.line1.priority = my_fill_tpl.line2.priority = 0 my_fill_tpl.line3.priority = my_fill_tpl.line4.priority = 0 my_fill_tpl.xlabel1.priority = my_fill_tpl.ylabel1.priority = 0 my_fill_tpl.xlabel2.priority = my_fill_tpl.ylabel2.priority = 0 my_fill_tpl.xmintic1.priority = my_fill_tpl.ymintic1.priority = 0 my_fill_tpl.xmintic2.priority = my_fill_tpl.ymintic2.priority = 0 my_fill_tpl.xtic1.priority = my_fill_tpl.ytic1.priority = 0 my_fill_tpl.xtic2.priority = my_fill_tpl.ytic2.priority = 0 my_fill_tpl.xname.priority = my_fill_tpl.yname.priority = 0 my_fill_tpl.zname.priority = my_fill_tpl.tname.priority = 0 my_fill_tpl.xunits.priority = my_fill_tpl.yunits.priority = 0 my_fill_tpl.zunits.priority = my_fill_tpl.tunits.priority = 0 my_fill_tpl.xvalue.priority = my_fill_tpl.yvalue.priority = 0 my_fill_tpl.zvalue.priority = my_fill_tpl.tvalue.priority = 0 my_fill_tpl.comment1.priority = my_fill_tpl.comment2.priority = 0 my_fill_tpl.comment3.priority = my_fill_tpl.comment4.priority = 0 #- Now that the good template settings are all done, copy it and # for the isoline graphics method. If the plottype is 'both', # turn things on/off as needed in the graphics methods to make # sure nothing is overwritten: my_line_tpl = v.createtemplate('linetp'+uniq_str, 'filltp'+uniq_str) if (plottype == 'both'): my_line_tpl.legend.priority = 0 #- Calculate and set filled-contour levels based upon the max and # min of the non-missing data in data, unless they are specified # as an input keyword. Make contour lines to be at the same place # as the filled contours and turn the contour labels on. Make the # linestyle for contour lines to be long dashed if contour are # negative and solid if they are positive or 0: if c_levels != None: if type(c_levels) == type(N.array([])): con_levels = c_levels.tolist() else: con_levels = c_levels else: con_levels = \ vcs.mkscale(data_min, data_max, sysvar.__class__.p_maxnlev) my_fill_gm.levels = con_levels my_fill_gm.fillareacolors = vcs.getcolors(con_levels, split=1) my_line_gm.level = con_levels my_line_gm.label = 'y' line_pos_con = v.createline('clinep'+uniq_str, 'default') line_neg_con = v.createline('clinen'+uniq_str, 'default') line_pos_con.type = 'solid' line_neg_con.type = 'long-dash' my_line_gm.line = N.where( N.array(con_levels) >= 0.0 \ , line_pos_con, line_neg_con ).tolist() #- Create x and y axis: xAxis = cdms.createAxis(x[:]) yAxis = cdms.createAxis(y[:]) #- Load new color table if new contour plot includes filled contours, # render plot, plot overall title, x-axis title, and y-axis title: if (plottype == 'fill') or (plottype == 'both'): loadct(v, ctindex, verbose=0) v.plot( dataMA, my_fill_gm, my_fill_tpl, xaxis=xAxis, yaxis=yAxis \ , continents=continents_loc ) if plottype == 'both': v.plot( dataMA, my_line_gm, my_line_tpl \ , xaxis=xAxis, yaxis=yAxis ) elif plottype == 'line': v.plot( dataMA, my_line_gm, my_line_tpl, xaxis=xAxis, yaxis=yAxis \ , continents=continents_loc ) else: raise ValueError, "ocontour: Bad plottype" #- Update active_canvas in system variables object: sysvar.__class__.active_canvas = v del v # ===== end file =====