A Complex X-Y Line Plot

Description

Given two Numeric vectors, make an x-y line plot with customized labels, line types, etc.

Sample Data

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)

Plotting

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:

Full Source Code For Example


Notes: Thanks to Dean Williams for the help! This discussion applies to CDAT 3.3.

Return to the Tips and Examples index page.

Updated: December 8, 2003 by Johnny Lin <email address>. License.