POSTSCRIPT PLOTTING PostScript is a page description language that is used by most modern printers for both text and graphics. You can generate Postscript files automatically from programs. For example on the Macs, you can print to a file rather than to the printer and it will save whatever you were going to print out as a Postscript file. However, you can also generate your own Postscript files and that is the subject of these notes. Postscript is ideally suited for user generated graphics because it is in ascii (plain text), is well documented, and is generally easy to understand. Let's start with an example Postscript file: (mypost1) newpath 144 72 moveto 288 432 lineto stroke showpage We start with the NEWPATH operator. This empties the current path and declares we are starting a new path (because we are at the beginning of the file, this command is not actually necessary here but it's a good idea to start with this). Next we move to the point (144, 72): 144 72 moveto The default coordinate system for Postscript files is in units of 1/72 of an inch, measured from the lower left corner of the page. The arguments for moveto are the x and y coordinates. These numbers move onto a "stack" and are then read by the moveto command. Thus, this command moves us to a point 2 inches to the right of the left page edge and 1 inch above the page bottom. This is the "current point" and we can think of this as a pen location. Next we add a line segment between the current point and a new point at (288 432) (4 inches, 6 inches): 288 432 lineto You can think of this as a "draw" command except the draw is not actually executed yet--it is just added to the currently defined path. To draw the path on the page, we use the "stroke" command: stroke Finally, we display the current page: showpage We can preview this file with pageview or ghostview. We can also send it to a Postscript printer. However, to be sure that the printer recognizes that it is a Postscript file, we should always start the file with %! PostScript file, i.e., (mypost2) %! PostScript file newpath 144 72 moveto 288 432 lineto stroke showpage If you don't start with this, then the printer will just print out the ascii text. This is no big deal with a small file like this but is a big problem for a large Postscript file. You don't want 200 pages of text to come out of the printer when you really wanted a single page of graphics! If you ever do make this mistake, you will need to cancel the print job on the printer and/or kill the job on your computer. If you are like me, you will find the 1/72 inch coordinate system an annoyance. Both Bob Parker and I prefer to use 1/1000 inch coordinates. This can by adding the following line at the beginning of your file: 0.072 0.072 scale % Coords are 1/1000 th inch This means that objects will be drawn 0.072 times as large as they would have been drawn with the old coordinate system. The coordinate 1000 thus now indicates 1000*(1/72)*0.072 = 1 inch. Note that % is used to add comments. Anything to the right of the % is assumed to be a comment (the first line is a special "comment" that the printer recognizes). Why use 1/1000 inch as the coordinate instead of inches directly? The nice thing about 1/1000 inch is that this is roughly the limit of what the human eye can resolve on the page so it is not likely that one will need to divide things smaller than this. Thus, one can avoid decimal places in the numbers and can save a little bit of space in the file. We can save more space if we shorten the "moveto" and "lineto" commands since they will be used many times in a complicated file. We can shorten then by defining our own shorter operators: /m {moveto} def /d {lineto} def This tells the Postscipt interpreter to replace "m" with "moveto" and "d" with "lineto". Our revised Postscript file thus becomes: (mypost3) %! PostScript file /m {moveto} def /d {lineto} def 0.072 0.072 scale % Coords are 1/1000 th inch newpath 2000 1000 m 4000 6000 d stroke showpage Now let's draw a box with thicker lines than our starting examples: (mypost4) %! PostScript file /m {moveto} def /d {lineto} def 0.072 0.072 scale % Coords are 1/1000 th inch newpath 4000 4000 m 4000 5000 d 5000 5000 d 5000 4000 d 4000 4000 d 30 setlinewidth stroke showpage We draw the four sides with four "d" (lineto) commands. Before actually drawing the box, we set the line width to 30/1000 inch. The resulting plot will have a slight notch in the starting corner. To avoid this, we can use the "closepath" command: (mypost5) %! PostScript file /m {moveto} def /d {lineto} def 0.072 0.072 scale % Coords are 1/1000 th inch newpath 4000 4000 m 4000 5000 d 5000 5000 d 5000 4000 d closepath 30 setlinewidth stroke CLOSEPATH adds a line segment from the current point to the initial point in the path, thus we don't need the fourth LINETO command. It also closes the path with a mitered join so we don't have the notch as in the previous example. Once a closed path is defined, we can fill in the box using the FILL command: (mypost6) %! PostScript file /m {moveto} def /d {lineto} def 0.072 0.072 scale % Coords are 1/1000 th inch newpath 4000 4000 m 4000 5000 d 5000 5000 d 5000 4000 d closepath 0.5 setgray fill showpage Before doing the fill, we set the gray level for printing at 0.5. The gray levels range from zero (black) to one (white). Of course we can also print text of various sizes and types. Here is an example: (mypost7) %! PostScript file /m {moveto} def /d {lineto} def 0.072 0.072 scale % Coords are 1/1000 th inch /Times-Roman findfont 300 scalefont setfont 2000 3000 moveto (Example text) show showpage Here we set the type of font with "/Times-Roman findfont" and then set the font size with: 300 scalefont This sets the font height to 0.3 inches. The scaled font must then be set to the current font with the SETFONT command. We then move to the point (2000 3000). Finally, we print the string "Example text" at the current location using the SHOW command: (Example text) show We must enclose the text in parentheses to denote it as a string. There are lots of different choices for the fonts. The main choices for science graphics are Helvetica and Times and their bold and italic variations. Well, I could go on with these examples but it would be better to just consult a Postscript book to learn more about all the things one can do. Two good ones are: Postscript Language, Tutorial and Cookbook and Postscript, Language Reference Manual both by Adobe Systems and printed by Addison-Wesley. ---------------------- PSPLOT FORTRAN SUBROUTINES ----------------------- It is useful to be able to generate Postscript files directly from within a Fortran program. Given knowledge of the Postscript language, one could write the appropriate commands to a file. For convenience, I have written a set of Fortran subroutines for making Postscript files that handle a lot of the bookkeeping details and allow the user to concentrate on the graphics. These routines are contained in: /net/rock/shearer/PLOT/psplot.f (Suns) /home/shearer/PROG/PLOT/psplot.f (Macs) To call these routines from a F90 program, they should be linked with: /net/rock/shearer/PLOT/psplotlib_f90.a (Suns) /home/shearer/PROG/PLOT/psplotlib_f90.a (Macs) Documentation for these routines is contained in: /net/rock/shearer/PLOT/psplot.doc (Suns) /home/shearer/PROG/PLOT/psplot.man (Macs) Here is an example program that uses some of these routines: program testpsplot implicit none call PSFILE('mypost') call PSWIND(1.5, 7.5, 3.0, 7.0, 0., 10., 0., 20.) call PSAXES(1., 2., 0.05, 'i3', 5., 10., 0.05, 'i3') call PSLAX('X label', 0.5, 'Y label', 0.5) call PSMOVE(2., 2.) call PSDRAW(5., 8.) call PSEND end program testpsplot Let us consider the commands one at a time: call PSFILE('mypost') This opens the Postscript file with name "mypost" (assigning it to Fortran unit number 17; units 17 and 18 should not be used in any program that uses the psplot routines). We could of course give the file any name we want at this stage. The argument could also be a string variable, allowing the user to input the name. call PSWIND(1.5, 7.5, 3.0, 7.0, 0., 10., 0., 20.) This defines a user coordinate system that will be used for subsequent psplot commands. The first four numbers (x1,x2,y1,y2) define the location of a coordinate "box" on the page in inches. In this case, (1.5, 3.0) and (7.5, 7.0) define the (x,y) coordinates of the lower left and upper right corner of the this rectangle in inches. The next four numbers (x3,x4,y3,y4) define the corresponding values at these points for the user scale. Thus in this case, the lower left point in user coordinates is (0, 0) while the upper right point is (10,20). All other points will be linearly interpolated (or extrapolated) using these values. Often the we will want to plot the frame defined by this imaginary box (as in the PSAXES command below) but this is not required to define the coordinate transformation. call PSAXES(1., 2., 0.05, 'i3', 5., 10., 0.05, 'i3') This draws a frame with tics and numbered tic labels as specified. The position of the frame is assumed to be defined by the corners set in PSWIND. Here is the documentation for the PSAXES command: PSAXES(xtic,xlab,xticl,xfrmt,ytic,ylab,yticl,yfrmt) makes a frame with tics and tic labels as specified. The position of the frame is assumed to be defined by the corners set in WINDOW. xtic = small tic increment ylab = numbered tic increment (length is 2*nxtic) xticl = small tic length (inches) xfrmt = format for x-axis numbers (see PSNUMB for format details) etc. for y Note: Don't make tic increments negative even for reversed scale plots. PSAXES should figure it out correctly. If you don't want tic labels or a particular axis, set xfrmt or yfrmt to a blank character, i.e. to ' '. Next we draw axes labels with the PSLAX command: call PSLAX('X label', 0.5, 'Y label', 0.5) The labels are offset by 0.5 inches from the frame. Next we draw a line between user-coordinate points at (2,2) and (5,8): call PSMOVE(2., 2.) call PSDRAW(5., 8.) Finally, we must close the plot file: call PSEND If you leave out this step, the program will not put a showpage command at the end of the file and your Postscript plot will not work! WARNING: Most of the numerical inputs to the PSPLOT routines must be real numbers! Do not use 2 instead of 2. because the integer and real binary representations of 2 are different (the subroutines have no way of knowing that you used 2 rather than 2.) Note, however, that you can use 'i3' for the format for the axes numbers if you know that there won't be decimal places in the numbers. Here is a more complicated example that demonstrates many of the PSPLOT subroutines. See the psplot.doc file for details. program testpsplot2 implicit none real :: x, y integer :: i, icol character (len=10) :: text call PSFILE('mypost') call PSWIND(1.5, 7.5, 3.0, 7.0, 0., 10., 0., 20.) call PSAXES(1., 2., 0.05, 'i3', 5., 10., 0.05, 'i3') call PSLAX('X label', 0.3, 'Y label', 0.4) call PSTIT('Example title',0.2) call PSNOTE('note that you might want to put down here') call PSFRAM(1., 1.5, 2., 3.) call PSBOX(1., 1.5, 4., 5., 0.5) call PSCBOX(1., 1.5, 6., 7., 0., 1., 1.) call PSGRAY(0.) call PSMOVE(0.5, 9.) call PSDRAW(1., 10.) call PSDRAW(1., 15.) call PSTIC(0.1, 0., 1) x = 3.1415926 call PSNUMB(x,'f6.3') do icol=1,15 x=2.5 y=float(icol)+2. call PSMOVE(x,y) call PSCOL(icol) call PSSYMB(-3,0.15) call PSMOVE(x,y) call PSTIC(0.2, 0., 0) call PSNUMB(float(icol),'i3') enddo call PSGRAY(0.) do i=1,9 call PSLORG(i) x=8.5 y=float(i)*2 call PSMOVE(x, y) call PSSYMB(1, 0.1) write (text,'(a4,i1)') 'lorg', i call PSLAB(text) enddo call PSMOVE(5., 2.) call PSSYMB(1, 0.1) call PSANG(45.) call PSCOL(2) call PSLORG(1) call PSLAB('Red text') call PSMOVE(5., 7.) call PSSYMB(2, 0.05) call PSANG(90.) call PSCOL(3) call PSLAB('blue text') call PSMOVE(5., 12.) call PSFONT('hb14') call PSSYMB(3, 0.05) call PSANG(0.) call PSCOL(1) call PSLAB('bold text') call PSMOVE(5., 15.) call PSFONT('h8') call PSSYMB(3, 0.05) call PSANG(0.) call PSCOL(1) call PSLAB('small font') call PSEND end program testpsplot2 The PSIMAG command allows the display of a bitmap in either black and white or color. This can be rather tricky to use but is powerful once you understand how it works. Here is an example program: program testpsimag implicit none integer :: i, j integer*2, dimension(50, 50) :: ibuf integer*2, dimension(3, 50, 50) :: ibuf3 integer :: red, green, blue real :: gray call PSFILE('mypost') ! First do black and white image call PSWIND(1.5, 7.5, 6.0, 9.0, 0., 50., 0., 50.) do i = 1, 50 do j = 1, 50 gray = sqrt(real(i*j))/50. ibuf(i,j) = nint(gray*255.) !must be 0 to 255 enddo enddo call PSIMAG(ibuf, 50, 50, 0) call PSAXES(5., 10., 0.05, 'i3', 5., 10., 0.05, 'i3') call PSLAX('X label', 0.25, 'Y label', 0.4) call PSTIT('Square root of x*y' ,0.15) ! Then do color image call PSWIND(1.5, 7.5, 2., 5., 0., 1., 0., 1.) do i = 1, 50 do j = 1, 50 red = nint(real(i)*(255./50.)) green = nint(real(j)*(255./50.)) blue = 0 ibuf3(1,i,j) = red !must be 0 to 255 ibuf3(2,i,j) = green ibuf3(3,i,j) = blue enddo enddo call PSIMAG(ibuf3, 50, 50, 1) call PSAXES(0.1, 0.2, 0.05, 'f4.1', 0.1, 0.2, 0.05, 'f4.1') call PSLAX('Postscript Red', 0.3, 'Postscript Green', 0.45) call PSTIT('RGB colors, Green = 0', 0.15) call PSEND end program testpsimag ASSIGNMENT POST1 Write a F90 program to read the station coordinates and station names from the file station.list, which you can get from: igpphome.ucsd.edu/~shearer/Files/COMP/ or via anonymous ftp to mahi.ucsd.edu in the /pub/239/GMT directory). The station.list file has the following format: latitude longitude dep name 34.10450 -117.09700 600 SVD 22.79167 5.52700 1395 TAM 24.97540 121.48810 53 TATO Using the PSPLOT subroutines, make a Postscript map of southern California from -121 to -114 longitude and 32 to 37 latitude. Plot the axes using a reasonable tic interval and axes labels. For those stations within the map limits, plot the name of the station, centered on the station coordinates. Please e-mail me to me the source code for your program.