IDL plot objects

This document gives some notes about using IDL plot objects. Particular focus is given on how to reproduce features from IDL direct graphics.

If you want to test your plot object expertise, then try my exercises.

I also have a list of IDL plot object quirks that you should be careful of.

What is a plot object made of?

When you create a plot object with, for example,

IDL> p=plot(findgen(5))

it's worth noting that 'p' is really just the straight line corresponding to the data. In putting this on the screen, however, IDL has had to first create a window that contains the straight line plot, and create four axes. Each of these is a plot object in its own right.

The objects can be accessed by doing:

IDL> window=p.window
IDL> axes=p.axes

So if you'd like to change the background color from white to pink, and change the plot dimensions to 700x400, you can do:

IDL> window.background_color='pink'
IDL> window.dimension=[700,400]

Similarly you can modify the axis properties - see "Suppressing or replacing plot axes" further down.

If you now overplot a new object:

IDL> q=plot(/overplot,4-findgen(5),color='blue')

then 'q' inherits the window and axes objects (thus q.window and q.axes are the same as p.window and p.axes). One consequence is that if you try to close 'q', thinking that it will only remove the over-plotted line, you find that the entire window gets destroyed. This is because 'close' actually tells IDL to close the window that the object is plotted in, not just the object.

This behavior also helps explain what happens when you do:

IDL> p=plot(findgen(5))
IDL> p=plot(4-findgen(5),color='blue',/overplot)
IDL> p=plot([0,4],[2,2],color='green',/overplot)

Why does IDL not get confused by having three objects of the same name? Because each new 'p' inherits the window and axes from the previous 'p'. You can no longer modify the first 'p' once you've created the second one, but you can modify 'window' and 'axes' since they've just been copied into the new 'p'.

Multiple plots on one page

IDL> p1=plot(findgen(5),layout=[2,1,1])
IDL> p2=plot(findgen(5),layout=[2,1,2],/current)

The POSITION= keyword is also available to directly position a plot within a window.

When annotating multiple plots with the 'text' object (the equivalent of xyouts in direct graphics), make sure to specify which of the existing plot objects is being annotated by using the target= input:

IDL> p1_text=text(1,1,/data,'Hello',target=p1)
IDL> p2_text=text(1,1,/data,'Goodbye',target=p2)

Creating an output file with fixed size

When creating a .eps file with direct graphics, the X-size and Y-size are set with a call to DEVICE. With object graphics, the plot window that is displayed to the screen can be manually adjusted using the mouse, and there's a button in the plot window that allows it to be saved in various formats. For figures for journals, however, you want to have an IDL routine that generates a plot with a fixed size without any manual intervention.

The following shows how a plot can be written to an eps file without manual intervention:

IDL> p=plot(findgen(5),dimension=[600,450],/buffer)
IDL> p.close

The /buffer keyword sends the plot to a buffer rather than the screen.

The dimension is set in pixel units. I notice the size of the output .eps file is larger than the IDL plot window, although the aspect ratio is correct.

Making png/jpeg images

These can be made simply with, e.g.,


However, you'll notice that the image ends up huge, much bigger than the dimensions specified with dimension=. This is because the default is to write the image with 600 dpi (dots per inch). To get an image that is (almost) the same size as the IDL window, do:


This sets the dpi to 97.

Images with axes

With direct graphics there wasn't a built-in routine to plot 2D images with X and Y axes, so many people used the Solarsoft routine 'plot_image'. Using the object function it's easy to produce image plots with axes. One point to note is that plot_image plotted images such that the coordinates corresponded to the center of the pixels. With, they correspond to the bottom-left corners of the pixels so care has to be taken when comparing the two routines.

The functionality of plot_image can be reproduced using the IMAGE object. The following example assumes that 'a' has size 256x256:

IDL> x=findgen(256)-128
IDL> y=findgen(256)-128
IDL> p=image(a,x,y,axis_style=2)

The equivalent of the SCALE= input to plot_image is achieved by simply multiplying X and Y by the scale factors. For example:

IDL> x=(findgen(256)-256)*2
IDL> y=findgen(256)-128
IDL> p=image(a,x,y,axis_style=2)

will stretch the image in the X-direction by a factor 2.

By default, image() will plot the image at its exact size - in this case 256x256 pixels. To make the image fill the display window better, the best option seems to be with the MARGIN= keyword. For example:

IDL> p=image(a,x,y,axis_style,margin=0.10)

Problem with scaling

Even after using the margin keyword, you may want to stretch the image in one direction in order to make a better plot. You can do this with, e.g.,

IDL> p.scale, 1, 1.5

which applies a 1.5 scaling in the Y-direction.

This works fine if the x and y arrays that define the axes are uniformly spaced. If they're not uniformly spaced, then the software seems to apply an interpolation to the data when doing the scaling. You will notice this by the fact that the pixels will not be stretched. I find that the interpolation often introduces ugly artifacts.

If your axes are close to being uniformly spaced, then I suggest re-defining them to make them uniformly-space and then using these new axes.

One example where I've seen this effect is when time is one axis of an image and (typically with space image data) the time difference between each frameis not exactly the same.

Suppressing or replacing plot axes (the axis object)

When you create a plot, it's worth noting that IDL creates the plot (a line plot, symbol plot, image, etc.) and a set of axes. Each axis is it's own object. Thus if you do:

IDL> p=plot(findgen(5))

'p' is really just the straight line representing the data, but IDL has had to create a set of four axis objects representing each of four displayed axes. You can access these axes by doing

IDL> ax=p.axes

'ax' is an object array that specifes each of the four axes. ax[0] is the bottom X-axis, ax[1] is the left Y-axis, ax[2] is the top X-axis and ax[3] is the right Y-axis. You can make any of the axes disappear by using the 'hide' property. For example:

IDL> ax[3].hide=1

gets rid of the right Y-axis. Set hide to 0 to make it reappear.

You can also modify the individual axes separately. For example:

IDL> ax[3].thick=3
IDL> ax[3].color='blue'

You can also perform a coordinate transform to the axis, which is useful if you're using the two Y-axes to represent two different curves. Suppose the left axis represents degrees centigrade, then the right axis can represent by Fahrenheit by doing

IDL> ax[3].coord_transform=[32,9./5.]

(You may have to do ax[3].showtext=1 to show the axis numbers.)


For line and symbol plots, specifying colors is much easier than with direct graphics. For example:

IDL> p=plot(findgen(5), color='red')

Note that only the data will be colored red, with the axes left with their default colors (use xcolor and ycolor to change the axis colors). See the IDL user guide for a complete set of IDL color names, and alternative ways of specifying the color.

For images, you can specify the standard IDL color tables as follows:

IDL> p=image(data, rgb_table=n)

where n is the number of table (e.g., 3 for the standard red table).

Spectrum plot (histogram)

Spectra are usually displayed with a histogram plot, and with direct graphics one uses PSYM=10. With the plot object command, the correct option is /stairstep:

IDL> p=plot(x,y,/stairstep)

Make sure that you don't use the /histogram option as this offsets the spectrum by half a pixel compared to the old psym=10 option.

Object-equivalent for UTPLOT

The SSW routine UTPLOT is widely used for plotting light curves, giving time in a nice format on the X-axis. IDL has its own capability for plotting times, but the times must be in Julian Day format.

Suppose we have a 1D array called TT containing times in any of the standard SSW formats, and an array LC containing the light curve data points. We can plot this with:

IDL> tt_jd=tim2jd(tt)
IDL> p=plot(tt_jd, lc, xtickunits='time')

where IDL will choose which time unit (seconds, minutes, hours, etc.) is best to use based on the data range. You can force it to use hours by using xtickunits='hours'.

The above would give the tick marks as, e.g., '0', '1', etc., for hours. If you'd prefer to have '00:00', '01:00', then you have to specify a format with XTICKFORMAT='(C(CHI2.2,":",CMI2.2))'. Here C() tells IDL to interpret the input as time, then CHI extracts the hour portion, CMI the minute portion, and 2.2 gives the format for the number string. Check the IDL help page for TICKFORMAT for more details.

If you want to set the X-range, you have to set it in Julian Days. So use TIM2JD again to convert your times, and then use XRANGE=.

Application to GOES data

There is object-based software for plotting GOES data - see the page at GSFC. If you want to take the GOES data and plot it with the IDL object software, then follow the method below.

First extract the GOES data for the required time period

t0=anytim2utc('4-mar-2014 12:45',/ccsds)
t1=anytim2utc('4-mar-2014 13:30',/ccsds)

Create the time array in JD format:


Extract the light curve for the low energy channel, and then plot:


IDL Maps

Dominc Zarro created a suite of software for displaying solar images based around "maps", which are IDL structures containing an image with metadata. I've created a routine called plot_map_obj (available in Solarsoft) that allows a map to be plotted as an object:

IDL> p=plot_map_obj(map)

I've tried to re-create the functionality of, so some of the keywords are the same:

/log  - plot logarithm of the image
dmin - specify a minimum intensity value to display
dmax - specify a maximum intensity value to display

One new keyword is /velocity which tells the routine that it's displaying a velocity map and so it uses the familiar red/blue color table. Note that the keyword dmax= specifies the maximum absolute velocity to display.

For over-plotting a map as a contour on another map, do:

IDL> p=plot_map_obj(map1)
IDL> q=plot_map_obj(map2, /overplot [, levels=levels])

where the keyword levels allows the intensity levels of the contour to be plotted.

Color bars for image plots

These are straightforward to add, as illustrated below using a map image:

IDL> p=plot_map_obj(map,/log)
IDL> pcb=colorbar(target=p,orientation=1,title='Log!d10!n Intensity',/border_on)

By specifying the target as the image object, then colorbar automatically picks up the range of intensity values in the image. The orientation keyword determines whether the colorbar appears above the image (0) or to the right side (1). The position keyword can be used to place the colorbar at any location.

Creating a multi-page postscript/pdf file

If you have a lot of data that you'd like to browse quickly, it's useful to send the plots to a single postscript or pdf file, such that single (or multiple) plots appear on each page. You can then quickly flick through the pages (either with an electronic or hardcopy). This can be done using the /APPEND keyword when you save a plot. I give an example below of making a 6-page pdf document with 2x3 plots per page. Note that object "w" gets destroyed and recreated on each "i" loop, but the channel that sends w to the pdf file remains open, irrespective of this.

  FOR j=0,5 DO BEGIN
    p=plot(randomu((i+1)*(j+1)*systime(1),5), $
                   yrange=[0,1],xrange=[0,4],/xsty,/ysty, $
           layout=[2,3,j+1],/current, $

Destroying a plot object

Simply use the 'close' method:

IDL> p=plot(findgen(5))
IDL> p.close

Page maintained by Dr Peter R Young.