Coyote's Guide to IDL Programming

Positioning and Sizing Images in PostScript

QUESTION: Can someone help me position or size images in PostScript?

ANSWER: PostScript devices use scalable or variably-sized pixels. If you don't understand how scalable pixels work you will never be able to position or size a image correctly on a PostScript page.

To the uninitiated, placing an image on a page along with other IDL graphics must be one of the most frustrating, aggravating, impossible tasks known to IDL programmers. I don't know how many times I've been in the act of picking the computer up to throw through the window when I was stopped by a sympathetic co-worker. Here is what you need to know.

First of all, there are two sizes that are important. The PostScript window size and the image size. The PostScript window size is determined by the XSIZE and YSIZE keywords to the DEVICE command, like this:

   SET_PLOT, 'PS'
   DEVICE, XSIZE=5, YSIZE=4, /INCHES

The default PostScript window size is 7 inches in X and 5 inches in Y. The PostScript window is analogous to the IDL graphics window on your display. That is, it is the location where graphics are going to be drawn in the larger universe of the display or PostScript page.

In the absence of other information, the PostScript device uses the PostScript window size to size the image. If (and only if) the aspect ratio of the image is exactly the same as the aspect ratio of the PostScript window, then the image will be exactly as big as the PostScript window. If the aspect ratio of the image is different from the aspect ratio of the PostScript window, then the image will be sized in such a way that the image maintains its aspect ratio and the image fits into the PostScript window.

To give a simple example, suppose you have a square 256-by-256 image. And suppose you set up your PostScript window like this and display the image like this:

   DEVICE, XSIZE=4, YSIZE=4, /INCHES
   TV, image

Then the output image will be exactly four inches square.

But, suppose your output window was not square but had an aspect ratio of 3:5, like this:

   DEVICE, XSIZE=5, YSIZE=3, /INCHES

Then the same TV command would result in an image that was three inches square, located on the left-hand side of the rectangular window. In other words, there would be two inches of empty space in the right-hand side of the window.

Combining Images with Other Graphics

Although a little strange, the way images are sized by the PostScript window size can be accommodated easily until you try to combine an image with other graphics. For example, you want to display a colorbar next to an image, or you want to put axes around your image, etc. Then it becomes hopeless... Unless you know that PostScript images should really be sized with the TV or TVSCL commands.

In other words, suppose I want my image to be 2 inches square and located in the center of my 5 inch by 3 inch window, above. I would do it like this:

   DEVICE, XSIZE=5, YSIZE=3, /INCHES
   TV, image, 1.5, 0.5, XSIZE=2, YSIZE=2, /INCHES

Notice that the image start locations and the image sizes are given in inches in the TV command above. These four parameters can be given in device or pixel coordinates, normalized coordinates, or inches. They cannot be given in centimeters (which are the normal sizing unit for PostScript!). In other words, I can achieve the same effect using device coordinates, by typing this:

   windowXSize = !D.X_SIZE
   windowYSize = !D.Y_SIZE
   xstart = (1.5/5.0) * windowXSize
   ystart = (0.5/3.0) * windowYSize
   xsize = (2.0/5.0) * windowXSize
   ysize = (2.0/3.0) * windowYSize
   TV, image, xstart, ystart, XSIZE=xsize, YSIZE=ysize

Or, I can achieve the same results in normalized coordinates by typing this:

   xstart = (1.5/5.0)
   ystart = (0.5/3.0)
   xsize = (2.0/5.0)
   ysize = (2.0/3.0)
   TV, image, xstart, ystart, XSIZE=xsize, YSIZE=ysize, /NORMAL

This method of sizing images with the XSIZE and YSIZE keywords to the TV command is, of course, not the way it is done on the display. In fact, the XSIZE and YSIZE keywords are ignored by any display device that does not have scalable pixels. Most of the time images are sized by using the CONGRID or REBIN commands. However, with the significantly increased resolution of the PostScript device, these commands cannot be used. (You get "Unable to allocate memory for array" error messages if you try.) In practice, this means you usually have to have two different display commands for images. Your code might look like this:

   windowXSize = !D.X_SIZE
   windowYSize = !D.Y_SIZE
   xstart = (1.5/5.0) * windowXSize
   ystart = (0.5/3.0) * windowYSize
   xsize = (2.0/5.0) * windowXSize
   ysize = (2.0/3.0) * windowYSize
   IF !D.NAME EQ 'PS' THEN $
      TV, image, xstart, ystart, XSIZE=xsize, YSIZE=ysize ELSE $ ; PostScript
      TV, CONGRID(image, xsize, ysize), xstart, ystart           ; Display

I usually work around this issue by using the cgImage program for displaying images. The cgImage program allows me to position and size images with the POSITION keyword, just like I do for other IDL graphics displays. It works in exactly the same way in a display window and in a PostScript window. For example, the result above can be achieved with this code:

   xstart = (1.5/5.0)
   ystart = (0.5/3.0)
   xend = (2.0/5.0) + xstart
   yend = (2.0/3.0) + ystart
   cgImage, image, POSITION=[xstart, ystart, xend, yend]

I particularly like the cgImage program because I can work in a natural way with other IDL graphics commands that also use the POSITION keyword to position graphics in an IDL window. For example, to display an image with a contour plot on top of it in any window of any size in any display device, I can type this:

   image = DIST(400)
   thisPosition = [0.15, 0.15, 0.85, 0.85]
   cgImage, image, POSITION=thisPosition
   cgContour, image, /NOERASE, POSITION=thisPosition

Google
 
Web Coyote's Guide to IDL Programming