1. #!/usr/bin/python -tt
#======================================================================
=
# General Documentation
"""Single-procedure module.
See procedure docstring for description.
Save this documet as “contour.py” . It is slighltly modified by myself (Arulalan.T) which is
suitable to CDAT 5.2 .
"""
#-----------------------------------------------------------------------
# Additional Documentation
#
# RCS Revision Code:
# $Id: contour.py,v 1.6 2004/05/20 23:03:18 jlin Exp $
#
# Modification History:
# - 18 Dec 2003: Original by Johnny Lin, Computation Institute,
# University of Chicago. Passed minimally passably reasonable
# tests.
# - 24 Dec 2003: Make VCS object names "unique". Passed minimally
# passably reasonable tests.
# - 11 May 2004: Make so it'll work with CDAT 4.0b2. Passed mini-
# mally reasonable tests.
#
# Notes:
# - Written for Python 2.2.
# - For dependencies see the import statements in the file.
#
# Copyright (c) 2003 by Johnny Lin. For licensing, distribution
# conditions, contact information, and additional documentation see
# the URL http://www.johnny-lin.com/py_pkgs/IaGraph/.
#======================================================================
=
#---------------- Module General Import and Declarations ---------------
#- Set package version number: #arul commented the following lines
'''
import IaGraph_version
__version__ = IaGraph_version.version
del IaGraph_version
'''
2. #------------------------------- Function ------------------------------
def contour( data, x, y
, annot=None
, c_levels=None
, colorbar=1
, continents=0
, ctindex=13
, plottype='both'
, position=None
, title=None
, xrange=None, yrange=None
, xtitle=None, ytitle=None
):
"""Interactively make 2-D contour plots.
Plots a contour plot onto the active VCS canvas. If there is a
pre-existing plot on the active canvas, that plot is overwritten.
Syntax of method call is based on IDL conventions. However,
defaults for input parameters are set to my personal preferences,
not IDL defaults; a call of contour(data,x,y) should give a
generic contour plot of that type I would (subjectively) find
generally suitable for publication.
Default plot is a generic contour plot with no axis titles or
overall title, and with a colorbar legend plotted at the bottom.
The plot is rainbow filled with contour lines overlain.
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:
* annot: Extra text to annotate the graph with, placed in the
3. very upper-left corner in small-sized text (1/2 the height of
the overall title). String scalar. Different lines of text
are separated by the os.linesep string.
* 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 'both'.
* position: 4-element list of [bbllx, bblly, bburx, bbury] of
the bounding box (x,y) coordinates in normalized values. The
data origin is the lower-left corner, and the upper-right cor-
ner of the data box is the upper-right corner of position.
If keyword not defined, IaGraph.Sysvar.__class__.p_position is
used. If that system variable is not defined, this procedure
uses the following values:
position value | colorbar? | annot?
-------------------------------------------
[0.2, 0.30, 0.9, 0.80] | Y | Y
[0.2, 0.15, 0.9, 0.65] | N | Y
[0.2, 0.35, 0.9, 0.85] | Y | N
[0.2, 0.25, 0.9, 0.75] | N | N
* title: The overall plot title. String scalar. Only a single
line of input is accepted.
* [xy]range: The range of the [xy]-axis used to calculate "neat"
tickmarks, from which the actual axis range is set. Two-element
list, where the first element is the minimum in the axis range
and the second element is the maximum in the axis range. If
keyword not defined, IaGraph.Sysvar.__class__.[xy]_range is used.
If that system variable is not defined, this procedure uses the
the minimum and maximum of each input axis vector.
* [xy]title: The x-axis and y-axis title. String scalar. Only
a single line of input is accepted.
4. Output:
* Contour plot of data on screen.
* The active_canvas system variable in IaGraph.Sysvar is set to
the canvas (overwritten if a previous canvas exists) that is
drawn by this procedure. This allows other procedures in
IaGraph to operate on the canvas outside of the IaGraph.contour
procedure.
Notes:
* The font for the colorbar and the annot keyword text are set to
be the overall title font.
* Font heights for the axes labels are set to be the same as the
axes titles.
* Color of overall title, colorbar text, and axes tick labels and
titles are forced to always be black.
* The colorbar and annot font heights are 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.
Example to plot a simple contour plot with an overall title:
import Numeric as N
from contour import contour
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.))
contour(data, x, y, title='Example')
Example to make a contour plot with manually set contour levels:
levs = [-0.3, -0.15, 0, 0.4, 0.6, 0.78]
contour(data, x, y, c_levels=levs)
"""
#- Import modules and set system variable object:
#import MA
import numpy.ma as MA
import time
#import Numeric as N
import numpy as N
import cdms2 as cdms
import vcs
import IaGraph
from IaGraph.loadct import loadct
5. sysvar = IaGraph.Sysvar()
#- 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 data is MV, the isMA function will return True,
# and so we ensure dataMA has no MV attributes in this section:
if MA.isMA(data) ==0:#arul changed from 1 to 0
dataMA = MA.masked_values( data.filled(), data.fill_value() )
else:
dataMA = MA.masked_array(data)
data_min = MA.minimum(dataMA)
data_max = MA.maximum(dataMA)
#- Set range of axes, if not defined in keywords:
if xrange == None:
xrange = sysvar.__class__.x_range
if sysvar.__class__.x_range == None:
xrange = [min(x), max(x)]
if yrange == None:
yrange = sysvar.__class__.y_range
if sysvar.__class__.y_range == None:
yrange = [min(y), max(y)]
if len(xrange) != 2:
raise ValueError, "contour: Bad x-axis ranges"
if len(yrange) != 2:
raise ValueError, "contour: Bad y-axis ranges"
6. #- Open VCS canvas window (first closing and reseting any pre-
# existing active canvas):
if sysvar.__class__.active_canvas != []:
sysvar.__class__.active_canvas.clear()
v = sysvar.__class__.active_canvas
else:
v = vcs.init()
#- 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('.','')
#- Create graphics method object for isoline and fill plots and
# the template for isofill:
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, 'default')
#- Choose a color map and make it active:
loadct(v, ctindex, verbose=0)
#- Set coordinates (normalized units) for lower-left and upper-
# right bounding box:
if position == None:
position = sysvar.__class__.p_position
if sysvar.__class__.p_position == None:
if (colorbar_loc != 0) and (annot != None):
position = [0.2, 0.3, 0.9, 0.8]
elif (colorbar_loc == 0) and (annot != None):
position = [0.2, 0.15, 0.9, 0.65]
elif (colorbar_loc != 0) and (annot == None):
position = [0.2, 0.35, 0.9, 0.85]
elif (colorbar_loc == 0) and (annot == None):
position = [0.2, 0.25, 0.9, 0.75]
else:
raise ValueError, "contour: Bad position state"
bbllx = position[0]
bblly = position[1]
bburx = position[2]
bbury = position[3]
7. #- Set height of font for the axis labels and the title, and set
# the tick length. Font height is in arbitary units and is
# converted to normalized units by multiplying by fontht2norm.
# Set maximum number of ticks for axis:
title_fontht = sysvar.__class__.p_fontht
xtitle_fontht = sysvar.__class__.x_fontht
ytitle_fontht = sysvar.__class__.y_fontht
xticklabel_fontht = sysvar.__class__.x_fontht
yticklabel_fontht = sysvar.__class__.y_fontht
xticklength = sysvar.__class__.x_ticklen
yticklength = sysvar.__class__.y_ticklen
xtickmaxnum = sysvar.__class__.x_tickmaxnum
ytickmaxnum = sysvar.__class__.y_tickmaxnum
#- 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
#- Create text-table and text-orientation objects for x- and
# y-axis tick labels and using them set font and font height.
# Also set y-axis tick labels to be vertically aligned at the
# mid-way point:
ttab_xticklabel = v.createtexttable('ttxtic'+uniq_str, 'default')
ttab_yticklabel = v.createtexttable('ttytic'+uniq_str, 'default')
tori_xticklabel = v.createtextorientation('toxtic'+uniq_str, 'defcenter')
tori_yticklabel = v.createtextorientation('toytic'+uniq_str, 'default')
ttab_xticklabel.font = xaxis_font
ttab_yticklabel.font = yaxis_font
ttab_xticklabel.color = 241
ttab_yticklabel.color = 241
tori_xticklabel.height = xticklabel_fontht
tori_yticklabel.height = yticklabel_fontht
tori_yticklabel.valign = 'half'
#- Create/set text-table and text-orientation objects for overall
# title and axis title settings. Turn on these fields in the
8. # template and set the new text-table/orientation objects to
# those fields in the template:
ttab_title = v.createtexttable('tttitl'+uniq_str, 'default')
tori_title = v.createtextorientation('totitl'+uniq_str, 'defcenter')
ttab_title.font = title_font
ttab_title.color = 241
tori_title.height = title_fontht
ttab_xtitle = v.createtexttable('ttxtit'+uniq_str, 'default')
ttab_ytitle = v.createtexttable('ttytit'+uniq_str, 'default')
ttab_xtitle.font = xaxis_font
ttab_xtitle.color = 241
ttab_ytitle.font = yaxis_font
ttab_ytitle.color = 241
tori_xtitle = v.createtextorientation('toxtit'+uniq_str, 'defcenter')
tori_ytitle = v.createtextorientation('toytit'+uniq_str, 'defcentup')
tori_xtitle.height = xtitle_fontht
tori_ytitle.height = ytitle_fontht
my_fill_tpl.dataname.texttable = ttab_title
my_fill_tpl.dataname.textorientation = tori_title
my_fill_tpl.xname.texttable = ttab_xtitle
my_fill_tpl.xname.textorientation = tori_xtitle
my_fill_tpl.yname.texttable = ttab_ytitle
my_fill_tpl.yname.textorientation = tori_ytitle
my_fill_tpl.dataname.priority = 0 #+ Default is for overall title
my_fill_tpl.xname.priority = 0 # and axis titles to be off
my_fill_tpl.yname.priority = 0
if title != None: my_fill_tpl.dataname.priority = 1
if xtitle != None: my_fill_tpl.xname.priority = 1
if ytitle != None: my_fill_tpl.yname.priority = 1
#- Turn off some default titling fields:
my_fill_tpl.mean.priority = 0
my_fill_tpl.max.priority = 0
my_fill_tpl.min.priority = 0
#- Set position of data field, plot box, and x-axis bottom ticks
# and labels; set text-table and text-orientation of x-axis bottom
# labels; turn off upper-right set of tick labels:
my_fill_tpl.data.x1 = my_fill_tpl.box1.x1 = bbllx
my_fill_tpl.data.y1 = my_fill_tpl.box1.y1 = bblly
my_fill_tpl.data.x2 = my_fill_tpl.box1.x2 = bburx
my_fill_tpl.data.y2 = my_fill_tpl.box1.y2 = bbury
9. my_fill_tpl.xtic1.y1 = bblly - xticklength
my_fill_tpl.xtic1.y2 = bblly
my_fill_tpl.xlabel1.y = bblly - (3.0 * xticklength)
my_fill_tpl.xlabel1.texttable = ttab_xticklabel
my_fill_tpl.xlabel1.textorientation = tori_xticklabel
my_fill_tpl.xtic2.priority = 0
my_fill_tpl.ytic2.priority = 0
#- Create "nice" x-axis labels (if the system variable for the
# tick values is not defined) and set them into the graphics
# methods. If the xlabels that are calculated are "too long"
# (i.e. longest label is >= 5 characters), recalculate the
# labels to have no more than 5 labeled ticks on the axis.
# Set bounds of data frame to exactly span xlabels:
if sysvar.__class__.x_tickvalues == None:
xlabels =
vcs.mklabels( vcs.mkscale(xrange[0], xrange[1], xtickmaxnum) )
longest_xlabels =
max([ len(xlabels.values()[i]) for i in range(len(xlabels))] )
if longest_xlabels >= 5:
xlabels = vcs.mklabels( vcs.mkscale(xrange[0], xrange[1], 5) )
else:
xlabels = sysvar.__class__.x_tickvalues
my_fill_gm.xticlabels1 = my_line_gm.xticlabels1 = xlabels
my_fill_gm.datawc_x1 = my_line_gm.datawc_x1 = min(xlabels.keys())
my_fill_gm.datawc_x2 = my_line_gm.datawc_x2 = max(xlabels.keys())
#- Create "nice" y-axis labels (if the system variable for the
# tick values is not defined) and set them in the graphics
# methods. Set bounds of data frame to exactly span ylabels.
# Calculate y-axis title location based on the longest label
# length, set y-axis ticks and tick label locations, and set
# text-table and text-orientation for y-axis tick labels:
if sysvar.__class__.y_tickvalues == None:
ylabels =
vcs.mklabels( vcs.mkscale(yrange[0], yrange[1], ytickmaxnum) )
else:
ylabels = sysvar.__class__.y_tickvalues
my_fill_gm.yticlabels1 = my_line_gm.yticlabels1 = ylabels
my_fill_gm.datawc_y1 = my_line_gm.datawc_y1 = min(ylabels.keys())
my_fill_gm.datawc_y2 = my_line_gm.datawc_y2 = max(ylabels.keys())
longest_ylabels = max([ len(ylabels.values()[i])
for i in range(len(ylabels)) ])
10. my_fill_tpl.ytic1.x1 = bbllx - yticklength
my_fill_tpl.ytic1.x2 = bbllx
my_fill_tpl.ylabel1.x = bbllx - yticklength
- ( longest_ylabels
* yticklabel_fontht * fontht2norm )
my_fill_tpl.ylabel1.texttable = ttab_yticklabel
my_fill_tpl.ylabel1.textorientation = tori_yticklabel
#- Position overall title, x-axis title, and y-axis title. If
# there is no [xy]-axis title, the [yx]-location of the [xy]-axis
# "title" is just the location of the labels; this allows us to
# plot the color bar correctly:
my_fill_tpl.dataname.x = (bburx-bbllx)/2.0 + bbllx
my_fill_tpl.dataname.y = bbury + ( 1.7 * title_fontht
* fontht2norm )
my_fill_tpl.xname.x = (bburx-bbllx)/2.0 + bbllx
if xtitle == None:
my_fill_tpl.xname.y = my_fill_tpl.xlabel1.y
else:
my_fill_tpl.xname.y = my_fill_tpl.xlabel1.y
- (2.0 * xtitle_fontht * fontht2norm)
if ytitle == None:
my_fill_tpl.yname.x = my_fill_tpl.ylabel1.x
else:
my_fill_tpl.yname.x = my_fill_tpl.ylabel1.x
- ( 1.6 * ytitle_fontht * fontht2norm )
my_fill_tpl.yname.y = (bbury-bblly)/2.0 + bblly
#- Legend formatting: 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 colorbar_loc == 0:
my_fill_tpl.legend.priority = 0
else:
my_fill_tpl.legend.priority = 1
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
11. 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 = bburx
my_fill_tpl.legend.y2 = my_fill_tpl.xname.y
- (3.0 * xtitle_fontht * fontht2norm)
- (3.0 * legend_fontht * fontht2norm)
my_fill_tpl.legend.x1 = my_fill_tpl.yname.x
my_fill_tpl.legend.y1 = my_fill_tpl.legend.y2 - legendht
#- 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.box1.priority = 0
my_line_tpl.dataname.priority = 0
my_line_tpl.legend.priority = 0
my_line_tpl.xname.priority = my_line_tpl.yname.priority = 0
my_line_tpl.xlabel1.priority = my_line_tpl.xlabel2.priority = 0
my_line_tpl.ylabel1.priority = my_line_tpl.ylabel2.priority = 0
my_line_tpl.xtic1.priority = my_line_tpl.xtic2.priority = 0
my_line_tpl.ytic1.priority = my_line_tpl.ytic2.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'