Given two Numeric
vectors, make an x-y line plot
with customized labels, line types, etc.
First we import the packages we'll need:
import Numeric as N
import vcs
We import Numeric
with the
shorthand N
to save us some typing.
Our data is a sine curve:
x = N.arange(17)*N.pi/8.
y = N.sin(x)
We first open a VCS canvas and create our own graphics method and template, since we'll be revising these two objects to customize our plot:
v = vcs.init()
my_gm = v.createxvsy('my_graph_meth', 'default')
my_tpl = v.createtemplate('my_template', 'default')
We set the plot symbol to a square and the line style to dashed. Both these changes are made in the graphics method:
my_gm.marker = 11
my_gm.line = 1
In this example we will customize the size and position of the plot on the page. To make this easier, we make variables of the coordinates of a bounding box, and later on we will use those coordinates in calculating positioning of labels and titles. The bounding box will describe the corners of the plot data field (i.e. the origin and upper-right corner of the data field). Font heights are given in arbitrary units and are set for the overall title, the x-axis and y-axis titles, and the tick labels (for both axis). The tick length and the bounding box coordinates are in normalized coordinates. Remember, here we're just making temporary variables; the actual setting of plot elements occurs later on:
bbllx = 0.25
bblly = 0.25
bburx = 0.75
bbury = 0.75
title_fontht = 40
xytitle_fontht = 30
ticklabel_fontht = 30
ticklength = 0.01
Here we make variables that specify which font to use for the tick labels and all of the titles (overall and the axes). Later on we'll set the plot attributes controlling those fields to these values. Font code 2 is a courier-looking font:
ticklabel_font = 2
titles_font = 2
Because font heights are in arbitrary units, to make our calculations easier we define a conversion factor that we multiply font height coordinates by in order to get the equivalent value in normalized coordinates. This "magic number" was obtained through trial-and-error:
fontht2norm = 0.0007
Now the complicated part begins. VCS knows where to put everything
based upon the template that's provided to it, but our current template
is bare-bones. What we have to do now is create secondary objects
that will define the attributes of our template to be what we want
it to be. First, let's create text-table and text-orientation
objects for the x- and y-axis tick labels. We use the standard default
objects for all except the text-orientation for the x-axis tick label,
which we want to be centered at the ticks, so we use the
'defcenter'
object:
ttab_xticklabel = \ v.createtexttable('xticklabel_ttab', 'default') ttab_yticklabel = \ v.createtexttable('yticklabel_ttab', 'default') tori_xticklabel = \ v.createtextorientation('xticklabel_tori', 'defcenter') tori_yticklabel = \ v.createtextorientation('yticklabel_tori', 'default')
Then we set the font attribute for the tick labels (to the
previously defined variable ticklabel_font
), and
also set the y-axis tick labels to be vertically aligned at the
"mid-way" point of the string:
ttab_xticklabel.font = ticklabel_font
ttab_yticklabel.font = ticklabel_font
tori_xticklabel.height = ticklabel_fontht
tori_yticklabel.height = ticklabel_fontht
tori_yticklabel.valign = 'half'
The default template settings for overall and x- and y-axis
titles are a bit plain. We could create gussied-up text-table and
text-orientation objects to replace those fields in the template, and
then pass in the string values via the plot
method's
xname
etc. keywords; this has the benefit of keeping
everything in the template and accessed via one plot
command. Alternately, we can create title
text objects from scratch
and place them separately; this has the benefit of not requiring
anything to be passed via keywords in the plot
command.
Here we'll do it the latter way to demonstrate how that method works.
If you want to see the method of changing the method
via the template titling members, see the
complex contour plot example.
For both the overall title and x-axis title we want the text-orientation
to be horizontally centered. For the y-axis title we want the
text-orientation to be vertical and vertically centered.
We also set the font and font height.
The string
attribute of the text objects holds
the value of the title strings:
text_title = v.createtext( 'title_ttab', 'default' \ , 'title_tori', 'defcenter' ) text_title.font = titles_font text_title.height = title_fontht text_title.string = 'A Nice Sine Curve' text_xtitle = v.createtext( 'xtitle_ttab', 'default' \ , 'xtitle_tori', 'defcenter' ) text_xtitle.font = titles_font text_xtitle.height = xytitle_fontht text_xtitle.string = 'x' text_ytitle = v.createtext( 'ytitle_ttab', 'default' \ , 'ytitle_tori', 'defcentup' ) text_ytitle.font = titles_font text_ytitle.height = xytitle_fontht text_ytitle.string = 'sin(x)'
The standard template has some titling fields that I want removed, so I turn them off here:
my_tpl.xname.priority = 0
my_tpl.yname.priority = 0
my_tpl.mean.priority = 0
my_tpl.max.priority = 0
my_tpl.min.priority = 0
Here we define the coordinates of the data field, the box around
the data field,
the x-axis ticks (whose lengths are set to ticklength
),
and the location of the x-axis tick labels.
We also replace the default text-table and text-orientation objects
for the x-axis tick labels with the secondary objects we've created
for our plot. Finally we turn off the upper x-axis and right y-axis
ticks, so that the only ticks there are are on the lower x-axis and
left y-axis:
my_tpl.data.x1 = my_tpl.box1.x1 = bbllx my_tpl.data.y1 = my_tpl.box1.y1 = bblly my_tpl.data.x2 = my_tpl.box1.x2 = bburx my_tpl.data.y2 = my_tpl.box1.y2 = bbury my_tpl.xtic1.y1 = bblly - ticklength my_tpl.xtic1.y2 = bblly my_tpl.xlabel1.y = bblly - (3.0 * ticklength) my_tpl.xlabel1.texttable = ttab_xticklabel my_tpl.xlabel1.textorientation = tori_xticklabel my_tpl.xtic2.priority = 0 my_tpl.ytic2.priority = 0
VCS has a number of neat routines (described in the
Quick Start
sheet) that calculate "nice" levels, given a range of data. Here
we use mkscale
to
calculate the labels for the x-axis, making sure there are no
more than 8 labeled ticks on the axis, and then format the
values using mklabels
so they are "nicely" written:
my_gm.xticlabels1 = \ vcs.mklabels( vcs.mkscale(min(x), max(x), 8) )
Then we do the same to get "nice" y-axis tick labels. We also calculate the length of the longest y-axis tick label, in order to figure out how far to set back the y-axis title. Based on all this information we can set the y-axis ticks, location of their labels, and replace the text-table and text-orientation objects for the y-axis tick labels with our custom objects:
ylabels = vcs.mklabels( vcs.mkscale(min(y), max(y)) ) my_gm.yticlabels1 = ylabels longest_ylabels = \ max([ len(ylabels.values()[i]) \ for i in range(len(ylabels))] ) my_tpl.ytic1.x1 = bbllx - ticklength my_tpl.ytic1.x2 = bbllx my_tpl.ylabel1.x = bbllx - ticklength \ - ( longest_ylabels * ticklabel_fontht \ * fontht2norm ) my_tpl.ylabel1.texttable = ttab_yticklabel my_tpl.ylabel1.textorientation = tori_yticklabel
We're almost done! Now we set the coordinates for the overall title, x-axis title, and y-axis title. These coordinates in the text-combined object have to be specified as either lists or tuples:
text_title.x = [ (bburx-bbllx)/2.0 + bbllx ] text_title.y = [ bbury + (1.7 * title_fontht * fontht2norm) ] text_xtitle.x = [ (bburx-bbllx)/2.0 + bbllx ] text_xtitle.y = [ my_tpl.xlabel1.y - (1.7 * title_fontht \ * fontht2norm) ] text_ytitle.x = [ my_tpl.ylabel1.x - ( 1.6 * title_fontht \ * fontht2norm ) ] text_ytitle.y = [ (bbury-bblly)/2.0 + bblly ]
Finally we render the plot. Note that we set the name
keyword to whitespace, to make sure that field isn't displayed.
After rendering the plot we render all the titles:
v.plot(x, y, my_gm, my_tpl, name=' ')
v.plot(text_title)
v.plot(text_xtitle)
v.plot(text_ytitle)
We can write Postscript and GIF output of the plot with the following commands:
v.postscript('sine_plot.ps')
v.gif('sine_plot.gif','r')
And the plot we get is:
Notes: Thanks to Dean Williams for the help! This discussion applies to CDAT 3.3.