IDL Programming Techniques 2nd Edition
IDL Programming Techniques 2nd Edition
Programming
Techniaues I
Second Edition
--
David W;Fanlzing, Ph.D.
IDL Programming Techniques
Second Edition
David W. Fanning, Ph.D.
Copyright O 2000 Fanning Software Consulting. All lights reserved. No part of the contents of this book
may be reproduced or transmitted in any form or by any means without the written permission of the
publisher.
Printing History
October 2000: First Printing
November 2000: Added information on double buffering of graphics displays.
April 2003: Minor updates to image processing section.
ISBN 0-9662383-2-X
ISBN 0-9662383-2-X
April 2003
+ Discovering the Possibilitie
Table of Contents
iii
Help with IDL Commands 8
Creating Command Journals 8
Creating Variables 9
Variable Attributes Change Dynamically 10
Be Careful With Integer Variables 11
Working with Vectors and Arrays 13
Creating Vectors 13
Using Array Subscripts 13
Creating Arrays 14
Accessing Elements in Arrays 14
Extracting Vectors and Subarrays 15
Working with IDL Graphics Windows 15
Creating Graphics Windows 15
Determining the Current Graphics Window 16
Making a Graphics Window the Current Graphics Window 16
Deleting Graphics Windows 17
Positioning and Sizing Graphics Windows 17
Bringing a Graphics Window Forward on the Display 17
Putting a Title on a Graphics Window 18
Erasing a Graphics Window 18
An Alternative to TVRD 73
Basic Image Processing in IDL 74
Histogram Equalization 74
Smoothing Images 75
Removing Noise From Images 77
Enhancing the Edges of Images 77
Frequency Domain Filtering of Images 78
Building Image Filters 78
viii
Adding Attributes to Scientific Data Sets and HDF Files 170
Gathering Information about Scientific Data Sets 172
Gathering SDS Attribute Information 173
Adding Color Palettes to HDF Files 173
Examples of Reading and Writing HDF Files 174
xiv
CW-FSlider Event Stl-ucture 393
CW-Orient Event Structure 394
CW-PDMenu Event Structure 394
CW-RGBSlider Event Structure 394
CW-Zoom Event Stl-ucture 394
FSC-InputField Event Structure 394
Widget Program Event Sttuctures 394
XColors Event Structure 394
ReadImage Event Structure 395
Other Widget Event Stsuctures 395
Keyboard Focus Events 395
Kill Widget Request Events 395
Widget Timer Events 395
Widget Tracking Events 395
Index ...................
IIIlI~lLllmmIlnnllmu~Blmulmmmllm~lmmmllBllllnlamlllmlmllmlnlllllllllmmBmlllmll 435
Preface
Preface to the First Edition
Writing a book must surely be one of the most difficult, solitary, and lonely things I
have ever done. And yet, paradoxically, it cannot be done at all without the help and
support of a great many people. Foremost among these is my wife, Carol. It is not pos-
sible to write a book, make a living, and pay adequate attention to hearth and home, all
at the same time. Carol has made it possible-in more ways than you can imagine-to
have this obsession realized. I am deeply grateful to her and to our three sons for the
time I borrowed from their lives. Meny Christmas! I am finally home.
Many people have read earlier versions of this book and have offered their comments
and suggestions. I wish to thank all of them for their help. I am particularly indebted to
Dick Jackson of the National Research Council of Canada who volunteered a great
many hours of his own time to read the entire manuscript from start to finish and offer
extensive valuable suggestions. This is a much better book as a result of his efforts. I
am also thankful to many fine folks at Research Systems and on the IDL newsgroup
who have patiently answered many of my questions about IDL and have been support-
ive of my work. The errors that remain in the book are entirely my own.
I owe a special debt of gratitude to David Stern, the founder of Research Systems and
the original creator of IDL. I spent five of the best years of my professional life work-
ing for David. I am deeply grateful for the freedom David gave me to pursue my
overriding interest, which was to teach people how to use IDL. Working with IDL is
the best job I have ever had.
I don't think this book would have ever happened without a thin volume of stories by
Barry Lopez, called River NotesDesert Notes. I don't know why this book speaks to
me in the way it does, but I know I owe much of my professional life and development
to its messages. Stories tell us who we are and connect us to the world we live in. I
often tell his wonderful story, Directions, in my IDL courses. It is a powerful symbol
to me of what I am about both personally and professionally. Thank you, Mr. Lopez,
for guiding and nourishing me with the mystery of your stories.
Finally, I wish to thank the hundreds of people who have attended my IDL program-
ming courses over the past six years. I learned almost everything I know about IDL
from you. Thanks for making those workshops warm, safe places where we could all
make mistakes and learn from one another. This book is written especially for you.
Fort Collins, Colorado
Christmas Eve, 1997
xvii
Preface
xviii
Getting Started
Chapter Overview
The purpose of this chapter is to explain why I wrote this book, what you can expect to
get out of reading it, and to give you information that will make it easier to work
through the IDL programming examples you find here. Specifically, you will learn:
* How this book is organized
* How to use this book
How to download and organize the files that come with this book
How to use variables, keywords, and commands in IDL
How to create and work with vectors and arrays in IDL
How to work with graphics windows in IDL
book is an overview of the essential elements of IDL for people who don't like to read
manuals and who learn best by example. It is a compendiun~of IDL programming tips
and techniques that can only be learned by experience. Essentially, this is the book I
wish I had when I was learning to use IDL!
Realize that if you change the current color table on a machine with a 24-bit display
and you are working at the IDL command line, you will almost certainly need to re-
type the graphic display command to see the new colors take effect. This is normal
and is a direct effect of how 24-bit color works. You will see how to deal with this
more effectively later in the book.
You can determine the depth of your color display by typing these two commands:
IDL> Device, Get-Visual-Depth=thisDepth & Print, thisDepth
Commands that you should type at the IDL command line are preceded by the IDL
command prompt, IDL>, like this:
IDL> Surface, data
Other IDL commands will be typed in editor windows. You can use the editor of your
choice or the editor that is supplied with IDL. It is up to you.
Capitalization
I use a particular style of capitalization for IDL commands in this book. This style is
completely arbitrary. IDL is case insensitive, except for commands that interact with
the operating system (e.g., the file names of commands that open files will be case
sensitive on UNIX machines) and when it is performing string comparisons. The
capitalization is meant to help you remember command and keyword names and to
give you a visual clue as to the function of words on the command line.
I capitalize the first letter of all IDL commands and keywords. In addition, any letter
that may serve as a mnemonic is also capitalized. For example:
Surface, data, CharSize=2.0, Color=180
XLoadCT
Widget-Control, tlb, Set-Walue=info, /NO-Copy
I do not capitalize the first letter of variable names, although I may capitalize
subsequent letters in variable names that may be compound words. For example:
data = FIndGen(l1)
buttonvalue = thisvalue
ptrToData = Ptr-New ( )
I completely capitalize IDL reserved words. For example:
REPEAT test UNTIL
FOR j=0,10 DO BEGIN
ENDWHILE
You may use any capitalization you like when you type the commands at the IDL
command line or into a text editor.
Comments
Anything to the right of a semi-colon on an IDL command is treated as a comment by
IDL and is ignored by the IDL interpreter. In general, I try to write comments on their
own line in an IDL program, usually set off by a blank space before and after it and
indented three spaces from the line it is commenting on. For example:
; This is the loop part of the program.
FOR j=0,10 DO BEGIN
data = j*2
count = count + j
ENDFOR
Occasionally you will see a comment on the end of a command line. I do this
especially when I am documenting the fields of an IDL structure variable. For
example:
info = {r:r, $ ; The red color vector
g:gt $ ; The green color vector
b:b ) ; The blue color vector
Getting Stwted
If you are using anonymous ftp, the files can be found via an Internet browser at:
Copy all program files in text or ASCII mode. If you like-and your computer can
uncompress a zip file archive-you can copy all the program and text files at once by
copying the coyotefiles.zip file. This is a zip file archive of the program files you need.
The data files will be collected from their various locations and copied into your
cunent directory. There is a list of the data files that will be used in the book, along
with their data types and sizes, in "Appendix B: Data File Descriptions" on page 397.
Positional Parameters
The three variables peak, lorz, and 1at in the command above are also called positiorznl
parameters. In this particular instance, these positional parameters are irzput variables
(i.e, they are bringing data into the command), but you cannot tell this by looking at
them. They could just as easily be output variables. (Or they could be both input and
output variables, for that matter.) The command line syntax is exactly the same. You
will only be able to tell by context and by reading the published documentation for the
command or program.
Working with ZDL Contntands
A positional parameter has a defined sequence or order to the right of the command
name. (Note that key~~ordparcimeters, discussed below, do not affect positional
parameter order.) In this case, the variable peak must be to the right of the command
Contocn. and to the left of the variable 1011,for example. The variable 1011must be to
the right of peak and to the left of Iat, and so on. You cannot leave out, say, the second
positional paranleter and specify the first and third.
For example, these two commands are incorrectly formatted and will cause errors.
The first because the order of the positional parameters is changed, and the second
because the second positional parameter is not present.
Contour, lon, peak, lat, XStyle=l, YStyle=l, / ~ o l l o w ,$
Levels=vals, C-Labels= [l,O,l,0, O I I I l01
r
Contour, peak, , lat, XStyle=l, YStyle=l, / ~ o l l o w ,$
Levels=vals, C ~ L a b e l s = [ l r O r l , O , O , 1 , 1 , 0 ]
Positional parameters are often required parameters to the command, but they do not
have to be. For example, in the cosrect command above peak is a required parameter
to the Contoul. command, but lorz and lat are optional positional parameters. Again,
you will know this by reading the published documentation for the command.
Keyword Parameters
XStyle, YStyle, Follo~v,Levels, and C-Labels are keyword pamneters. Unlike
positional parameters, keyword parameters can come in any order to the right of the
command name. They can even come in the midst of positional parameters without
affecting the relative positions of those parameters. In other words, keyword
parameters are not counted like positional parameters. This is a valid construction of
the Contour command above:
Contour, peak, Levels=vals, lon, X ~ t y l e = l ,~ ~ t y l e = l$ ,
/ ~ o l l o w ,Levels=vals, lat, ~ - ~ a b e l s = [ l , O , l , O , O ~ 1 ~ 1 , 0 1
By convention keyword parameters are optional parameters. Like positional
parameters they can be input parameters or output parameters to the command. You
will know by context and by reading the documentation for the command.
Notice the way in which the keywords are used in the command above. Keywords can
be set to a particular value (e.g., xstyle=l), to a variable (e.g., ~evels=vals),to a
vector of values (e.g., C-~abels=[I,O,l,O,o , l ,1,0]), and even set with a slash
character (e.g., /~ollow).
Consider the latter syntax more closely. Some keywords have a binary quality. That is,
they are either onloff, yeslno, tmelfalse, 110, etc. You often find these keywords being
"set" or "turned on" by the syntax /Keyword. The syntax /Keyword is identical to
(means the same as) the syntax Keywoid=l. No more and no less.
In fact, the Coiztour command above could have been wt-itten like this:
Contour, peak, Levels=vals, lon, /xstyle, / ~ ~ t y l e$,
/ ~ o l l o w ,Levels=vals, lat, C-Labels=[l,O,l,O,O,l,1,0]
This command means the same thing as the command above. The reason the
command was not written like this, is that it might falsely imply that the XStyle and
YStyle keywords have a binary quality (i.e., they are either on or off), which they
don't. They can be set to other values besides 0 and 1.
IDL> a = [ 3 , 5, 7 , 3 , 6, 91
IDL> Help, a
IDL> Plot, a
When you want to close the journal file, just type the Jozirltal command again, all by
itself at the IDL command line, like this:
IDL> Journal
The journal file is a simple ASCII text file that you can edit, if you like, with any text
edito:; including the editor built into IDL Development Environment. When you want
to replay the commands in the journal file, use the @ sign as the first character on the
IDL command line. For example, to replay the commands in the book-commartds.pro
file above, type this:
Be sure to give each journal file you create a unique name. You cannot append to jour-
nal files, so if you open a second file with the same name as the first many operating
systems will simply overwrite the first journal file without warning.
If you would like to have a unique journal file name each time you wanted a journal
file, you could write an IDL program like this:
PRO Journal-Unique
Journal, String ( l journal-l , in-Date (SysTime( ) ) , .pro , $
Format=' (A, 14, 512.2, A) l)
END
Then, instead of typing Jour~zal,you can type Journal-Unique to open a journal file
with a unique name. This file has already been written for you and is one of the files
you downloaded to use with this book.
Creating Variables
You will be creating many variables in this book. It will help if you know a little about
them before you get started. Variable names must start with a letter. They can include
other letters, digits, underscore characters, and dollar signs. A variable name may
have up to 255 characters. A convention used in this book is to make the initial letter
in variable names lowercase. Here are some valid variable names:
pt rToDat a
image2
this-image
ashandle
Variables have two important attributes: a data type, and an orgarzizntiorzal structure.
Data type refers to the kind of data it is. There are 14 basic data types in IDL (as of
IDL 5.3). In Table l you see each basic type, the size of each variable type in bytes,
the way a variable of that type can be created, and the name of the IDL function that
can convert a variable to that data type. In addition to a data type, a variable has an
organizational structure. Valid organizational structures are scalars (i.e., single
values), vectors (really a one-dimensional array), arrnys (of up to eight dimensions),
and IDL structures (a type of organization that can contain variables of various data
types and organizational structures in separate compartments calledfields).
As you will see, IDL is a program that excels at working with vector or array data, so
there are a number of built-in IDL commands for creating vectors and arrays of the
different data types. In particular, there are functions for creating arrays of the proper
data type in which each element is initialized to zero, and there are functions for
creating arrays of the proper data type in which each element is initialized to its own
Getting Started
index in the array. You see a list of these functions in Table 2. For example, to create a
100 by 100 byte anay of zeros, you can type this:
IDL> array = BytArr (100,100)
To create a vector of 100 floating point values ranging in value from 0 to 99, you can
type this:
IDL> vector = FIndGen(100)
You will see many ways to use these IDL functions in this book.
Table 1: The 14 basic data types iiz ZDL. Also showit is the size of t12e variable,
tlze way a variable of that type caiz be created, and tlze IDL fuizctiolz that
caiz be used to cast or coerce a variable to that data type.
value. This is because IDL "promotes" variables to the data type that preserves the
most accuracy in mathematical calculations. When nun^ is redefined (on the left hand
side of the equal sign) it is promoted to a float to maintain the accuracy of the floating
point calculation on the right hand side of the equal sign.
Consider this example:
result = 4 * X
In this case, it is impossible to know what data type and organizational structure the
variable result will have because you know nothing about the variable x.In fact, reszrlt
will depend almost completely on the data type and organizational structure of
variable x.If x is a floating-point vector of 10 elements, result will also be a floating-
point vector of 10 elements. If it is a long-integer array of size 100 by 200, result will
be the same. Note that if x has a date type of byte, that result will always have a data
type of integer. (Organizational structure doesn't matter, in this case.) This is a result
of the multiplication by an integer value.
.. Remember that the expression on the right-hand side of the equal sign is always eval-
uated before a data type and organizational structure can be assigned to the variable on
the left-hand side of the equal sign. IDL will promote variables to the data type that
maintains the most precision in evaluating the expression
It might take you a long time to figure out why your aspect ratio is always O! The
correct way to write this code is to force one of the integer values to be a float, like
this:
aspect = Float ( !D.X-Size) / !D.Y-Size
Now your aspect variable has the floating value you expect.
The other common way to get into trouble with integer variables is to not realize that
integers in IDL are what are often called short integers in other programming
languages. In other words, an integer in IDL is only two bytes long. Integers in most
other programming languages are four bytes long (called a long integer in IDL).
A two-byte signed integer can only have positive values up to 32,767. Values greater
than this "overflow" and are usually represented in IDL as negative numbers.
You can have trouble with short integers in two ways. First, you don't take account of
short integers in loops. For example, suppose you wanted to read a data file and you
didn't know how many lines there were in it. You might write a piece of code like this:
count = 0
WHILE NOT EOF (lun) DO BEGIN
Getting Started
Table 2: IDL functions that will create vectors and arrays of multiple dimen-
sions, either initialized to 0 or initialized to their own index number.
If you had more than 32,768 lines of data, this code would fail. The reason is that the
variable couizt is initialized as an integer. It should be initialized as a long integer:
count = OL
WHILE NOT EOF (lun) DO BEGIN
READF , lun , temp
data(count) = temp
count = count + 1L
ENDWHILE
Now you can read as many lines as you like.
Another common place to find this error is in the counter for For loops. It is a good
idea to always write your For loop command something like this:
FOR j=OL,num-l DO ...
The second way you might get into trouble with short integers is when you try to read
data that was produced by a program written in some other programming language (or
vice versa). If you are going to read integer data produced by a C or Fortran program,
you almost always want to be sure you are reading into lolzg integers in IDL.
Similarly, you will want to write long integer data to files that will be read by a C or
Fortran program as integers.
Working with Vectors and Arrnys
Note that a new compiler option in IDL 5.3 can force IDL integers to be four-byte
integers rather than two-byte integers by default. Normally this compiler option com-
mand is placed at the beginning of IDL procedures and functions. It causes the com-
piler to force all integer valiables in that procedure or function to be four-byte long
integers.
Compile-Opt DEFINT3 2
Creating Vectors
You can create a vector (a vector is just a one-dimensional array) or an assay at the
IDL command line by enclosing the vector values in square brackets, like this:
IDL> v e c t o r = [l, 2 , 31
This is an integer vector because the data values are integer values.
You can get information from IDL about the data type and organizational structure of
variables by using the Help command, like this:
IDL> Help, v e c t o r
VECTOR INT = Array [3]
If you wanted to add a fourth element to this vector, this is easily done in IDL. Just
type this, for example:
IDL> v e c t o r = [ v e c t o r , 41
IDL> P r i n t , v e c t o r
Notice that vector subscripts start at 0 and not at 1. Notice also that vector subscripts
use parentheses to distinguish themselves. This makes it difficult sometimes to
distinguish a call to a function command from a subscsipted array. To help with this
problem, square bracket subscript notation was introduced in IDL 5.0. In other words,
if you are running IDL 5.0 or higher you can type this:
IDL> P r i n t , v e c t o r [ O :21
Beginning with IDL 5.3, you can force square bracket notation by setting a compiler
option. Normally this compiler option command is placed at the beginning of IDL
Getting Started
procedures and functions. It causes the compiler to enforce square bracket subscript
notation in that procedure or function.
Compile-Opt STRICTARR
This book uses square bracket subscripting to avoid any confusion with function calls.
If you are using an IDL 4.x version of IDL, you will have to substitute parentheses for
square brackets in the code to get the commands to work.
To use array subscripting to put another element between the second and third
elements of the vector, you can do this:
IDL> v e c t o r = [ v e c t o r [O : l ] , 5 , v e c t o r [2 : 31 1
IDL> P r i n t , v e c t o r
Vectors can also be created by using the array creation routines discussed above. For
example, to create a 6-element floating-point vector with values ranging from 0 to 50,
you can type this:
IDL> v e c t o r = FIndGen(6) * 10
IDL> P r i n t , v e c t o r
Creating Arrays
Arrays can also be created from the IDL command line. For example, we can create a
3 column and 2 row array like this:
IDL> a r r a y = [ [ l , 2 , 31 , [ 4 , 5 , 61 ]
IDL> P r i n t , a r r a y
Your output in your IDL output window will look like this:
Notice that this is identical to first creating a vector and then reformatting that vector
into a 3 column by 2 row array with the Refonn command, like this:
IDL> v e c t o r = IndGen ( 6 ) + 1
IDL> a r r a y = Reform(vector, 3 , 2 )
IDL> P r i n t , a r r a y
What this tells you is that vectors and arrays are stored in IDL in row order. This
becomes important when you are writing IDL programs because you will often want
to take advantage of the way data is stored in IDL.
simply an arbitrary choice. There is no particular reason to choose one way or the
other.
You can access the same element in this array using one-dimensional subscripts.
Knowing that array elements are stored in row order, you want the fourth element in
the array. You can access it like this:
IDL> Print, array [3]
The fact that you can access multi-dimensional arrays with one-dimensional
subscripts is a powerful tool in many D L programs.
You can also subscript arrays with vectors. For example, if you want to print the first,
second, fourth, and sixth element of the array, you can type this:
IDL> indices = [O, 1, 3, 51
IDL> Print, array [indices]
IDL> Window
Notice that the title bar of this window has a 0 in it. This is this window's graplzics
wirzdow irzdex rl~fl?~bei-.
Each graphics window has an unique graphics window index
number associated with it when the window is created. The Window command without
any positional parameters always creates a window with window index number 0. We
say this is "Window 0".You can have at least 128 graphics windows open at any one
time in an IDL session. You can assign a graphics window index number for windows
0 through 3 1. IDL will assign graphics window index numbers for windows 32
through 127 by creating windows with the Free keyword (discussed below). For
example, if you want to create a window with graphics window index number 10, you
can type this:
IDL> Window, 10
If a window with the same graphics window index number already exists on the
display, a Wirzdow command like this will first destroy the old one and then create a
new one with this index number.
If you prefer (this is always a good idea when you are creating windows in IDL
programs), you can open a window with a graphics window index number that is
"free" or unused. The Free keyword is used for that purpose, like this:
IDL> Window, /Free
A window that is created with a Free keyword will have a graphics window index
number greater than 3 1. The Free keyword is the only way to create a normal graphics
window with an index number greater than 3 1.
Note that on PCs and Macintosh computers, you can use the ALT-TAB or OlTION-
TAB keys, respectively, to cycle through and select windows that are open but not cur-
rently visible on the display and make them the window with the window focus.
Chapter Overview
The bread and butter of scientific analysis is the ability to see your data as simple line
plots, contour plots and surface plots. In this chapter, you will learn how easy it is to
display your data this way. You will also learn to use system variables and keywords
to position and annotate simple graphical displays.
Specifically, you will learn:
Q
How to display data as a line plot with the Plot command
* How to display data as a surface with the Sulfice and Sl~ade-Su~fcommands
How to display data as a contour plot with the Corztour command
Q
How to position simple graphical displays in the display window
* How to use common keywords to annotate and customize your graphical displays
are written. Object graphics comnlands are not really meant to be typed at the IDL
command line. Rather, they are included in IDL programs, especially widget
programs (programs with graphical user interfaces). Object graphics commands are
discussed later in this book.
You see that cuive is a floating point vector (or one-dimensional array) of 101
elements.
To plot the vector, type:
IDL> Plot, curve
IDL will try to plot as nice a plot as it possibly can with as little information as it has.
In this case, the X or horizontal axis is labeled from 0 to 100, corresponding to the
number of elements in the vector, and the Y or vertical axis is labeled with data
coordinates (i.e., this is the dependent data axis).
But most of the time a line plot displays one data set (the independent data) plotted
against a second data set (the dependent data). For example, the curve above may
represent a signal that was collected over some period of time. You might want to plot
the value of the signal at some moment of time. In that case, you need a vector the
same length as the curve vector (so you can have a one-to-one correspondence) and
scaled into the units of time for the experiment. For example, you can create a time
vector and plot it against the curve vector, like this:
IDL> time = FIndGen(lO1)* (6.0/100)
IDL> Plot, time, curve
The FIlzdGerz command creates a vector of 101 elements going from 0 to 100. The
multiplication factor scales each element so that the final result is a vector of 101
elements going from 0 to 6. Your graphical output should look similar to that in
Figure 1.
Creating Line Plots
Figure I: A plot of the independent data (time) versus the dependent data (curve).
Notice that there are no titles on the axes associated with this plot. Placing titles on
graphics plots is easy. Just use the XTitle and YTitle keywords. For example, to label
the curve plot, you can type this:
IDL> Plot, time, curve, XTitle=ITime Axis1, $
YTitle=ISignal Strength1
You can even put a title on the entire plot by using the Title keyword, like this:
JDL> Plot, time, curve, XTitle='Time Axis1, $
YTitle=lSignal Strength1, Title=lExperiment 35M1
Your output will look similar to the illustration in Figure 2. Note that the display
shows white lines on a black background, while the illustration shows black lines on a
white background. These illustrations are encapsulated Postscript files produced by
IDL. It is common for the drawing and background colors to be reversed in PostScript
files. (See "Problem: PostScript Devices Use Background and Plotting Colors
Differently" on page 189 for more information.)
Experiment 35M
30
5
m
E 20
G
-
gm 10
iTj
0
0 2 4 6
Time Axis
Figure 2: A simple line plot with axes labels and a plot title.
Si~npleGraphical Displays
Notice that the plot title is slightly larger than the axes labels. In fact, it is 1.25 times
as large. You can change the size of all the plot annotations with the Charsize
keyword. For example, if your eyes are like mine, you might want to make the axis
characters about 50% larger, like this:
IDL> Plot, time, curve, XTitle=ITime Axis', $
YTitle='Signal Strength1, Title=l~xperiment3 5 M 1 , $
CharSize=1.5
If you want the character size on all your graphic displays to be larger than normal,
you can set the ChnrSize field on the plotting system variable, like this:
IDL> ! P.CharSize = 1.5
Now all subsequent graphics plots will have larger characters, unless specifically
overruled by the ChnrSize keyword on a graphics output command.
You can even change the character size of each individual axis by using the
[XYZIChnrSize keyword. For example, if you want the Y axis annotation to be twice
the size of the X axis annotation, you can type this:
IDL> Plot, time, curve, XTitle='Time Axis', XCharSize=l.O, $
YTitle=lSignal Strength', YCharSize=2.0
Note that the [XYZIChnrSize keywords use the current character size as the base from
which they calculate their own sizes. The current character size is always stored in the
system variable !l?CharSize. This means that if you set the XCharSize keyword to 2
when the !RCharSize system variable is also set to 2, then the characters will be four
times their normal size.
Table 3: The line style carz be clta~zgedby assignirzg these index nz~~nbers
to the
Linestyle keyword.
The thickness of lines used on line plots can also be changed. For example, if you
wanted the plot displayed with dashed lines that were three times thicker than normal
you can type this:
IDL> Plot, time, curve, LineStyle=2, Thick=3
Figure 3: A line plot with the data plotted as symbols instead of as a line.
To create larger symbols, add the SyrlzSize keyword like this. Here the symbol is twice
the "normal" size. A value of 4 would make the symbol four times its normal size, and
SO on.
4 Diamond
5 Tiiangle
6 Square
7 X
8 User defined (with the UserSyn? procedure).
Table 4: Tlzese are the index izuntbers you can ztse witlt tlze PSym keyword to
produce different plotting symbols on your plots. Note that using a
negative number for the plotting symbol will coizrtect the symbols with
lines.
color triples that describe the charcoal, yellow, and green colors.) For example, load a
charcoal, yellow and green color into color indices 1,2, and 3 like this:
IDL> TVLCT, [70, 255, 01, [70, 255, 2551 I [70, 0, 01 I 1
To draw the plot in yellow on a charcoal background, type:
IDL> Plot, time, curve, Color=2, Background=l
If nothing appears in your display window when you type the command above, it is
likely because you are running IDL on a 24-bit color display and you have color
decomposition turned on. You will learn more about color decomposition later, but for
now, be sure color decomposition is off. Type this to produce the proper colors:
IDL> Device, Decomposed=O
IDL> Plot, time, curve, Color=2, Background=l
If you want just the line to be a different color, you must first plot the data with the
NoDntn keyword turned on, then overplot the line with the OPlot command
(discussed below). For example, to have a yellow plot on a charcoal background, with
the data in green, type:
IDL> Plot, time, curve, Color=2, Background=l , / ~ o ~ a t a
IDL> OPlot , time, curve, Color=3
Figure 5: A plot with the zero poirzt of the Y axis located ort the top of the plot.
If your chosen axis range doesn't fall within IDL's sense of what an aesthetically
pleasing axis range should be, IDL may ignore the asked-for range. For example, t ~ y
this command:
IDL> Plot, time, curve, XRange=[2.45, 5.641
The X axis range goes from 2 to 6, which is not exactly what you asked IDL to do. To
make sure you always get the axis range you ask for, set the XStyle keyword to 1, like
this:
IDL> Plot, time, curve, XRange=[2.45, 5.641, XStyle=l
You will learn more about the [XYZIStyle keywords in the next section.
Table 5: A table of the key~vordvalues for the [XYZIStyle keywords that set
properties for the axis. Note that values cart be added to specih more
than one axis property.
Czrstoinizing Graphics Plots
You can turn an axis off completely. For example, to show the plot with a single Y
axis, you can type:
IDL> Plot, time, curve, XStyle=4, YStyle=8
Your output should look similar to the illustration in Figure 6.
Figlcre 6: A plot with the X axis srcppressed aitd Y box axes turned off.
You could show the same plot with Y axes and Y grid lines:
IDL> Plot, time, curve, XStyle=4, YTickLen=l, YGridStyle=l
The [XYZIStyle keyword can be used to set more than one axis property at a time. This
is done by adding the appropriate values together. For example, you see from Table 5
that the value to force the exact axis range is 1, and the value to suppress the drawing
of box axes is 8. To both make an exact X axis range and to suppress box axes, these
values are added together, like this:
IDL> Plot, time, curve, XStyle=8+1, XRange= [ 2 , 5 ]
To create a full grid on a line plot is accomplished (strangely) with the TickLerz
keyword, like this:
IDL> Plot, time, curve, TickLen=l
Outward facing tick marks are created with a negative value to the [XYZlTickLe~z
keywords. For example, to create all outward facing tick marks, type:
IDL> Plot, time, curve, T i c k ~ e n = - 0 . 0 3
To create outward facing tick marks on individual axes, use the [XYZITickLerz
keywords. For example, to have outward facing tick marks on just the X axis, type:
IDL> Plot, time, curve, XTickLen=-0.03
You can also select the number of major and minor tick marks on an axis with the
[XYZITicks and [XYZIMinor keywords. For example to create the plot with only two
major tick intervals, each with 10 minor tick marks, on the X axis, type:
IDL> Plot, time, curve, XTicks=2, XMinor=lO, XStyle=l
Sintple Graplzicnl Displays
Figure 7: An unlimited number of data sets can be plotted on the same line plot.
Figure 8: A line plot with two Y axes. The second axis is positioned with the Axis
command. Be sure to save the data scaling with the Save keyword.
The initial Plot command establishes the data scaling (in the !X.S and ! X S scaling
parameters) for all the subsequent plots. In other words, it is the values in the !X.S and
!Y;S system variable that tells IDL how to take a point in data space and place it on the
display in device coordinate space. Make sure the initial plot has axis ranges sufficient
to encompass all subsequent plots, or the data will be clipped. Use the XRnrzge and
YRarzge keywords in the first Plot command to create a data range large enough. To
distinguish different data sets, you can use different line styles, different colors, differ-
Creatirtg Sztrface Plots
ent plot symbols, etc. The OPlot command accepts many of the same keywords the
Plot command accepts.
IDL> TvLCT, [255, 255, 01, [O, 255, 2551, [O, 0, 01, 1
IDL> Plot, curve, / ~ o ~ a t a
IDL> OPlot, curve, Color=l
IDL> OPlot, curve/2.0, Color=2
IDL> OPlot , curve/5.0, Color=3
Figure 10: A surface plot with meatzirtgful values associated with its axes.
The parameters Ion and lat in the command above are monotonically increasing and
regular. They describe the locations that connect the surface grid lines. But the grid
does not have to be regular. Consider what happens if you make the longitudinal data
points irregularly spaced. For example, you can simulate randomly spaced
longitudinal points by typing this:
IDL> seed = -lL
IDL> newlon = RandomU(seed, 41) * 41
IDL> newlon = newlon[Sort (newlon)] * (24./40) + 24
IDL> Surface, peak, newlon, lat, ~ T i t l e = ' L o n g i t u d e ~$,
YTitle='Latitudel, ~ ~ i t l e = ~ E l e v a t i oCharsize=1.5
n~,
You see now that the longitudinal X values are not regularly spaced. Although it may
look like the data is being re-sampled, it is not. You are simply drawing the surface
Cztstorniziitg Sztrface Plots
grid lines at the locations specified by the longitude and latitude data points. Your
output should be similar to the illustration in Figure l l .
Figzcre 11: The same surface plot, bzrt with alz irregularly spaced X vector.
Figure 12: Surface plots can be rotated with tlze Ax arzd Az key~vords.
are much more useful to them, especially when surface rotations are desired. Partly
this is because direct graphics surface rotations have an artificial limitation that
requires the Z axis to be pointing in a vertical direction in the display window. No
such limitation exits for object graphics surfaces.
To see what some of the advantages are, run the program FSC-Sutface, which is
among the program files you downloaded to use with this book. Press and drag the
cursor in the graphics window to rotate the surface plot. Controls in the menu bar give
you the opportunity to set many properties of the surface plot, including shading
parameters, lights, and colors. The FSC-Sulface program looks similar to the
illustration in Figure 13.
Figz~re13: Tlze FSC-Surface program. Click aizd drag in the grapltics ~viitdowto
rotate tlzis surface plot. Tlze program is written usiizg the object graplzics
class library. Properties of tlze surface caiz be chaizged via optioizs iiz the
meizzc bar.
It is also possible to draw the mesh lines of the surface in different colors representing
a different data parameter. For example, you can think of draping a second data set
over the first, coloring the mesh of the first data set with the information contained in
the second.
To see how this would work, open a data set named Snow Pack and display it as a
surface with these commands. Notice that the Srzow Pack data set is the same size as
the peak data set, a 41-by-41 floating point array.
IDL> snow = LoadData (3)
IDL> Help, snow
IDL> Surface, snow
Now, you will drape the data in the variable srzow over the top of the data in the
variable peak, using the values of srlow to color the mesh of peak. First, load some
colors in the color table with the LoadCT command. The actual shading is done with
the Slzades keyword, like this:
IDL> LoadCT, 5
IDL> Surface, peak, Shades=BytScl(snow, Top=!D.Table-Size-l)
Notice you had to scale the snow data set (with the BytScl command) into either the
number of colors you are using in this IDL session or the size of the color table. If you
failed to scale the data, you might see a set of axes and no surface display at all. This is
because the data must be scaled into the range 0 to 255 to be used as the shading
parameters for the surface.
Si~ttpleGraphical Displays
Sometimes you might like to display the surface colored according to elevation. This
is easily done just by using the data itself as the shading paranleter.
IDL> Surface, peak, Shades=BytScl(peak, Top=!D.Table-Size-l)
Figure 14: The surface plot with a skirt oiz the surface.
You can obtain a kind of "stacked line plot" appearance by drawing only horizontal
lines. For example, type:
IDL> Surface, peak, /~orizontal
If you like, you can display just the lower or just the upper surface and not both (which
is the default) using keywords. For example, type the following two commands:
IDL> Surface, peak, /upper-only
IDL> Surface, peak, /~ower-Only
Sometimes you might want to draw just the surface itself with no axes:
IDL> Surface, peak, XStyle=4, YStyle=4, ZStyle=4
Figure 15: A shaded surface plot of the elevatioiz data usiizg a Gourazcd light sozcrce
shading algoritlzm.
Figure 16: The peak elevation data shaded with tl2e sizow data set.
If you want to shade the surface according to its elevation values, simply byte scale
the data set itself, type this:
IDL> Shade-Surf, peak, Shades=BytScl(peak, Top=!D.Table-Size)
Draping another data set over a surface is a way to add an extra dimension to your
data. For example, by draping a data set on a three-dimensional surface, you are
visually representing four-dimensional information. If you were to animate these two
data sets over time you would be visually representing five-dimensional information.
(To learn about data animation see "Animating Data in IDL" on page 102.)
Sometimes you just want to drape the original surface on top of the shaded surface.
This is easy to do simply by combining the Shade-Suifand Su$ace commands. For
example, type:
IDL> Shade Surf, peak
IDL> surf ace, peak, / ~ o ~ r a s e
Figure 17: A basic corttozcr plot of the data. Notice that tlze X artd Y axis labels rep-
resent the ~zzcmberof elemertts irz tlze data array.
37
Sinzple Gi.aplzical Displays
In earlier versions of IDL, the Corztour command used what was called the cell
drnvvilzg r.tzetlzod of calculating and drawing the contours of the data. In this method
the contour plot was drawn from the bottom of the contour plot to the top. This
method was efficient, but it didn't allow options like contour labeling. The cell
follo~ililzglnetlzod was used to draw each contour line entirely around the contour plot.
This took more time, but allowed more control over the contour line. For example, it
could be interrupted to allow the placement of a contour label. The cell following
method could be selected with the Follow keyword:
IDL> Contour, peak, lon, lat, XStyle=l, ~ S t y l e = l ,/ ~ o l l o w
Starting in IDL 5 the contour command always uses the cell following method to draw
contours, so the Follow keyword is obsolete. But it is still used for its beneficial side-
effect of labeling every other contour line automatically.
Figure 19: A corztourplot with the izzlmber of contour levels set to 12. Note that ev-
ery other contour level is labeled. This is a side-effect of zcsiizg the Follow
keyword.
Unfortunately, although the IDL documentation claims that IDL will select a specified
number of "equally spaced" contour intervals, this may not happen. If you look
closely at the contour plot you just made, you will notice that fewer than 12 levels are
calculated by IDL. Apparently, the NLevels keyword value is used as a "suggestion"
by the contour-selecting algorithm in IDL.
Thus, most IDL programmers choose to calculate the contour levels themselves. For
example, you can specify exactly where the contours should be drawn and pass them
Modifying n Coi~torii.
Plot
to the Contoui-command with the Levels keyword, rather than with the NLevels
keyword, like this:
IDL> vals = [200, 300, 600, 750, 800, 900, 1200, 15001
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, /Follow, $
Levels=vals
To choose 12 equally spaced contour intervals, you can write code something like
this:
IDL> nlevels = 12
IDL> step = (Max(peak) - ~ i n ( p e a k)) / nlevels
IDL> vals = Indgen(nleve1s) * step + Min(peak)
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, /Follow, $
Levels=vals
If you like, you can specify exactly which contour line should be labeled with the
C-Labels keyword. This keyword is a vector with as many elements as there are
contour lines. (If the number of elements does not match the number of contour lines,
the elements do not get re-cycled like they sometimes do with other contour
keywords.) If the value of the element is 1 (or more precisely, if it is positive), the
contour label is drawn; if the value of the element is 0, the contour label is not drawn.
If there is no value for a particular contour line, the line is not labeled. For example, to
label the first, third, sixth and seventh contour line, type:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, /Follow, $
Levels=vals, C-Labels= [l,0, l, 0,O ,1 , 1 , 01
To label all the contour lines, you could use the Replicate command to replicate the
value 1 as many times as you need. For example, type this:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, /Follow, $
Levels=vals, C-Labels=Replicate(l, nlevels)
30
a,
m
.S
C.' 20
(6
-I
10
0 I . . . . . . . . . . . . . . . . . . , . d.'?m,,
b , . . , . .:
0 10 20 30 40
Longitude
Figzcre 20: Corttoui.lines cart be labeled with text yozc provide yourself.
Table 3 for a list of possible line style values.) For example, to make the contour lines
dashed, type:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, $
/ ~ o l l o w ,C_LineStyle=2
If you wanted every third line to be dashed, you could specify a vector of line style
indices with the C-Linestyle keyword. If there are more contour lines than indices, the
indices will be recycled or used over. Type:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, / ~ o l l o w ,$
NLevels=9, C_LineStyle=[0,0,2]
Your output should look similar to the illustration in Figure 21.
Figure 21: Yozc can rnodifi many aspects of a contourplot. Here every third contour
lirte is drawit in a dashed line style.
The thickness of the contour lines can also be changed. For example, to make all
contour lines double thick, type:
IDL> Contour, peak, Ion, lat, XStyle=l, YStyle=l, $
Modifying n Contozcr Plot
You could make every other line thick by specifying a vector of line thicknesses, like
this:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, $
NLevels=12, C-~hick=[1,2], /Follow
You can modify the contour plot so that you can easily see the downhill direction. For
example, type:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, $
/ ~ o l l o w ,NLevels=12, ownhi hill
Your output should be similar to the illustration in Figure 22.
the Tek-Color command to create and load drawing colors for the contour lines, like
this:
IDL> Tek Color
IDL> TVLCT, [70, 2 5 5 1 , [70, 2 5 5 1 , [70, 0 1 , 1
IDL> Contour, peak, lon, lat, XStyle=l, YStyle-l, $
NLevels=lO, Color=2, Background=l, $
C-Colors=IndGen (10)+2, / ~ o l l o w
It is also easy to use the C-Colors keyword to make every third contour line blue,
while the rest are gseen. For example, type:
IDL> Contour, peak, lon, lat, ~ ~ t y l e = lYStyle=l,
, $
NLevels=12, Color=2, ~ackground=l,$
C Colors= [3, 3, 41 , / ~ o l l o w
Figure 23: The coiztourplot showing the "hole" at the lowest corztour level.
The reason for the hole is that IDL fills the space between the first and second contour
lines with the first fill coloc It would seem to make more sense to fill the space
between the 0th (or background) and first contour with the first fill color. But to get
IDL to do that you have to specify your own contour intervals and pass them to the
Contour command with the Levels keyword. This is usually done with code like this:
IDL> step = (Max(peak) - Min(peak) ) / 12.0
IDL> clevels = IndGen (12)*step + Min (peak)
Now you get the contour fill colors correct:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, /Fill, $
Levels=clevels, /Follow, C-Colors=Indgen(l2)+1, $
Background=!P.Color, Color=!P.Background
In general, it is a good idea to always define your own contour levels when you are
working with filled contour plots. Moreover, if you are displaying your filled contour
plots along with a color bar, creating your own contour levels is the only way to make
sure that your contour levels and the color bar levels are identical.
Figure 24: The same contour plot, bzct with the levels calculated aizd specified di-
rectly. The hole is no~v
filled and the correct nzcinber of levels is apparertt
iiz the plot.
Sometimes you will want to fill a contour plot that has missing data or contours that
extend off the edge of the contour plot. These are called opelz colztours. IDL can
sometimes have difficulty with open contours. The best way to fill contours of this
type, is to use the Cell-Fill keyword rather than the Fill keyword. This causes the
Contour command to use a cell filling algorithm, which is not as efficient as the
algorithm used by the Fill keyword, but which gives better results under these
circumstances.
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, $
Levels=clevels, C-Colors=1ndgen(l2)+1, /cell-Fill
Siirtple Grapltical Displays
The Cell-Fill keyword should also be used if you are putting your filled contour plots
on map projections. Otherwise, the contour colors will sometimes be incorrect.
The cell filling algorithm sometinles damages the contour plot axes. You can repair
them by drawing the contour plot over again without the data. Like this:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, $
Levels=clevels, / N o ~ a t a ,/ N o ~ r a s e ,/F'ollow
Sometimes you want to see the contour lines on top of the color-filled contours. This
is easily accomplished in IDL with the Overplot keyword to the Contoul-command.
For example, type:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, $
Levels=clevels, ill, C Colors=IndGen(l2)+1
IDL> Contour, peak, lon, lat, ~ ~ ~ ~ l YStyle=l,
e = l , / ~ o l l o w ,$
Levels=clevels, /Overplot
Your output should look similar to the illustration in Figure 25.
Figztre 25: A contourplot with the contozcr lines overplotted oiz top of the filled con-
tours.
Don't confuse the Overplot keyword with the NoErase keyword. They are similar, but
definitely not the same. In a contour plot, the Overplot keyword draws the contour
lines only, rzot the contour axes. The NoEmse keyword draws the entire contour plot
without first erasing what is already on the display.
Graphic Margin
I I
Figzcre 26: The graphic positiorz is tlze area of tlze display erzclosed by the axes of the
graphic. The graphic region is sirttilar, but also corztairzs space for the
graplzic's titles and other artrzotatiorz. The graphic margin is jzcst tlze op-
posite of the graphic positiorz. The graphic margin is specified iiz char-
acter z~rzits, whereas the graphic position alzd graphic region are
specified irz norinalized coorditzate zcnits.
The entire graphic region may be set by the !P.Regiorz system variable, or regions on
individual axes can be set using the Regioiz field to the !X, !k: and !Z system variables.
The graphic margins may be set by the [XYZIMnrgirz keywords to the Plot, Surfice,
Corztoui; or other IDL graphics command or by the Mnrgi1.1field in the !X, !I: and !Z
system variables.
By default, IDL sets the graphic margin when it tries to put a graphic into the display
window. But, as you will see, that is not always the best choice. It is sometimes better
to use the graphic position to position your graphics displays, especially if you are
combining graphics output commands in one display window.
Sin~pleGraphical Displays
Unlike many other system variables, whose values are set back to the default values
by setting the system variable equal to 0, the margin system variables must be set
explicitly to their default values. If you didn't type the two commands above, be sure
to do it now.
All subsequent graphics output will be positioned similarly. To reset the ! R Position
system variable so that subsequent graphic output fills the window normally, type:
If you wanted to position just one graphic display, you could specify a graphic
position with the Position keyword to the graphics command. Suppose you wanted a
contour plot to just fill up the left-hand side of a display window. You might type this:
IDL> Contour, peak, position= [O.l, 0.1, 0.5, 0.91
Note that the Positiolz keyword can be used to put multiple graphics plots into the
same display window. Just be sure to use the NoEl-ase keyword on the second and all
subsequent graphics commands. This will prevent the display from being erased first,
which is the default behavior for all graphics output commands except TV and TVScl.
For example, to put a line plot above a contour plot, you can type:
IDL> Plot, time, curve, ~osition=[O.l,0.55, 0.95, 0.951
IDL> Contour, peak, ~ o s i t i o n = [ ~ . l0.1,
, 0.95, 0.451, / ~ o ~ r a s e
!P.Multi[cl] This element specifies whether the graphics plots are going
be displayed by filling up the rows first (!P.Mzrlti[4]=0) or by
filling up the columns first (!PMulti[4]=1).
For example, suppose you want to set !Z?M~lltito display four graphics plots on the
display in two columns and two rows, and you would like the graphics plots to fill up
the columns first. You would type:
Now as you display each of the four graphics plots, each graphic fits into about a
quarter of the display window. Type:
IDL> Window, XSize=500, YSize=500
IDL> P l o t , t i m e , c u r v e , LineStyle=O
IDL> Contour, peak, l o n , l a t , X S t y l e = l , Y S t y l e = l , NLevels=lO
IDL> S u r f a c e , peak, l o n , l a t
IDL> Shade-Surf, peak, l o n , l a t
Your output should look similar to the output in Figure 27.
Figzcre 27: You caiz plot multiple graphics plots iiz a siitgle display wiitdow.
room on the display for titles and other types of annotation. You can leave room with
multiple plots by using the "outside margin" fields of the !X, !t: and !Z system
variables. The outside margins only apply when the !l?Mzrlti system variable is being
used. They are calculated in character units, just as the normal graphic mara'
ulns are
calculated.
For example, suppose you wanted to have an overall title for the four-graphic plot
display you just created. You might leave room for the title and create it like this:
IDL> !P.Multi = 10, 2, 2, 0, l]
IDL> ! Y.OMargin = [2, 41
IDL> Plot, time, curve, LineStyle=O
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, NLevels=lO
IDL> Surface, peak, lon, lat
IDL> Shade-Surf , peak, lon, lat
IDL> XYOutS, 0.5, 0.9, /Normal, 'Four Graphics Plots1, $
Alignment=0.5, Charsize=2.5
Your output should look like the illustration in Figure 28.
Figure 28: A four by four multiplot with space left at the top of the plot for a title by
using the !Y.OMargiiz keyword.
Siirzple Grapltical Displays
Figzcre 29: You can use !P.Multi to position asymmetric arraitgements of plots irt
the display window.
Note that the TV command does not work with !Z?Multi like the Plot or Contour com-
mands do. However, the TVInzage program, which is a replacement for the TV com-
Addiizg Text to G1*aplticalDisplays
mand and is among the programs you downloaded to use with this book, does honor
the !P.Mzrlti system variable. Try these commands:
IDL> image = LoadData ( 7 )
IDL> !P.Multi=[O, 2 , 21
U)L> FOR j = 0 , 3 DO TVImage , image
Be sure you reset !l?Mzrlti to display a single graphics plot on the page. Like many
system variables, !P.Mzrlti can be reset to its default values by setting it equal to zero,
like this:
Table 6: Tlzefont "Jlavor" cart be selected by setting the !R Foitt system variable
or the Foizt keyword to the appropriate value. Vectorfonts are the
defaultfont type for direct graphics commaitds. They have the
advaittage of being platform irtdeperzde~tt.
By default fonts in direct graphics routines are set to the vector or software font style.
Vector fonts are described by vector coordinates. As a result, they are platform
independent and can be rotated in 3D space easily. However, many people find vector
fonts too "thin" for quality hardcopy output and prefer more substantial fonts for this
purpose (i.e., true-type fonts or Postscript hardware printer fonts). Vector fonts can be
selected permanently by setting the !P.Forzt system variable to -1 or by setting the
Forzt keyword on a graphical output command, like this:
IDL> Plot, time, curve, Font=-l, XTitle=lTime', $
YTitle='Signall, Title=IExperiment 35F3a1
True-type fonts are also called outline fonts. The font is described by a set of outlines
that are then filled by creating a set of polygons. IDL comes with four true-type font
families: Times, Helvetica, Courier, and Symbol, but you can supplement these with
any other true-type font family you have available. True-type fonts take longer to
render because the font must be scaled and the polygons created and filled, and many
people find them a bit unattractive at small point sizes on normal low-resolution
displays. But they have the great advantage of being rotatable and nice looking on
hardcopy output devices. True-type fonts are the default font for the object graphics
system in IDL.
To render a plot with the default me-type Helvetica font face, set the Font keyword to
1, like this:
Siinple Graplzical Displays
For example, you can put the title on the line plot using rzounnlized coordinates, like
this. When you are writing IDL programs it is often a good idea to specify things like
titles and other annotation with normalized coordinates. This makes it easy to place
graphics not only in the display window, but in Postscript and other hardcopy output
files as well.
IDL> Plot, time, curve, Position= [0.15, 0.15, 0.95, 0.851
IDL> XYOutS, 0.2, 0.92, 'Results: Experiment 3 5 F 3 a 1 , $
Size=2.0, /Normal
revert back to the default Simplex Roman is write another string with explicit Simplex
Roman characters. For example:
IDL> XYOutS, 0.5, 0.5, !3Junk1,/Normal, Charsize=-l
Notice the use of the ClznrSize keyword in the code above. When this keyword has a
value of - 1, the text string is suppressed and not written to the display.
Figure 30: Be careful when yozc select a Hershey font, oryou might end zcg with plot
titles that look like Greek to yozcr users.
Aligning Text
You can position text with respect to the location specified in the call to XYOutS with
the Aligrzrnei~tkeyword. A value of 0 will left justify the string (this is the default
case); a value of l will right justify the string; and a value of 0.5 will center the string
with respect to the location specified by the X and Y values. For example:
IDL> Window, XSize=300, YSize=250
IDL> XYOutS, 150, 55, 'Research1,Alignment=O.O, $
/Device, CharSize=2.0
IDL> XYOutS, 150, 110, 'Research', Alignment=0.5, $
/Device, CharSize=2.0
IDL> XYOutS, 150, 170, 'Research1,Alignment=l.O, $
/Device, CharSize=2.0
IDL> Plots, [0.5, 0.51, [1.0, 0.01, / ~ o r m a l
Erasing Text
Text that is written with XYOutS can sometimes be "erased" by writing the same text
over in the background color. The Color keyword in conjunction with the
!P.Bnckgrouizd system variable is used for this purpose. Note that this only works
perfectly if the text was written on nothing but the background! There are often other,
better methods to erase annotations. (See "Erasing Annotation From the Display" on
page 114, for example.) To see how to erase annotation with the background color,
type this:
IDL> Window, XSize=300, YSize=250
IDL> XYOutS, 150, 110, 'Researchv,Alignment=0.50, $
/Device, CharSize=2.0
IDL> XYOutS, 150, 110, 'Research1,Alignment=0.50, $
/Device, CharSize=2.0, Color=!P.Background
Adding Text to Grapltical Displays
Orienting Text
The text specified with the XYOutS command can be oriented with respect to the
horizontal with the Orieiztation keyword, which specifies the number of degrees the
text baseline is rotated away from the horizontal baseline. For example, type:
IDL> Window, XSize=300, YSize=250
IDL> XYOutS, 150, 110, 'Research', Alignment=0.50, $
/ ~ e v i c e ,CharSize=2.0, Orientation=45
IDL> XYOutS, 150, 180, 'Research1,Alignment=0.50, $
/ ~ e v i c e ,CharSize=2.0, Orientation=-45
Positioning Text
IDL also provides a number of ways to position and manipulate text in graphical
displays. For example, it is possible to create subscripts and superscripts in a plot title.
Text positioning is accomplished by means of embedded positioning commands, as
shown in Table 8, below.
Figure 31: Tlzisplot is annotated witlz a dashed line drawiz across its center witlz the
PlotS commaitd.
The PlotS procedure can also be used to place marker symbols at various locations.
For example, here is a way to label every fifth point in the curve with a diamond
symbol.
IDL> TvLCT, [70, 255, 01, [70, 255, 2501, [70, 0, 01, 1
IDL> Plot, time, curve, Background=l, Color=2
IDL> index = IndGen ( 20) * 5
IDL> Plots,time [index], curve [index], PSym=4,$
Color=3, SymSize=2
The PlotS command can also be used to draw a box around important information in a
plot. By combining the PlotS command with other graphics commands, such as
XYOutS, you can effectively annotate your graphic displays. For example, like this:
IDL> TvLCT, [70, 255, 01, [70, 255, 2551, [70, 0, 01, 1
IDL> Device, Decomposed=O
IDL> Plot, time, curve, Background=l, Color=2
IDL> box-X-coords = [O.4, 0.4, 0.6, 0.6, 0.4l
IDL> box-y-coords = [0.4, 0.6, 0.6, 0.4, 0.41
IDL> Plots, box-X-coords, box-y-coords, Color=3, /Normal
IDL> XYOutS, 0.5, 0.3, 'Critical Zone1,Color=3, Size=2, $
Alignment = 0.5, /Normal
Adding Color to Your Gi.aphical Displays
You can easily use the XYOzitS and PlotS commands to create legends for your graph-
ics displays.
Open a window and plot the XY locations, so you can see how the data is distributed
in a random pattern. Type:
IDL> Window, XSize=400, YSize=350
IDL> Plot, X, y, Psym=4, Position=[0.15, 0.15, 0.75, 0.951, $
XTitle='X ~ o c a t i o n s ' ,YTitle='Y Locations'
You are going to display the Z data associated with each XY point pair as a circle of a
different color. To do this, you will need to load a color table and scale the Z data into
the range of colors you have available to you. Type:
Siitlple Graplzical Displays
IDL> LoadCT, 2
IDL> zcolors = BytScl(z, Top=!D.Table-Size-l)
The Circle progranl you are using in this example has a couple of weaknesses. Its
major deficiency is that it doesn't always produce circles! If you specify the
coordinates of the circle in the data coordinate system, the circle may show up on the
display as an ellipse, depending upon the aspect ratio of the plot and other factors. (To
obtain an excellent circle program, download the program TVCii-clefrom the NASA
Goddard Astrophysics IDL Library. You can find the library with a web browser at
this World Wide Web address: http://idlastro.gsfc.i~asa.go~~fionzepage.ht~~zl.)
To avoid this deficiency in the Circle program, convert the data coordinates to device
coordinates with the Coizvert-Coord command. Type:
IDL> coords = Convert-Coord(x, y, /Data, /To-~evice)
IDL> X = coords(O,*)
IDL> y = coords(l,*)
Now, you are finally ready to use the Polyfll command to draw the colored circles
that represent the data Z values. Type:
IDL> For j=O, 29 DO Polyfill, Circle(x(j), y(j), 10), $
/Fill, Color=zcolors(j), / ~ e v i c e
As an added touch, it would be nice to have a color bar that can tell you something
about the Z values and what the color means. You can add a color bar to the plot with
the Colorbar program that came with this book. Type:
IDL> Colorbar, position = [0.85, 0.15, 0.90, 0.951, $
Range= [Min(z),Max (z)1 , /vertical, $
Format=I(I5)l, Title='Z Values1
Your output should look similar to the illustration in Figure 32.
3186
V)
-a3,
(d 1595
>
N
5
0.0 0.2 0.4 0.6 0.8 1.O
X Locations
Figure 32: The color of the circles represents a tlzird dimensiorz irt tlzis 2Dplot.
Working with mage Data
Chapter Overview
IDL got its start as a language for handling and processing images. It is no surprise
that many scientists and engineers the world over still use it for that reason. This
chapter lays the groundwork for working with images. Among the topics you will
learn about are these:
* How to read and display image data
* The difference between 8-bit and 24-bit images
* How to scale image data
* How to position an image in the display window
* How to change image sizes
* How to read images from the display device
* How to perform basic image processing tasks
* How to construct simple image filters
Displaying Images
You can use either of two IDL commands to display your image: TV or TVScl. These
commands are identical in almost every way, including the keywords that can be used
with them. They differ in only one respect: TVScl scales the image data into byte
values that correspond to the number of colors you are using in your IDL session. (On
24-bit displays, the image data is scaled into the range 0 to 255.) For example, if you
are using 220 colors in your IDL session, TVScl scales the image data into byte values
between 0 and 219 before the image is displayed.
The TV command, on the other hand, takes the image data at face value and simply
transfers it to the display as byte data. Image data values are truncated to byte values if
necessary. If the image data is not scaled into the range of 0 to 255, there is an
excellent chance the image will be displayed incorrectly.
Notice that unlike the Plot, Suiface, and Colztour commands, the TV and TVScl com-
mands do not erase the display before displaying the image. Most of the time this is
not a problem, but sometimes it is. If you want a clear display window to view your
image data, you can erase whatever is currently displayed in the window with a simple
Erase command, like this:
IDL> Erase
Here is an example to illustrate the point. The image data set you just read into IDL
has already been scaled into the range of 0 to 255. You can see this is so by typing:
IDL> Print, Max (image), Min (image)
But, if you are working on an 8-bit display, chances are very good that you are not
using all 256 colors that are available on your display device. To see how many colors
you are using, type:
IDL> Print, !D.Table-Size
The number of colors used in an IDL session on an 8-bit display (here represented by
the size of the color table) is usually in the range of 210-240 colors, but it can be
considerably less. On a 24-bit display, you will have access to 16.7 million colors, but
your color table size will still be just 256. You will learn later how IDL selects the
number of colors it uses.
If you are sunning IDL on an 8-bit display, open a window, load the gray-scale color
table, and display the image with the TV command, like this:
IDL> Window, 0, XSize=192, YSize=l92
IDL> LoadCT, 0
IDL> TV, image
Your output should look like the illustration in Figure 33.
Since you used the TV command, the data was transferred to the display without any
scaling. Although it is not apparent yet, all the pixels with image values greater than
the number of colors in the IDL session are set to the same color value. That is, pixels
with values greater than !D. Table-Size-l are being displayed with the same color
pixel. (In this case, you are seeing "colors" as shades of gray.)
You might be able to see the difference if you display the image with the TVScl
command. (You won't see any difference at all on a 24-bit display, since there you
have all 256 possible byte values available to you.) Open another window and move it
near the first. Use the TVScl command to display the image, like this:
IDL> Window, 1, ~ ~ i z e = l 9 2YSize=192
,
IDL> TVScl, image
Figure 33: Art image of David Sterrt, fozcrtder of Research Systems artd creator of
IDL. The otlter image irt the people.dat data set is Ali Bahrami, the first
employee of Research Systems. Both David arzd Ali are still irtvolved irt
the developmertt of IDL.
You may be able to see a difference in shading between the two images. Since this
image data has a maximum value of only 238, the difference might be subtle. If you
can't see the difference, try scaling the data between 0 and 255 first, like this:
IDL> WSet, 0
IDL> image = BytScl (image)
IDL> TV, image
IDL> WSet, 1
IDL> TVScl, image
If you still can't see the difference, try loading a color table. The Red Temperature
color table might work. Type:
IDL> LoadCT, 3
Now, to see what TVScl does, scale the data and display it with the TV command:
IDL> Window, 2, XSize=192, YSize=192
IDL> scaled = BytScl(image, Top=!D.Table-Size-l)
IDL> TV, scaled
What you see in window 2 should be identical to what you see in window I. This is
what we mean when we say TVScl byte scales the data into the number of colors used
in the IDL session.
Note that if the image in the display window is not displayed with a red-temperature
color scale you may be running IDL on a 16- or 24-bit color display. If this is the case,
be sure to turn color decomposition off for these exercises. (You will learn more about
color decomposition in "Working with Colors in IDL" on page 8 1.) Type these com-
mands:
IDL> Device, Decomposed=O
IDL> TV, scaled
If you are on a 16-bit or 24-bit display, you will need to re-issue each graphics
command from now on after changing the color table in order to see the new colors
take effect. On a 16-bit or 24-bit display, the colors in the color table are not directly
indexed or linked to the colors on the display. Rather, the color table is used as a way
for the image to find the color it should use for the pixel. But that pixel color is
expressed directly.
Working witlt Iinage Data
In general, if you don't know whether your data is scaled or not, you probably want to
use the TVScl command, since this will give your image the maximum possible
contrast in pixel values. But if color is important to you (and it almost always is), then
you probably never want to use the TVScl command. Instead, you will want to scale
your image data yourself, and use the TV command to display it.
You do this by applying the keywords Top, Min, and M m to the BytScl command. For
example, suppose you always want to display your data in 100 different shades of gray
or colors. And suppose that the minimum data value you expect in any data set is 15,
whereas the maximum valid data value is 245. Then the BytScl command is used like
this:
In this example, any value in the data set that has a value less than 15 will be set to the
value of 15 before the data is scaled. Similarly, any value in the data set with a value
greater than 245 will be set to 245 before the data is scaled.
Once your data is scaled, it is displayed with the TV command, like this:
IDL> TV, scaledImage
Now, if you always scale your data sets in the same way (and you always have at least
100 shades of gray or colors in your IDL session), your data set from last week can be
compared directly with this week's data set. A particular color red will always indicate
a specific data range or pressure.
At this point, you may have a number of graphics windows on the display. Here is a
trick to get rid of all the open windows in a single command. Type:
IDL> WHILE !D.Window N E -1 DO WDelete, !D.Window
To display the image data iinage in the same window in what appears to be two
different color tables, you must scale the image data into these two portions of the
color space. First, use the BytScl command to scale the image data into the first
portion of the color table. Make a new image, iinagel, like this:
W L > image1 = BytScl (image, Top=half - l)
Working with Iitzage Data
Now, make a second image, inzage2, by scaling the image data into the second portion
of the color table, like this:
IDL> image2 = BytScl (image, Top=half -l) + Byte (half)
You want the image on the left to be displayed with a gray-scale color table (table
number 0). To do so, you must load those gray-scale colors in the portion of the color
table occupied by the first image's data values. Type:
IDL> LoadCT, 0, NColors=half, Bottom=O
You can interactively choose any color table you like for the image on the right if you
use the XLoadCT command to load those colors in just the second portion of the color
table. Like this:
IDL> XLoadCT, NColors=half, Bottom=half
Finally, put the two scaled images side-by-side into the same window, like this. Notice
that you are using the TV command. Do you understand why?
IDL> Window, XSize=192*2, YSize=192
IDL> Device, Decomposed=O
IDL> TV, image1
IDL> TV, image2, 192, 0
To restore a single normal color table before continuing with the examples in this
chaptel; type:
IDL> LoadCT, 0
The output should look similar to that in Figure 34 and in color if you are on a 24-bit
display. It should appear in gray-scale on an 8-bit display. If that isn't what you
expected, please read on.
Figure 34: The rose data set as it is srcpposed to look. If your output doesn't look like
a bearctificl rose, try turning decomposed color orz and display it agairt.
If your output doesn't look like a beautiful rose, the problem may be that you are
running IDL on a PC or Macintosh computer, with 24-bit color turned on, and with
color decomposition turned off. (UNIX computers will display a 24-bit image in the
correct colors, no matter what the color decomposition setting, but this is not true for
other computers.) Set the color decomposition keyword and re-display the image:
IDL> Device, Decomposed=l
IDL> TV, rose, True=l
Unfortunately, 24-bit images always appear in gray-scale on 8-bit display devices. To
see the image in its actual colors on such devices, you have to create a 2D image and
the red, green, and blue color tables to go with the image from the 24-bit or 3D image
data set. This is done in IDL with the Color-Quan command. If you are on an 8-bit
display device, type these commands:
IDL> image2d = Color-Quan (rose, 1, r, g, b)
IDL> TVLCT, r, g , b
IDL> TV, image2d
You will now see the image in color, although the color reproduction is never as good
as the 24-bit image displayed on a 24-bit device.
on. This is done automatically on most workstations which are in 24-bit color, but is
not done automatically in Windows or Macintosh 24-bit color. To be absolutely sure
of seeing the 24-bit image in the correct image colors, you should type these
commands on a 24-bit display:
IDL> Device, Decomposed=l
IDL> TV, rose, True=l
Note that the TVItnnge program you downloaded to use with this book auton~atically
sets the correct color decomposition mode and the correct True keyword, depending
upon whether it is a 24-bit or 8-bit image you are displaying. It will also automatically
apply the Colo~Quaizfunction if a 24-bit image is to be displayed on an 8-bit device.
This is true even when the 8-bit device is the Postscript device.
IDL> TVImage, rose
Save the file as disp1ny.pl.o and compile the program code like this:
IDL> .Compile display
If the program has errors and doesn't compile for some reason, fix the errors and
recompile.
Now, to see the image immediately update when the color table is changed, type this:
IDL> XColors, N ~ t i f y P r o = ~ D i s p l a ,y 'Image=world
Close the currently open XColovs program.
You can use the WID keyword to select another window for the display. For example,
you can type this:
IDL> Window, 5
IDL> image5 = LoadData (5)
IDL> TV, image5
IDL> XColors, N ~ t i f y P r o = ~ D i s p l a yTitle=IWindow
~, 5 ColorsT, $
Image=image5, WID=5
IDL> Window, 4
IDL> image4 = LoadData (4)
IDL> TV, image4
IDL> XColors, N o t i f y P r ~ = ~ D i s p l a yTitle='Window
~, 4 ColorsT, $
Image=image4, WID=4
Notice that your two XColovs programs can exist on the display at the same time and
that they display into the proper window. This is not possible with the RSI-supplied
XLoadCT, which must limit itself to a single copy in order to protect its color tables
with are in a common block. (You can have as many XCo1ol.s programs as you like, as
long as each has a different title.)
The equivalent capability for XLoadCT is located in the keywords UpDateCallback
and UpdateCbDatn. The display program must be written so that it can accept one,
and only one, keyword parameter, which must be named Data. For example, you
could write the display procedure like this:
PRO XDisplay, ~ a t a = i m a g e
TV, image
END
Then call the XLoadCT program like this:
IDL> Window, 0
IDL> image = LoadData (7)
IDL> TV, image
IDL> XLoadCT, UpDateCallback='XDisplayl, UpdateCbData=image
If you wanted to pass the window index number into the XDisplay program, you
would have to make the Data variable into a structure. For example, like this:
PRO XDisplay, Data=struct
WSet, struct.wid
TV, struct.image
END
But this would mean you would always have to pass a window index number:
$
IDL> XLoadCT, Up~ateCallback=~XDisplay',
~ p d a t e ~ b ~ a t a = { i m a g e : i m a gwid:!D.~indow)
e,
To my mind, this makes the XLoadCT method a lot less flexible than the XColovs
method for passing information back and forth. I find XLoadCT even less flexible in
widget programs.
Workiirg with Intage Data
Figure 35: Images resized with the Rebiiz commarzd must be integer multiples of the
origirzal image size.
By default, Rebirz uses bilinear interpolation when magnifying an image, and nearest
neighbor averaging when reducing an image. Nearest neighbor sampling can be used
in both directions if the Snrnple keyword is set. Bilinear interpolation is more accurate,
but requires more computer time.
Working with brtnges
The positions start at the top-left of the display and continue to the bottom-right. For
example, in a 384 by 384 window, there are four positions for a 192 by 192 image,
starting in the upper-left corner of the window. Try typing the following commands:
IDL> Window, XSize=384, YSize=384
IDL> TVScl, image, 0
IDL> TVScl, image, 1
IDL> TVScl, image, 2
IDL> TVScl, image, 3
It is also possible to position an image in a window by specifying the pixel location of
the lower-left hand corner of the image explicitly. This is done by specifying two
additional parameters to the TV or TvScl commands after the name of the image data.
For example, to locate the 192 by 192 image named image in the middle of the
window you just created, type:
IDL> Erase, Color=!D.Table-Size-l
IDL> TVScl, image, 96, 96
In this case, you placed the lower-left corner of the image at pixel location (96,96).
Positioning an image in this way is important when you want to leave room for such
additional graphic elements as color bars or other annotations.
For example, type these commands to display a color bar on the left-hand side and the
image on the right-hand side of a window. Your display window should look similar to
the illustration in Figure 36.
Figure 36: The image with a color bar next to it showing the color gradations.
Figzcre 37: Usirzg the TVZmage commarzd rzot only allows you to positiorz images iiz
a device-iizdeperzdent way, it also makes it easy to zrse other graphics
comrnarzds.
An Alternative to TVRD
A color decomposition independent alternative to TVRD, named TVREAD, is
available among the programs you downloaded to use with this book. It can be used in
exactly the same way TVRD is used, but you don't have to be aware of the state of the
Working lvitlz Zrnage Data
Decolnposecl keyword to the Device command. Nor do you have to worry about
setting the True keyword in TVREAD, as you do in TVRD. The return image will be an
8-bit image if the screen dump is done on an 8-bit display, and a 24-bit image if the
screen dump is done on a 24-bit display.
Another advantage of TVREAD is that it can automatically send the screen dump to a
BMP, GIF, JPEG, PICT, PNG, or TIFF file. You don't have to wol-sy about the depth of
your display device, or the state of color decomposition. For example, to display two
images one above the other in a window and create a JPEG file of result, type these
commands:
IDL> LoadCT, 4
IDL> Window, XSize=350, YSize=500
IDL> !P.Multi = [O, 1, 21
IDL> TVImage, LoadData ( 5 )
IDL> TVImage , LoadData (7)
IDL> image = TVREAD (/JPEG)
IDL> !P.Multi = 0
Histogram Equalization
If you look at the distlibution of pixel values in an image, you will often find that the
distribution tends to cluster in a narrow range of values. In effect, the image has a very
narrow dynamic color range. If you spread the pixel distribution out, so that each
subrange of values had approximately the same number of pixels with those values,
the infornation content of the image can sometimes be improved. This process of
spreading the pixel distribution over the entire dynamic color range is known as
histogram equalization.
For example, open the data set CT Stall Thoracic Cavity with the LoadData
command. This image is a CT scan with a narrow dynamic color range.
IDL> scan = LoadData (5)
To see a histogram plot of the pixel value distribution of the variable scmz, type these
commands. Your graphics window will look similar to the illustration in Figure 38.
Figure 38: A normal image with a narro~vpixel distribution. Here most of the pixels
have values behveen 50 and 100.
Basic Zi~tageProcessirtg in ZDL
IDL> LoadCT, 0
IDL> Window, 0, ~ ~ i z e = 6 0 0YSize=250
,
IDL> TV, scan
IDL> Plot, ~istogram(scan),/ ~ o E r a s e ,Max_Value=5000, $
position=[0.5, 0.15, 0.95, 0.951
You see that most of the pixels have values that fall between 50 and 100. To spread the
pixel distribution out over the whole color range, so that there are approximately equal
number of pixels with each possible color value, use the Hist-Equal command, like
this:
IDL> equalized = Hist-Equal (scan)
To see the new pixel distribution histogram and the histogram-equalized image, type:
IDL> Window, 1, XSize=600, YSize=250
IDL> TV, equalized
IDL> Plot, ~istogram(equa1ized) , Max-Value=5000, $
Position= [O.5, 0.15, 0.95, 0.951 , / ~ o E r a s e
The histogram equalized image should look similar to the illustration in Figure 39.
Figure 39: A histogram-equalized image. The pixel distributiorz has been spread out
over the entire color dyitamic range.
Smoothing Images
Images can be smoothed by averaging each pixel value with the values of its
surrounding neighbors. This is known as mean or boxcar smoothing. Mean smoothing
is accomplished in IDL with the Sn1ootl.1function, which performs an equally
weighted smoothing using a square neighborhood of a given odd width. For example,
if the neighborhood is 3-by-3, then each pixel is replaced by the mean value of it and
its eight surrounding pixels.
To see an unsmoothed image and the image smoothed with a 5-by-5 boxcar average,
type:
IDL> Window, 0, XSize=192*3, YSize=192
IDL> TV, image, 0, 0
IDL> smoothed = Smooth (image, 5, /Edge-Truncate)
IDL> TV, smoothed, 192, 0
Notice the Edge-Truncate keyword used with the Smooth command. This keyword
replicates pixels near the edge of the image so that a smoothing occurs over the entire
image. If the keyword is not used, pixels near the edge of the image are simply
replicated and not smoothed.
Working with Ii~tngeData
Your display window should look similar to the illustration in Figure 40.
Figure 40: The origirtal irnage or2 the left, the srnoothed irnage irz the middle, arzd the
urzsharp masked intage orz the right.
Using the Snzootlz command, you are giving equal weight to the neighboring pixels to
determine the mean value. This sometimes results in unwanted blurring of the image.
Another approach is to use a process called com~olutioizto do the smoothing. In this
technique, a square kernel is convolved with the image. For example, the Sr~zootlz
command, in its 3-by-3 case, uses a kernel like this:
1 1 1
1 1 1
1 1 1
You will get less blurring in your image if you give more weight to the central pixel
and less to its neighbors. For example, you might want to create a kernel like this:
To convolve the image with this kernel, use the Convol command, like this:
IDL> kernel = [[1,2,11, [2,8,2], [1,2,1]1
IDL> TV, image, 0, 0
IDL> TV, Smooth(image, 3, /~dge-Truncate), 192, 0
IDL> TV, Convol (image, kernel, Total (kernel), $
/Edge-Truncate), 2*192, 0
You can, of course, create kernels of any size. Here is a typical Gaussian distribution
5-by-5 kernel:
Figure 41: The original irnage on the left, the noisy image in the center, and the
noisy image smoothed with a mediarz filter on the right.
Figure 42: Three different ways to ertharzce the edges of images. Oiz the left, the So-
be2 method. The Roberts method is shown iiz the certtec Coizvolvirtg tlze
image with a Laplaciarz kernel is sho~vitoiz tlze right.
In general, low frequency terms represent the general shape of the image and high
frequency terms add fine detail to the image. Viewing the frequency domain image is
not usually instructive, but it is sometimes useful to observe the power spectlzolz of the
frequency domain image.
The power spectrum is a plot of the magnitude of the various components of the
frequency domain image. Different frequencies are represented at different distances
from the origin (usually represented as the center of the image) and different
directions from the origin represent different orientations of features in the original
image. The power at each location shows how much of that frequency and orientation
is present in the image. The power spectrum is particularly useful for isolating
periodic structure or noise in the image. The power spectrum magnitude is usually
represented on a log scale, since power can vary dramatically from one frequency to
the next.
To calculate and display the power spectrum of this convection image next to the
original image, type:
IDL> power = ~hift(~log(Abs(freq~ornainImage)),
124, 124)
IDL> TV, power, 248, 248
The symmetry in the power spectrum indicates that this image contains a great deal of
periodic structure at increasing frequencies. (Your output should look similar to the
illustration in Figure 43. The purpose of this exercise is to filter out the higher
frequencies in the image.
The next step is to apply a frequency filter to the transformed image. A Butterworth
low-pass filter is used to filter out the high frequency components of the image. These
Workiiig with Zntnge Datn
high frequencies give detail to the image, so the end result will be to perform an image
smoothing operation. Construct the low-pass frequency filter by typing this:
IDL> filter = 1.0 / (1.OD + ( ~ i s (248)/15.0)
t "2)
Notice that the cutoff frequency width is 15 pixels. This will be sufficient to eliminate
about half the higher frequencies you see in the power spectrum in Figure 43.
Figure 43: Art illustration of frequeitcy domain filtering. The zcrzfiltered image next
to its power spectrum iiz the top half of thefigure. Thefiltered image next
to its power spectrum irt the bottom half of the figure. About half the
high-frequency cornportents have been removed from the filtered image.
To apply the frequency filter, transform the image from the frequency domain back
into the spacial domain, and finally display the filtered image, type:
IDL> filtered = ~ ~ ~ ( f r e q D o m a i n 1 r n a*g e
filter, 1)
IDL> TV, filtered, 0, 0
To prove you did in fact filter out the higher frequency components of the image,
display the power spectrum of the filtered image next to the filtered image. Type:
IDL> filteredFreqImg = FFT(filtered, -1)
IDL> power = Shift (Alog (Abs (filteredFreqImg)) , 124, 124)
IDL> TV, power, 248, 0
Your output will look similar to the illustration in Figure 43.
Graphical Display Techniques
Chapter Overview
After you have learned how to display a line, surface, and contour plot, you are on
your way to displaying data in imaginative and innovative ways. This chapter
describes a number of specific visualization techniques that will enhance your data
displays. No attempt is made to describe every possible technique in IDL. Rather, this
chapter introduces a few of the more common ones. The purpose of this chapter is to
give you tools and ideas for creating your own unique data displays.
Specifically, you will learn:
How IDL works with colors
* How to ask for color in a device independent way
How to create and save color tables in IDL
* How to modify axis annotation to your specifications
* How to set up a 3D coordinate system in IDL direct graphics
How to combine graphical displays
How to work with bad or missing data in IDL
How to animate graphical displays
How to grid XYZ data for graphical display
How to provide cursor interaction with your graphical display
* How to erase annotation from your graphical display
How to draw a "rubberband" box on your graphical display
How to use the Z graphics buffer for graphical display tricks
m-
Pixel Value
used as ltlrlex
-
Color Table Color Palette
Q56 colors)
Figure 44: The Indexed Color Model for 8-bit pixel values. The pixel valzle is used
as art index into the color table. The values found in the red, green, and
blue columns of the color table determine the specific color triple asso-
ciated with or indexed to that pixel value.
turned off. Otherwise, they specify colors directly as a color triple.) But the Indexed
Color Model also ties or links the indexed color to a specific location in the color
table, whereas the RGB Colos Model specifies the color directly. Colors that are
linked to a particular color table location are called dy~zanziccolor displays. While
colors that are displayed directly are often called static color displays. For the most
part (there are exceptions), 8-bit displays are dynamic displays and 24-bit displays are
static displays.
The most important difference between a dynamic and static color display is that if
you have a dynamic display and you change the numbers loaded at a particular
location in the color table, pixels that are indexed to that location change colors
immediately. Whereas with a static display, pixel colors are specified directly and
more or less peimanently. They are unaffected by subsequent changes in the color
table values. (This is not always m e . See the discussion below concerning the
DirectColor visual class.)
While this may seem strange, it actually has great value. What it means is that systems
that use the RGB Color Model can display all 16.7 million colors simultaneously,
whereas systems that use the Indexed Color Model can only display 256 simultaneous
colors out of the palette of 16.7 million colors.
You can tell what type of color model you are using by using new keywords to the
Device command that were first introduced in IDL 5.1. These keywords are
Get-Visual-Depth and Get-Visual-Nanze. These are both output keywords which
return a value to a named IDL variable, like this:
IDL> ~ e v i c e , et-visual-Name=thisName, $
Get-Visual-Depth=thisDepth
IDL> Print, thisName, thisDepth
Truecolor 24
this well. The most common problem is that Directcolor visuals often give you
private color maps, which require you to make the graphics window the current
window to load the correct colors in the window. When this is done, other windows
can disappear. This is known as the "color flashing problem" and is a result of the way
the X Window manager handles color tables. Recent advances in both hardware and
software has eliminated many of these problems, but they are still encountered
frequently. As a rule, I like to use either 8-bit Pseudocolor or 24-bit Truecolor
visuals, since I can count on these working properly across many platforms.
The visual class used in an IDL session is normally assigned by default when IDL
starts up, either from information in the .XDefnults file if IDL is running on a UNIX
machine or from IDL's normal rules for assigning the visual class. (The rules require
that IDL inquire of the hardware what visual classes are supported and assigns the
"highest' visual class and depth available.) But this default assignment can be
overruled by specifying the visual class and depth from within IDL. (PC and
Macintosh versions of IDL make their assignment from the graphics card installed on
the machine and its current configuration. This cannot be changed from within IDL.)
This assignment must be made before any graphics windows have been opened and
will apply for the rest of the IDL session.
Here are typical visual class assignment statements on a UNIX machine:
IDL> Device, Pseudo_Color=8
IDL> Device, True_Color=24
eight bits as the blue index. For example, on a 24-bit display with color decomposition
on, IDL tlies to decompose the number 180 in the command above into red, green, and
blue indices. Since the number 180 sets only bits in the red portion of a decomposed
number, this plot will be displayed with a red color, no matter what color is actually
loaded at that index in the color table. You see this idea of a decomposed index
illustrated in Figure 45.
128 128 0l 2 8
Pixel Value
Decomposed into
Three Indices 253 253 253 253
254 254
255 255
254
255
@
255
Color Table Color Palette
(16 million colors)
Figure 45: The RGB Color Model uses a 24-bitpixel v a h e to iltdepettdently speciJL
the RGB compoizents of a color. All 16.7 millioiz colors are available si-
multarzeously in the color palette if the color vectors coiztaiiz valzcesfront
0 to 255.
When a gray-scale color table is loaded (as illustrated in Figure 45) all 16.7 million
colors are immediately accessible to IDL. So, for example, if I wanted to draw a plot
in the color yellow on this 24-bit system, I would want to pick a 24-bit integer number
that had the eight lowest bits set (full red), the eight middle bits set (full green), and
none of the highest bits set (no blue). As it happens, this number for the color yellow
is represented as the long integer 65535. To draw the plot above on a 24-bit color
display, you would type:
IDL> Plot, data, Color=65535L
Since most of us are not fluent manipulating the bits of a 24-bit number, the number is
sometimes expressed in hexadecimal notation, where two digits (0-F) are enough to
set eight bits at a time (i.e., 256 or 2"8 possible values can be set by two hexadecimal
digits). For example, using hexadecimal notation, the way to express full red and
green, but no blue is like this:
IDL> Plot, data, Color= OOFFFF1xL
To draw a yellow (255, 255,O) plot on a charcoal (70,70,70) background, with a
green (0, 255,O) title, using hexadecimal notation, you type this:
Graphical Display Techniqztes
decon~positionone way or the other (e.g., you might be displaying either an 8-bit
image, in which case you want color decomposition to be turned off, or a 24-bit
image, in which case you want color decomposition turned on) you must set it before
you issue the graphics display command:
Device, Decomposed=O
TV, image8bit
Device, Decomposed=l
TV, image24bit, True=l
A new Get-Deconzposed keyword was introduced for the Device command in IDL 5.2
which can tell you the current "decomposed" state.
IDL> Device, Get~Decomposed=usingDecomposed
IDL> Print, usingDecomposed
Note that if you are running IDL on a PC or Macintosh platfornl with a 24-bit display,
and you want to display a 24-bit image in the correct image colors, you must either
have a gray-scale color table loaded or you must set the Deconzposed keyword equal
to l. If you have Deconfposed set equal to 0, then the 24-bit image values are routed
through the color table vectors. This rzever results in what you want, unless a gray-
scale color table is loaded. So a good rule of thumb is to always set color decomposi-
tion on before you display a 24-bit image. Note that the TVInzage program you down-
loaded with this book automatically sets the correct Decolnposed value depending
upon whether the image is 8-bit (Deconzposed=O) or 24-bit (Deco~?zposed=l).
GetColor works like this. If it is called with a single parameter that is the "name" of a
color it recognizes, it returns either a three-element vector that is that color's triple or
a 24-bit value that can be decomposed into that color, depending upon the current
decomposed state of the device at the time the program is called. If you have color
decomposition turned off (or if you are on an 8-bit display device) then you can load
the color triple at a color table index and use it appropriately, like this:
IDL> Device, Decomposed=O
Graphical Display Techiziques
One of the huge advantages of a 24-bit display is that you can have literally dozens of
images on the display at once, each displayed with a different color table. (Remember
that loading color tables on a 24-bit display only really makes sense if color decomp-
osition is turned off. And remember that it is turned or? by default when IDL starts up.)
But what if you now loaded a different color table? Would you want the colors in
those dozens of images to change? Probably not, since each image uses its own set of
colors, which are specified directly.
Thus, if you have an image displayed on a 24-bit monitor, and you change the color
table with a color table changing tool like XColors or XLoadCT, the new colors will
not take effect until you re-display the image, and translate the image pixels once
again through the color table into specific direct colors. To see how you might write
code to change color tables on a 24-bit display and have your graphics re-displayed
automatically, see "Automatic Updating of Graphic Displays When Color Tables are
Loaded" on page 66.
value of one is a linear ramp function from one value to the next. Gamma values of
less than one or greater than one result in exponential ramp functions of different
shapes and steepness.)
The XPnlette command allows you to modify and create your own color tables by
setting end point colors with sliders, and then interpolating the intervening values.
Individual colors may also be modified in this program. Note that you may specify
colors in XPcrlette in color systems other than the RGB color system. In the end,
however, no matter what color system you use to specify the colors, it is a red, green,
and blue color vector that is loaded into the color tables.
Note that if you run XPnlette on a 24-bit display you should be sure that you have
turned color decomposition off, since this program is an old 8-bit program that has not
been updated to work automatically on 24-bit devices.
It is quite easy to make your own color tables as well. Here is a simple little program
(with no error checking!) named Make-CT that can create a color table of an arbitrary
number of colors between two color end points. Open a text editor and type:
FUNCTION MAKE-CT, begcolor, endcolor, ncolors
scaleFactor = FindGen(nco1ors) / (ncolors - 1)
colors = BytArr (ncolors, 3)
FOR j= O r 2 DO colors [ * , j ] = begcolor [j] + (endcolor [j] $
- begcolor [j]) * scaleFactor
RETURN, colors
END
Compile this program by typing this:
IDL> .Compile make-ct
Open the World Elevatiorz Data image and display it in a window, like this:
IDL> image = LoadData (7)
IDL> Window, XSize=360, YSize=360
IDL> LoadCT, 0
IDL> TVScl, image
Suppose you want a color table that goes from yellow (255,255,O) to blue (O,O, 255).
You can create it with the Make-CT program and load it like this:
IDL> yellow = [255, 255, 01
IDL> blue = [O, 0, 2551
IDL> TVLCT, Make CT(yellow, blue, !D.Table-Size)
IDL> Device, ~ e c % ~ o s e d = ~
IDL> TVScl, image
Suppose you want to display this image in 150 colors that go from yellow to green to
blue. You might do this:
IDL> scaledImage = ~ y t S c l
(image, Top=149)
IDL> green = [O, 255, 01
IDL> TVLCT, Make CT (yellow, green, 75)
IDL> TVLCT, maker^^(green, blue, 75)) 75
IDL> TV, scaledImage
Note that a lot of obfuscation of data can take place in the way you choose your colors.
Be careful. You might want to have a look at the paper by Bernice E. Rogowitz and
Lloyd A. Treinish entitled "How Not to Lie with Visualization" in Computers Irz
Physics, 10(3):268, 1996. If you are really interested in color and the display of data,
read The Visual Display of Quarztitative Ii~omzntiorzand Erzvisiorzirzg Irzforrnatiolz by
Edward Tufte. These books just might change the way you write IDL programs!
Workirzg lvitlt Colors in ZDL
You could save the vectors now if you wanted to, but most color table vectors are 256
elements in length. It would be easy to make these vectors that length. Type:
Now, you can write these vectors into a file if you like, but it is easier to use IDL's
Save command to save them in an IDL save file. Type:
, r, g, b
IDL> Save, File=~mycolors.savl
Next, just to prove to yourself that you did save the vectors, load another color table
and delete these three variables. Type:
IDL> LoadCT, 0
IDL> DelVar, r, g, b
IDL> Help, r, g, b
Now, when you are ready to use the vectors, you restore them with the Restore
command. Notice that they come back in the same variable name in which you saved
them. If you have a variable with the same name defined in the same IDL session (as
you do here), the variable will be overwritten. This means you will probably want to
give these variables well-thought out names. Type:
IDL> Restore, l mycolors . savl
IDL> Help, r, g, b
To use these variables you will have to resize them into the number of colors in your
IDL session. The commands will look like this:
IDL> r = Congrid(r, !D.Table Size)
IDL> g = Congrid (g, !D.~ a b l e r ~ i z e )
IDL> b = Congrid (b, !D.Table-Size)
IDL> TVLCT, r, g, b
Rather than type these commands every time you want to load one of your saved color
tables, it would be easier to write a little IDL program that did it automatically. If you
always save your RGB vectors with the variable names r, g, and b, you can write a
program name CT-bad like this. (There is no error checking in this little program!)
Open a text editor and type:
PRO CT-Load, filename
IF N-Paramso EQ 0 THEN filename = 'mycolors.savl
Restore, filename
r = Congrid(r, !D.Table-Size)
Grapltical Display Teclzniqlres
g = Congrid(g, !D.~able-size)
b = Congrid(b, !D.~able-size)
TVLCT, r, g, b
END
Save the file as ct-londpr-o and compile it like this:
Now, whenever you want to load this color table, all you have to do is type this:
IDL> CT-Load
Figure 46: The izzcnzber of major tick i~ttewalsis chaitged with the XTicks keyword.
To write the labels as floating point values with two digits to the right of the decimal
place, type:
IDL> Plot, curve, XTickFormat=I (F6.2)l
It is possible to use specific strings as tick labels, too. This is accomplished with the
TickNnrne keyword, which can have up to 30 string elements. For example, you can
label the plot by the days of the week, like this:
IDL> labels = [lMON1,lTUE1,lWED1,lTHU1,lFRI','SAT1]
IDL> Plot, curve, XTickName=labels
Your output looks like the illustration in Figure 47
The axis annotation can be suppressed by setting the axis tick format to ( A l ) ,like this:
IDL> Plot, curve, XTickFormat= (Al)
1 , , , , , . . , , , , , , . .
30
20
10
0
1 Jan 91 24 Nov 91 16 0ct 92 8 Sep 93 1 Aug 94 24 Jun 95
As you can see, these labels are quite long and there is a danger that they will crowd
together. You may want to display the dates in another way. For example, you might
want to rotate them by 45 degrees with respect to the axis. Unfortunately, the tick
formatting function you just wrote will not help in this case. You will have to resort to
a more brute force method, placing the labels with the X Y O L ~command.
~S
You can, however, still use the Date program to format the strings for you. To make
this work, you will have to draw the plot with the X axis labels suppressed and you
will need a vector with the proper tick values. You can suppress the axis labeling by
setting the tick format for the axis to ( A l ) . You can get the tick label values in vector
form by using the XTick-Get keyword. For example, the plot will be drawn like this
(the Positiorz keyword is used to leave room for the axis labels):
IDL> Plot, dates, curve, XTickFormat=' (Al)l , XStyle=l, $
XTicks=numTicks, XTick-Get=tickValues, $
Position=[O.l, 0.2, 0.85, 0.951
Then the labels will be attached with the XYOutS command. You can use the
![XY].Window system variables to find the end-points of the X and Y axes in
normalized coordinates. This information is critical for positioning the labels
correctly. Your code will look like this:
IDL> ypos = Replicate ( !Y.Window [O] - 0.04, numticks+l)
IDL> xpos = !X.Window [0] + ( !X.Window [l]) - !X.Window [0]) * $
FIndGen (numTicks+l)/ n u m ~ i c k s
IDL> FOR j= O r numTicks DO XYOutS, xpos [j] , ypos [j] , $
Date(0, j, tickValues[j]), Alignment=O.O, $
Orientation=-45, /Normal
Your output will look like the illustration in Figure 49.
30
20
10
0
7 7
U g' 84' ;;7
O9 %.c 0 W q% V
'
9
Q7 Q
, 9 93 9
' Q$
Figztre 49: Rotated axis labels are created tvitlz tlze XYOzttS command.
One way to handle this kind of data is to assign it the value NaN. The NaN value is a
particular bit pattern, different on each machine architecture. The bit pattern for the
machine IDL is running on is stored in the system variable !Values in the field F-NaN.
To see this, open the Elevatiorz Data data set with the LoadData command, like this:
IDL> data = LoadData (2)
IDL> ! P.CharSize = 1.0
This data set is a 4 1 by 41 floating point array. But suppose the data was not complete.
Suppose while you were collecting the data the collecting instrument temporarily shut
down while scanning three lines in the middle of the 2D data set. You want to assign
the NaN value to these three scan lines. You can type this:
IDL> badData = data
IDL> badData[*, 30:32] = !Values.F-NaN
Now, when you display the surface, IDL does not connect those values that are
represented by the NaN bit pattern. Type:
IDL> Surface, badData
Your output will look similar to the illustration in Figure 50.
There is another way to handle missing or bad data besides setting it to the NaN bit
pattern. This is with the Mill-Value and Max-Value keywords that are available for
most IDL graphics output commands. By setting these keywords, any data value that
is less than the minimum value or greater than the maximum value is ignored (not
drawn) by the graphics output command. For example, in the elevation data set you
used above, you can draw just those contours within a specific range. Here are some
commands that show you how this works. Here contours lines with values less than or
equal to 400 and greater than or equal to 1000 are not drawn in the plot on the right:
IDL> Window, XSize=500, YSize=375
IDL> !P.Multi = 10, 2, 1, 0, l]
IDL> values = FIndGen(10)*150 + 100
IDL> label = Replicate (l,10)
IDL> Contour, data, Levels=values , /Follow, C-Labels=label
IDL> Contour, data, Levels=values, / ~ o l l o w ,C-Labels=label, $
Min-Value=400, Max-Value=1000
IDL> !P.Multi = 0
Setting Up a 3 0 Coordiitate System iiz ZDL
Figz~re50: Missing data is represented iit this szcrface plot by the NUN vabe.
Your output in the right-hand side of your graphics window will look similar to the
illustration in Figure 5 1.
Figure 51: Coiztour lines can be suppressed by the Miiz-Value aizd Max-Vahe key-
words to the Contour cornmartd.
two ways: (l) use the Surface comnland with the Save keyword if you want to have
axes in your 3 0 space, or (2) use the Scale3 command if you just want a 3D space, but
you are not concerned about axes.
To give this plot more of a 3D appearance, you might want to connect each point by a
line to the XY plane. In fact, you might want to color the line according to the Z value,
to give the user even more information. You might type this:
IDL> zcolors = ~ y t S c l ( z ,Top=99) + 1B
IDL> LoadCT, 22, NColors=100, Bottom=l
IDL> FOR j=0,40 DO plots, [x[jl, x[jIl [ ~ [ j l ,~ [ j l ,l $
[z [ j ] , 0] , ~olor=zcolors[j] , / T ~ D
Your output will look similar to the illustration in Figure 52.
A graphical display with colors may be considered incomplete without a color bar to
indicate what the colors mean. You can add a colorbar to this display with the
Colorbar program you received with this book. Type:
Settirtg Up a 3 0 Coo~.dirtateSystem iii IDL
I I
Figzcre 52: A scattei-plot iiz tlze 3 0 space set zcp with tlze Surface cornmuitd.
Figure 53: A surface displayed with the axes tlzrouglz the origin. The 3 0 space was
created witlz the Scale3 command.
Notice that the Y axis in this plot does not appear to extend to the edges of the plot.
This is an optical illusion caused by the rotation of the plot. What can you do to
convince yourself that this is really an illusion?
Before you move to the next section, be sure to restore your system variables to their
original values. Type:
IDL> Restore, 'system.sav'
It is also easy to combine surface and contour plots. Open the 41 by 41 Elevatior? Data
data set with the LaadData command like this:
IDL> peak = LoadData(2)
You might want to be able to see, for example, a shaded surface rendition of this data
at the same time that you look at a contour plot of the data. This is easily accomplished
in IDL by using the Slzade-Sulfcommand to establish a 3D coordinate system, which
is then used to position the contour plot. Your code might look like this:
IDL> Window, XSize=400, YSize=400
IDL> TVLct, [70,01, [70,2551, [70,01, 0
IDL> LoadCT, 5, NColors=!D.Table-Size-2, Bottom=2
IDL> !P.Charsize = 1.5
IDL> Shade-Surf, peak, /Save, Background=O, Color=l, $
Shades=BytScl(peak, Top=!D.Table-Size-2) + 2B
You might want to add another Z axis on the right-hand side of this plot, like this:
IDL> Axis, ZAxis=O, 40, 0, 0, /T3D, Color=l
Finally, you are ready to add the contour plot. Be sure to use the NoErase keyword to
avoid erasing whatever is already on the display. The ZValue keyword positions the
contour plots along the Z axis. The value given to the ZValue keyword will be in
Gvnpkical Display Techniques
normalized units. A value of 1.0 will put the contour plot on the top of the 3D
coordinate space that has been created.
IDL> Contour, peak, NLevels=12, Color=l, ZValue=l.O, /T3D, $
/NoErase, /Follow
Note that sometimes when graphics plots are located along the edges of a 3D coordi-
nate space that some lines can get clipped. This is usually the result of round-off eiror
in the calculation that places them in the 3D space. If some of the contour lines in your
contour plot appear incomplete, add the NoClip keyword to the contour command,
like this:
IDL> Contour, peak, NLevels=12, Color=l, ZValue=l.O, / T ~ D ,$
/NoErase, / ~ o l l o w ,/ ~ o ~ l i p
Your output should look similar to the illustration in Figure 55.
Other combinations of graphics output commands are also possible. You are limited
only by your imagination.
Figure 56: The XZizterAnimate animatioiz tool, with the MRI Head data set loaded.
tables while the animation is mnning. Note that if you click the End Animntiorz button,
you will have to type all three animation commands over again to get the animation
working. You will have to stop the animation before you can use the slider to go to a
particular animation frame.
Normally, animations run as fast as your machine allows them to run. (A widget timer
event is responsible for getting the next animation frame in the sequence.) The
animation speed button adds a delay to this timer event. If you want to start your
animation out at a slower speed initially, you can give the final XIlztel-A~ziinnte
command a speed parameter. This number will be between 0 and 100. For example, to
start the animation off at 50% of its fastest speed, you might type this:
IDL> XInterAnimate, Set= [80,100,57], /Showload
IDL> FOR j=O, 56 DO $
XInteranimate, Frame=j, Image=head[*,*,j]
IDL> XInteranimate, 50
This is being done with the Wiizdow keyword. Type this code in a new editor window
as you see it here.
XInterAnimate, Set=[256,320,57], /Showload
yellow = G e t C ~ l o r ( ~ y e l l o w '!D.Table-Size-2)
,
LoadCT, 3, NColors=!D.Table-Size-2
FOR j=O, 56 DO BEGIN
TVImage, BytScl(head[*,*,j], Top=!D.Table-Size-3)
XYOutS, 0.1, 0.1, / ~ o r m a l ,StrTrim(j,2), Color=yellow
XInteranimate, Frame=j, Window=!D.Window
ENDFOR
XInteranimate, 50
END
Save the program as headniziinnte.pro. To run this program (which is called a main-
level IDL program), type this:
IDL> . Run headanimat e
You can put nizy IDL graphics commands inside the loop. This gives you considerable
flexibility with the kinds of data you can animate. You're output should look similar
to the illustration in Figure 57.
Figzcre 57: The arzimatiolz pixmaps are loaded by fillilzg the wilzdow with graphics
and coping the colzteizts of the wirzdow to tlze pix~nap.
Notice the Write MPEG button on the XZrzterAizilnnte interface. Indeed, you can use
the button to create MPEG movies of your data. But be careful. The button suggests
that it is easier to do than it really is, at least in IDL version 5.3. In fact, you should be
careful that your animation window is some multiple of 16 (as in the example above)
or you might obtain strange results. And not all MPEG viewers can play the MPEG
movies created by IDL. Be sure you have a recent version of the MPEG viewer that
supports the "latest" MPEG standard.
Graphical Display Teclzrtiqltes
like this, where the variable angles is an output variable and will contain the set of
Delaunay triangles returned from the calculation:
IDL> Triangulate, lon, lat, angles
Note that you can return the co171,exI?zrll of these points with the Triangulate conl-
mand, too. Simply add a fourth variable argument to the command above and the
points on the perimeter of this set of points will be returned to you. The convex hull is
useful in many arithmetic operations.
You can visualize this set of tiiangles (there are 70 triangles in this data set), by typing
this command:
IDL> FOR j=0,69 DO BEGIN t = [angles[*,j], angles[O,jll & $
plots, lon [tl , lat [tl , Color=3 & ENDFOR
Your output will look like the illustration in Figure 59.
Figure 59: The set of Delaunay triangles returned by the Trtangulate comntartd.
To grid the data, take the set of triangles that came back from Triarzgulnte and pass it
to the TriGrid procedure. You can set up the grid endpoints or bounds, and the grid
spacing. You can also specify the value for data that lies outside the triangles with the
Missii7g keyword. Moreover, you can get the new latitude and longitude vectors that
go with the gsidded data from the output keywords XGrid and YGrid. For example,
you can type this:
IDL> latMax = 50.0
IDL> latMin = 20.0
IDL> lonMax = - 7 0 .0
IDL> lonMin = -130.0
IDL> mapBounds = [lonMin, latMin, lonMax, latMax]
IDL> mapspacing = [0.5, 0.251
IDL> gridData = Trigrid(lon, lat, value, angles, $
mapspacing, mapBounds, Missing=Min(value) , $
XGrid=gridlon, YGrid=gridlat)
You can now use the gridded data in those IDL commands that require it.
IDL> Contour, gridData, gridlon, gridlat, /Follow, $
NLevels=lO, XStyle=l, YStyle=l, Background=l, Color=2
Your output will look like the illustration in Figure 60.
Grnpltical Display Tecltrziqires
Figure 60: The results of the TriGrid commaizd displayed as a contozcr plot.
Note that you can smooth the surface by using the Quilztic or Smootl? keywords (they
are synonymous), like this:
IDL> gridData = Trigrid(lon, lat, value, angles, $
mapspacing, mapBounds, Missing=Min(value), $
XGrid=gridlon, YGrid=gridlat, /Quintic)
IDL> Contour, gridData, gridlon, gridlat , / ~ o l l o w ,$
NLevels=lO, XStyle=l, YStyle=l, Background=l, Color=2
You can also get better results sometimes by using the Extrapolate keyword to allow
the extrapolation of values outside the triangle boundary. For example, you can type
this:
IDL> Triangulate, lon, lat, angles, hull
IDL> gridData = Trigrid(lon, lat, value, angles, $
mapspacing, mapBounds, Missing=Min(value), $
Extrapolate=hull)
IDL> Contour, gridData, gridlon, gridlat, / ~ o l l o w ,$
NLevels=lO, XStyle=l, YStyle=l, Background=l, Color=2
Your output should look similar to the illustration in Figure 6 1.
Figure 61: The results of TriGrid ~vitltthe Extrapolatioit and Smootlz keywords set.
Figure 62: Spherically gridded data displayed as a corztorcrplot ort top of a ntappro-
jectiort.
FValue=value, /Degrees
IDL> gridData = Trigrid(value, Sphere=angles, $
map~pacing,mapBounds, Missing=Min(value), $
/Extrapolate, /Degrees)
IDL> Map-Set, /orthographic, /Grid, /Continents, / ~ a b e l ,$
s so tropic, 35, -100, Color=l
IDL> Contour, gridData, gridlon, gridlat, / ~ o l l o w ,$
NLevels=lO, XStyle=l, YStyle=l, Color=2, /overplot
Your output should look similar to the illustration in Figure 62.
sor is clicked do+vn.It is not. The default behavior is to wait for a click then act as if a
no wait were in effect. In a loop this difference can be critical.
Drawing a Box
You might want to select a portion of the display and draw a box around it. Here are
some commands to select the diagonal corners of a box with the Cursor command,
draw the box (be sure to draw the box so that it includes a portion of the actual data),
and zoom the plot into the box coordinates. First, draw the plot:
IDL> P l o t , c u r v e
Next, use the cursor to select one corner of the box you want to draw. You will want to
be sure to click the cursor in the current graphics window. To make sure you know
which one that is and that it is not hidden, type:
IDL> WShow
Now type the first Cursor command. Click somewhere inside the plot axes:
IDL> Cursor, x l , y l , o own ; S e l e c t one corner of box.
Now type the second Cursor command. Click somewhere inside the plot axes:
IDL> Cursor, x 2 , y2, /Down ; S e l e c t diagonal c o r n e r of box.
The coordinates returned from the Cursor commands above are in data coordinate
space. Draw the box like this:
IDL> P l o t S , [ x l ,x1, x2, x 2 , X 1 1 , [ y l ,y 2 , y2 ,y l Y ~ I Color=~ellow
Your output will look something like the illustration in Figure 63, although the actual
box on your plot will depend on where you clicked in the window.
Graphical Display Teclziziqries
Figure 63: A line plot with a box dratviz arozuzd the portioit of the data. The box co-
ordiizates were selected with the Cursor commaizd aizd the box drawn
with the PlotS comnzaizd.
To zoom into this portion of the plot, you will have to be sure the box coordinates are
ordered properly. This is necessary because you may have first selected the lower-
right corner of the box and then the upper-left, in which case xl will be greater than
x2.You can imagine other scenarios as well. To account for all of them, type:
IDL> xmin = Min ( [xl,x2] , Max=xmax)
IDL> ymin = Min ( [yl,y2] , Max=ymax)
Finally, you are ready to zoom into the portion of the data enclosed by the box. In
addition to setting the data ranges properly, you also have to set the [XYIStyle
keywords. Do you know why? If you don't, try the command below without these two
keywords. What happens?
IDL> Plot, curve, ~ ~ a n g [xmin,
e= xmax] , YRange= [ymin,p a x ] , $
XStyle=l, YStyle=l
IDL> S = Size(image)
IDL> Cursor, col, row, /Device ; Click in the window!
IDL> PlotS, [col, col] , [O, S [ 2 ] 1 , /Device, Color=yellow
IDL> PlotS, [0, S [l]] , [row, row1 , /~evice,Color=yellow
Notice how easy it is to access the data in the image in that particular column and row.
For example, you can easily plot the column and row data profiles for the image, like
this:
IDL> Window, 1, XSize=500, YSize=300
IDL> !P.Multi = [O, 2, l]
IDL> Plot, image[*, row], Title=IRow Profile1
IDL> Plot, image [col, *] , Title=' Column Profile '
IDL> !P.Multi = 0
IDL> WSet, 0
Your output should look similar to the illustration in Figure 64.
200
150
150
100
100
50
50
0 0
0 100 200 300 400 0 100 200 300 400
Figure 64: The columrz and row profiles of the irnage in the colulnlz and row select-
ed with the Cursor commarzd.
Save the file as 1oopl.pr.o. (This file is among the files you downloaded to use with
this book.) To compile and run this little main-level program, type:
Move your cursor into the image window and start clicking with the left mouse button.
You will see the image pixel values printed out in your log window until you use some
button other than the left in the image window.
What happens if you use other keywords besides DOMII?
with the Czrrsor command?
Experiment a little and find out.
You will notice that the line of the box is not yellow, as you might have expected, but
is instead a sort of multicolored hue, although it shows up reasonably well. The
underlying pixels have been "flipped" in this graphics function,
To erase the box, all you have to do is flip the underlying pixel values back to their
original values. This is easily done by issuing the PlotS command again, like this:
You can issue the above command over and over, making the box appear and
disappear at will. Before you go on, be sure you set your graphics function back to
SOURCE mode, like this:
IDL> Device, Set-Graphics-~unction=3
You can easily take advantage of graphics functions in your IDL programs. For
example, open the loopl.pro main-level program you wrote earlier and modify it to
look like this. Here you are going to draw a large cross-hair at each image location as
you click in the image window. Save this program as loop2.pl-o. Type:
yellow = GetC0lor(~yellow~,!D.Tab~e-Size-2)
LoadCT, 3, NColors=!D.Table-Size-2
TvLCT, 255, 255, 0, topcolor
TVImage, BytScl(image', Top=!D.Table-Size-3)
!Mouse.Button = 1
; Go into XOR mode.
Device, Set_Graphics_Function=6
; Get initial cursor location. Draw cross-hair
Cursor, col, row, /Device, /Down
PlotS, [col,col], [O,3601 , /Device, Color=yellow
PlotS, [O,3601 , [row,row] , /Device, Color=yellow
print, ' Pixel Value: , image (col, row)
l
; LOOP.
REPEAT BEGIN
; Get new cursor location.
Cursor, colnew, rownew, /Down, /Device
; Erase old cross-hair.
PlotS, [col,col], [0,360], /~evice,Color=yellow
PlotS, [O,3601 , [row,row] , /Device, Color=yellow
Print, 'Pixel Value: l , image(colnew, rownew)
; Draw new cross-hair.
Plots, [colnew,colnew] , [O,3601 , /~evice,Color=yellow
PlotS, [0,360], [rownew,rownew], /Device, Color=yellow
; Update coordinates.
col = colnew
row = rownew
ENDREP UNTIL !Mouse.Button NE 1
;Erase the final cross-hair.
PlotS, [col,col], [O,3601 , /Device, Color=yellow
PlotS, [O,3601, [row,row] , /~evice,Color=yellow
Graphical Display Techrziqztes
Place your cursor in the image window and click several times with your left mouse
button. You should see a cross-hair at each cursor location. To exit the program, click
the right or middle mouse button.
Figure 65: The device copy technique iizvolves copying a rectarzgzclarportion of the
source wirzdo~vinto a locatiorz in the destiizatio~zwiizdo~v.In practice erz-
tire windows may be copied or the source and destiizatio~zwindow can be
the same wiizdow.
The actual copying is done with the Device command and the Copy keyword (hence,
the name of the technique). The general form of the command is this:
Device , Copy= [ s x , Sy, Col, row, dx, dy, sourceWindowID]
In this command, the elements of the Copy keyword are:
The device coordinates of the lower-left corner of the rectan-
gle in the source window. (The source window is the window
the rectangle is being copied from.)
col The number of columns to copy in the source window. This is
the width of the rectangle.
Erasing Alznotatioiz Froin tlze Display
row The number of rows to copy in the source window. This is the
height of the rectangle.
dx, dy The device coordinates of the lower-left corner of the rectan-
gle in the destiizatioiz window. (The destii~ntioi~
window is
the window the rectangle is being copied to. The destirmtion
window is always the current graphics window.)
sourceWindowID This is the window index number of the source window. The
rectangle is copied from this window into the current graph-
ics window (which is identified by the !D. Wii7h1vsystem
variable). The source window can be the current graphics
window, but it is more often a window other than the current
graphics window. It is often a pixmap window.
To see how this works, create a pixmap window and display the image in it. Pixmap
windows are created with the Wilzdow command and the Pixinap keyword, like this:
IDL> Window, 1, / ~ i x m a p ,XSize=360, YSize=360
IDL> TVImage, BytScl(image, Top=!D.Table-Size-2)
Notice that you had no visual clue that anything happened when you typed these
commands. This is because the pixmap window exists only in video RAM, not on the
display. To be sure there is something in this window, open a third, regular window
and try to copy the contents of the pixmap window into it. If your third window looks
like your image window, you have typed the commands correctly. Type:
IDL> Window, 2, XSize=360, YSize=360
IDL> Device, Copy= [O, 0, 360, 360, 0, 0, l]
Notice that you copied the entire contents of the pixmap window into this new
window. This is similar to just re-displaying the image in the new window, except that
it is several orders of magnitude faster. It is not unusual to copy the entire contents of
a pixmap window into a display window, even if you just have to "repair" a portion of
the display window.
Delete the last two windows you created (including the pixmap window), like this:
IDL> WDelete, 1, 2
It is important to remember to delete pixmap windows when you are finished with
them. They do take up memory that you may want to use for something else. Some
window managers allocate a fixed amount of memory for pixmap windows. Others
use virtual memory if your pixmap window exceeds the capacity of the video RAM.
X-terminals have notoriously little memory for pixmap windows.
To see how the device copy technique works in practice, modify the main-level
program loop2.pro you wrote earlier. You may want to copy that program to another
file and name it loop3.pr.o. Make the modifications shown below.
yellow = G e t C ~ l o r ( ~ y e l l o w !D.Table-Size-2)
~,
LoadCT, 3, NColors=!D.Table-Size-2
TVImage, BytScl(image, Top=!D.Table-Size-3)
!Mouse.Button = 1
; Create a pixmap window and display image in it.
Window, 1, /Pixmap, XSize=360, YSize=360
TVImage, BytScl(image, Top=!D.Table-Size-3)
;Make the display window the current graphics window
WSet, 0
: Get initial cursor location. Draw cross-hair.
Graphical Display Teclzrziqltes
Place your cursor in the image window and click several times with your left mouse
button. To exit the program, click the right or middle mouse button. Notice that the
cross-hairs are drawn in a yellow color.
Delete the pixmap window before you move on to the next exercise. Type:
IDL> WDelete, 1
WDelete, 1
END
To lun this program, type:
IDL> .Run scroll
The program scrolls once. To run it again, type:
Can you modify the program to make it keep scrolling until you stop it?
Z-Graphics Buffer
Figzcre 66: The 2-graphics buffer can be thought of as a 3 0 box that keeps track of
depth infornzation. Rays hit the objects in the 2-graphic buffer and their
pixel valzces are projected back onto the projection plane.
The idea is that once you load your objects into the 3D box, you take a "snap-shot" or
picture of the projection plane. This is the 2D projection of the 3D objects in the box.
Objects that are behind other objects will not be shown. (This behavior can be
modified by the Transparent keyword to some IDL graphics commands, as you will
see.) The snap-shot is, in effect, a screen dump of the projection plane taken with the
TVRD command.
Grnphics Displny Tricks in the 2-Graphics Bziffer
The Copy keyword copies the current color table into the Z-buffer. Be sure to save the
name of your current graphics display device, so you can get back to it easily. Type:
IDL> thisDevice = !D.Name
IDL> Set-Plot, Z , /Copy
Figure 67: The Z-graphics buffer can be used to combine 3 0 objects with hidden
surface removal performed automatically.
Graphics Displny Tricks ilz the 2-Graphics Buffer
Next, you want to constluct the individual polygons that describe these three image
slices or planes. In this case, each polygon will be a simple rectangle with four points
(the corners of the rectangle). Each point in the rectangle will be descsibed by an
(x,y,z) triple. Another way to say this is that each plane will be a 3 by 4 array. You can
type this:
IDL> xplane = [ [xpt, 0, 01, [xpt, 0, zs] , [xpt, ys I zsl , $
[xpt, ys, 01 l
IDL> yplane = [ 10, ypt, 01, [O, ypt, zsl , [xs, ypt, zsl, $
[XS, YPt, 01 l
IDL> zplane = [ [ 0, 0, zptl, [xs, 0, zptl , [xs, YSI zptl, $
[O, ys, zptl l
The next step is to get the image data that will correspond to each image plane. This is
done easily by using array subscripts in IDL. Type:
IDL> ximage = head [xpt, *, *l
IDL> yimage = head[*, ypt, *l
IDL> zimage = head[*, * , zpt]
Notice that these images are all 3D images (one dimension is a l).What you want are
2D images associated with each image plane, so you must reformat these 3D images
into 2D images with the Refor111command, as shown below. In this case, the Refor111
command reformats the image into an 80 by 100 by l image. When the final
dimension in an array is 1, IDL discards it. The result here is an 80 by 100 image.
IDL> ximage = Re£ orm (ximage)
IDL> yimage = Re£ orm ( yimage)
IDL> zimage = Re£ orm ( zimage )
To display these images properly, you want to make sure they are scaled properly into
the number of colors on your display. It is important to scale them in relation to the
entire data set. Scale the data and load colors like this:
IDL> minData = Min (head, Max=maxData)
IDL> topcolor = !D.Table-Size-2
IDL> LoadCT, 5, NColors=!D.Table-Size-l
IDL> TVLCT, 255, 255, 255, topColor+l
IDL> Device, Decomposed=O
IDL> ximage = BytScl(ximage, Top=topColor, Max=maxData, $
Min=minData)
IDL> yimage = BytScl(yimage, Top=topColor, Max=maxData, $
Min=minData)
IDL> zimage = BytScl(zimage, Top=topColor, Max=maxData, $
Min=minData)
Next, you are ready to set up the Z-graphics buffer. The Erase command will erase
whatever may have been left in the buffer previously. In this case, you are erasing
with a white color, to give more definition to your display. Type:
IDL> thisDevice = !D.Name
IDL> Set-Plot, ' Z t
IDL> Device, Set-Colors=topColor, set-Resolution=[400,400]
IDL> Erase, Color=topColor + 1
Set up the 3D coordinate space with the Scale3 command. Here the axes will be
labelled with the size of each dimension. Type:
You are finally ready to render the slices in the Z-graphics buffer. You will use the
Polyfll command for this purpose. The Pnrterrz keyword will be set to the image slice
you wish to display. The Ir~znge-Coo& keyword contains a list of the image
Graphics Display Tricks in the 2-Graphics Buffer
Figzcre 68: Arz example of warpirzg irnage data into plartes irz the 2-graphics buffer.
file tl-ansparent.pro.You can find a copy of this journal file anlong the files you
downloaded to use with this book.)
IDL> Journal, transparent
IDL> Set-Plot, ' 2 '
IDL> Erase, Color=topColor + 1
DL> Polyfill, xplane, / T ~ D ,~attern=ximage,/image-~nterp, $
Image-Coord=[ [0,01, 10, zsl, [ys, zsl, [YS, 01 1 , $
Transparent=25
IDL> Polyfill, yplane, /T3D, Pattern=yimage, /Image-Inter~,$
Image-Coord=[ [0,01, [O, ZS], [xs, zs], [xs, 01 1 , $
Transparent=25
IDL> Polyfill, zplane, / T ~ D ,Pattern=zimage, /Image-Interp, $
Image-Coord=[ [0,01, [xs, 01, [xs, ysl, 10, ys] 1 , $
Transparent=25
IDL> picture = TVRD()
IDL> Set-Plot , this~evice
IDL> Window, /Free, ~Size=400,YSize=400
IDL> Erase, Color=topColor + l.
IDL> TV, picture
IDL> Journal
If you make a mistake and the final output image doesn't look correct, correct the
mistake in the journal file and re-n~nthe journal file by typing this:
Figure 69: The isosurface arzd data slice cornbirzed irz the Z-graphics buffer.
Reading and Writing Data in IDL
Chapter Overview
The purpose of this chapter is to introduce you to the general input and output routines
in IDL. The basic rule in IDL is this: "If you have data, you can read it into IDL."
There is no IDL format, no special data massaging you have to do to make your data
ready to be brought into IDL. This makes IDL one of the most powerful and flexible
scientific visualization and analysis programs available.
Specifically, you will learn:
How to open a file for reading and writing
How to locate files and assign logical unit numbers
How to obtain machine independent file names
How to read and write ASCII or formatted data
How to read and write unformatted or binary data
How to work with large data files
How to read and write files in popular file formats like GIF and JPEG
More often you see Operz commands written in a more general way. For example, you
might see IDL code that looks like this:
OpenR, lun, filename
In this case, the variable l~irlholds a valid logical unit number and the variable
filerlnnze holds a machine specific file name, which will be attached to the logical unit
number.
Note that thefilerznnze variable name is machine specific. This means that it must be
expressed with the local machine syntax if it contains directory specifications and it
Inay be case sensitive on those machines (e.g., UNIX machines) in which file names
are case sensitive.
Finding Files
Another useful command is the FirzdFile command. This command returns a string
array containing the names of all files matching a given file specification. This is
useful in automating file opening tasks from within IDL programs or for building a list
of files from a directory without knowing how many files will be located there at any
one time. For example, if you wanted to print the size (in bytes) of all the data files in
the current directo~y,you might write IDL code like this:
files = FindFile('*.datl, Count=numfiles)
IF numfiles EQ 0 THEN Message, 'No data files here!'
FOR j=O,numfiles-l DO BEGIN
OpenR, lun, files(j), et-~un
fileInfo = FStat (lun)
Print, fileInfo.size
Free-Lun, lun
ENDFOR
One important point to note about the FirzdFile command is that the file specification
(e.g., '*.dat' above) is given in relative path names, not absolute path names. The
command also returns relative path names and not absolute path names. This is differ-
ent from the Pickfile dialog, which always returns an absolute path name.
- -- -- -- -
Logical U~ritNumbers Purposes
1-99 These numbers can be used directly in Open commands
Table 9: The 128 logical zcizit fzzcinbers are separated into two groups. One group
yozc ztse directly. Tlte otlzer yozc inanage ~vitlzthe Get-Lzcit and
Free-Luiz commaizds.
opened fewer than 28 files all at once). If you open files from procedures and
functions, it is an excellent idea to always use the G e t L u n comr7zai1d,since you can
never guarantee that a particular logical unit number will be available if you select it
directly.
24.0000
33.6000 77.2000
5
a r r a y vector scalar
Notice that IDL has put white space between each element of the array variables and
that it has started each new variable on its own line. IDL is using an 80 column width
by default. If you want a different column width, you can set it with the Width
keyword to the Opei?W command.
What you see is that the entire first line is read into the word variable.
Test data f i l e .
(If you wanted to separate the first line into individual words, you could use IDL's
string processing routines to do so.) This ability to read to the end of a line can be a
nice feature of IDL. To see why, first rewind the file pointer to point to the beginning
of the file. You can use the Poirzt-Lurz command for this purpose. Type:
IDL> P o i n t - L u n , lun, 0
Now we can read the first two lines of the data file into a header variable. Type this:
IDL> h e a d e r = S t r A r r ( 2 )
IDL> R e a d F , l u n , header
IDL> P r i n t , header
These commands read the first two lines of the file and position the file pointer at the
start of the array data. You see printed out both header lines on the same output line,
like this:
Test data f i l e . C r e a t e d : Wed May 2 1 1 4 : 3 6 : 1 3 1 9 9 7
Reading arid Writing Fonitatted Data
In this case, IDL reads eight separate data values from the file. When it got to the end
of the first row of data, it went to the second row (rule 4), because more data remained
to be read. You read into the middle of the second row. Now mle 5 comes into play. If
you read more data now, you will start on the third data line, because the rest of the
second line will be ignored. Try it. Type this:
IDL> vector3 = F l t A r r ( 3 )
IDL> ReadF, l u n , vector3
IDL> P r i n t , vector3
You see this:
The variable vector3 contains the values 12.0, 13.0, and 14.0. And now the file pointer
is located on the fourth data line in the file (rule 5 again).
6. Make every effort to convert data into the data type expected by the variable.
To see what this means, read the fourth and fifth data lines into a string array, so that
the file pointer is positioned at the sixth data line (the line that starts with the value
33.6000). Type this:
IDL> dummy = St r A r r ( 2 )
IDL> ReadF, l u n , dummy
Now, suppose you want to read two integer values. IDL makes every effort to convert
the data (floating point values in this case) into integers. Type this:
IDL> i n t s = I n t A r r ( 2 )
IDL> ReadF, l u n , i n t s
IDL> P r i n t , i n t s
You see this:
Notice that the floating point values have simply been truncated. There is no attempt
to round values to the nearest integer in this conversion process.
Reading and Writing Data in IDL
Complex data must have a real part and an imaginary part separated by a comma
and enclosed in parentheses. If only a single value is provided, it is considered the
real value and the imaginary value is set to 0. For example, you can read complex
data from the keyboard by typing this:
IDL> value = ComplexArr (2)
IDL> Read, value
: (314)
: (4.4,25.5)
IDL> Print, value
Be sure to close the test.clc1t file before you leave this section. Type this:
IDL> Free-Lun, lun
Sometimes string processing is easier if you turn strings into byte arrays first. Then
you can process the byte arrays, and convert back to strings at the end. This may be a
good approach here, although other methods are possible. To convert the string to a
byte array, type this:
IDL> thisArray = B y t e (thisstring)
IDL> Help, thisArray
You see this is a 19-elementbyte array. You need to know the ASCII character for a
blank space. Use IDL to tell you by typing this:
Reading arzd Writing Formatted Data
IDL> thisTemp = F l t A r r ( 4 1 )
Open the colunzn.rlnt file for reading and read the header line, like this:
IDL> OpenR, l u n , 'column. d a t , / G e t -Lun
IDL> ReadF, l u n , h e a d e r
Now, since you put the data into the file using a loop, you might t ~ to
y read the data
out of the file using a loop, too. In other words, you might try to type this:
IDL> FOR j = 0 , 4 0 DO ReadF, l u n , t h i s ~ a [tj ] , t h i s ~ o n [ j,] $
thisTemp [ j ]
Unfortunately, this does not work. Although no error is generated by the command
above, no data is written into the variables either. (If you print the values of the
variables you will see they are all zeros.)
The reason this doesn't work is that there is a hard and fast rule in IDL that states that
you calzrzot read into n subscripted variable. The reason for this is that IDL passes
subscripted variables into IDL procedures like RendF by ~jnlueand not by reference.
Data that is passed by value cannot be changed inside of the called routine, since the
routine has a copy of the data and not a pointer to the data itself. Changing this
behavior would require a major re-write of IDL and is not likely to happen.
There are two ways to solve this problem. The first involves reading the data into
temporary variables in a loop. This solution is best implemented by using a text editor
to enter the commands into a file, since it is difficult to write multiple line loops at the
IDL command line. Type this Poilzt-Lurz command to position the file pointer back at
the start of the data file:
IDL> Point-Lun, lun, 0
Type the following commands in a text file named 1oopread.pr-o. The file is among
those you downloaded to use with this book. Type:
templ = 0 . 0
temp2 = 0 . 0
temp3 = 0 . 0
ReadF, l u n , h e a d e r
FOR j = 0 , 4 0 DO BEGIN
ReadF, l u n , t e m p l , temp2, temp3
t h i s L a t [ j ] = templ
t h i s L o n [ j l = temp2
thisTemp [ j ] = temp3
ENDFOR
END
provided by IDL. To see how this works, rewind the data file again by typing this
command:
IDL> Point-Lun, lun, 0
Next, read the data all at once into a 3 by 41 floating point array, like this:
IDL> header = "
IDL> array = FltArr(3, 41)
IDL> ReadF, lun, header, array
Parse the vectors out of this large array using array subscripts, like this:
IDL> thisLat = array [ 0 , * 1
IDL> thisLon = array [l,* l
IDL> thisTemp = array [2,* l
IDL> Free-Lun, lun
Notice, however, that these new vectors are column vectors (i.e., they are 1 by 41 two-
dimensional arrays). Type this:
IDL> Help, thislat, thisLon, thisTemp
To make these column vectors into the more familiar row vectors, the Reform
command is used to reform the 1 by 4 1 arrays into 4 1 by 1 arrays. When the last
dimension of a multi-dimensional array is one, IDL drops that dimension. Type this:
IDL> thisLat = Reform(thisLat)
IDL> thisLon = Ref orm (thisLon)
IDL> thisTemp = Reform(thisTemp)
IDL> Help, thislat, thisLon, thisTemp
respectively, by typing in the Name text widget at the upper right of the form. All three
fields are floating point type.
When you are finished naming and typing each column of data, click the Fi~lisl?button
on the form. The result is an IDL structure variable that describes the data in the file
and can be used as input into the Reacl_ASCII command. Type:
IDL> Help, fileTemplate, /Structure
To read the data in the file, use the Read-ASCII command like this:
IDL> data = Read-ASCII('column.datl, Template=fileTemplate)
The data is read immediately and the result is an IDL structure variable containing
three fields, labeled latitude, lorzgitzide, and tenlperature. Type:
IDL> Help, data, /Structure
If you want to pull the vectors out of the structure, you can type this:
The ASCII-Tenzplare and Read-ASCII commands can be used with either free
formatted data files or with the explicitly formatted data files described below.
This command is especially useful when numerical information is needed from a file
header. For example, suppose the first line in an ASCII data file indicates the number
of columns and rows in the data file, followed by the date the data was collected, like
this:
10 24500 12 June 1 9 9 6
This header could be read from the file and a properly sized data array created for
reading the data, like this:
firstLine =
ReadF, lun, firstLine
columns = 0
rows = o
date = l
Reads, firstline, columns, rows, date
dataArray = FltArr (columns, rows)
contain information about the data inside the file. This information, called n~etadataor
attributes, can be used to read the data properly.)
About the only thing you can learn about an unformatted data file without prior
knowledge is how big it is. That is, how many bytes it contains. For example, the
FStat (File Status) command can tell you the size of this file in bytes, like this:
IDL> f i l e I n f o = F S t a t ( l u n )
IDL> P r i n t , f i l e I n f o . s i z e
65536
There are 65,536 bytes in this file, but this gives you no clue as to how the data is
organized structurally. For example, the bytes could be arranged as a 128 by 5 12 two-
dimensional array or as a 64 by 64 by 16 three-dimensional array. There is no way to
know. Desperate programmers sometimes try various combinations of arrays sizes and
then display the data. If it doesn't "look" right, they tly another size, and so on.
In this case, the data should be arranged as a 256 by 256 byte array, so the image
variable can be set up like this:
IDL> i m a g e = ~ y t A r (r2 5 6 , 2 5 6 )
Now, read the data from the file and close the file, like this:
IDL> R e a d U , l u n , i m a g e
IDL> F r e e - L u n , lun
To display the data, type this:
IDL> W i n d o w , X S i z e = 2 5 6 , Y S i z e = 2 5 6
IDL> D e v i c e , D e c o m p o s e d = O
IDL> T V S c l , i m a g e
To get around this significant limitation, most headers in unformatted data files have a
defined size (usually some multiple of 256). Suppose, for example, that you decide
that all image files will have a 5 12-byte header. You could use the Replicate and String
commands in IDL to create a string filled with blank characters that was exactly 5 12
bytes long like this:
IDL> header = String (Replicate(32B, 512) )
The byte value 32 is the ASCII value of a blank character. To insert the file
information string into this longer header string to create a header of the proper size,
you can use the StrPut (Put a String inside of another string) command in IDL, like
this:
IDL> StrPut, header, f i l e ~ n f o ,0
Finally, the header and the data can be written into a new unfolmatted data file, like
this:
IDL> OpenW, lun, 'process.datl
IDL> WriteU, lun, header, edge
IDL> Free-Lun, lun
When strings are written to an unformatted file, just exactly the number of bytes that
comprise the string are written into the file.
The type of repeating unit in the data file does not have to be a simple 2D image array
as it is here. It can be a complicated structure. For example, each repeating unit might
consist of a 128 byte header, two 100-element floating point vectors and a 100 by 100
integer array. If this was the case, an associated variable might be created for the file
like this:
OpenR, 10, 'example.datl
info = BytArr(l28)
xvector = FltArr (100)
yvector = FltArr (100)
data = IntArr(100, 100)
struct = {header:info, x:xvector, y:yvector, image:data)
repeatingunit = Assoc(l0, struct)
Since the variable that is mapped to this data file is a structure variable, you must
make a temporary copy of the structure before it can be de-referenced. For example, to
display the image portion of the third repeating unit in the file, you would type:
The connection between an associated variable and a file is closed in the usual way
with the Free-Lur? or Close commands, like this:
Free-Lun, lun
Close, 10
Table 10: ZDL can read artd write many popular data file formats, often by meatts
of library routines written iiz the IDL la~tguageor by dynamic lirtk
modules (DLM) that are added to ZDL at run-time. The C D 4 netCD8
and HDFfile formats are known collectively as scierttific data formats
and have their own IDL interface and library of routi~tes.See "Readi~tg
and Writing HDF Data" on page 161for more irtformatio~zabout HDF
file formats.
The query commands all work in the same way. They are functions that return a value
of 0 or l to indicate if they were successful (indicated by a I) reading the metadata of
an image file. If they successfully read a file, an IDL structure variable containing
information about the file is returned to the user as an output variable. Users can
access the fields of this structure to obtain information about the file.
For example, to query the JPEG file, rose.dat, in the IDL exnmples/data subdirectory
and return information about the file in the variablefileirzfo, type this:
IDL> filename = Filepath(Irose.j p g l , $
SubDir= [ examples , data l)
IDL> ok = Query-JPEG (filename, f ileinfo)
Readiirg and Writing Files with Popular File Forrttats
20 5
0 b 1
0 100 200 300
Column Number
Figure 70: The ColumtzAvg program that will be saved as a GZF, JPEG, and TIFF
file.
display device supports) of your display device. To see the depth of your current
display device, type this:
Device, Get-Visual-Depth=thisDepth & Print, thisDepth
If the depth of your display device is eight, you will obtain the image and color table
vectors in one way, if the display device depth is greater than eight, you will obtain
them another way. If you are writing platform-independent IDL code, you will have to
check the depth of the display device and execute the appropriate sequence of
commands depending on the depth of the device.
To obtain the 2D byte array, all you need is to get a screen dump of the current
graphics window. That is done with the TVRD command, like this:
IDL> image2d = TVRD ( )
Note that if you already have a 2D byte array, there is no need to copy the graphics
window. We copy it here because we want the GIF file to contain the entire graphic
display, not just the image data.
If the Display Depth is Greater than Eight
If the display depth is greater than eight, then you have just a bit more trouble ahead of
you in creating a GIF file. Because, remember, when you take a screen dump on a 16-
bit or 24-bit display you obtain a 24-bit image with the color information built into the
image itself. (See "Obtaining Screen Dumps on 24-Bit Displays" on page 73 for
additional information.) In other words, the TVRD command should be used like this:
IDL> Device, Decomposed=l
IDL> image24 = TVRD (True=l)
What you have to do is go from a 24-bit image with colors built into the image itself to
a 2D image and the appropriate color table. You see an illustration of the problem in
Figure 71.
image24 image2d R G B
I I
Figure 71: To create a GIFfile on a 24-bit display, you have to take a 24-bit image
screen dump and extract a 2 0 inzage array and the appropriate color vec-
tors. This is done with the Color-Quan command.
This is done with the Colo~Quarzcommand in IDL. With Colo~Qunrzyou can select
either of two different algorithms for extracting the color information from the 24-bit
image: a statistical method that attempts to find the N best colors that represent the
original colors in the image, or a method that divides the color space into a cube and
uses a dithering method to select colors. I've found the statistical method (which I
illustrate here) the best when there are many colors in the graphic display, and the
color cube method (which is selected by means of values to the Cube keyword) is
better if you have just a few colors in your graphic or your graphic is rendered in
shades of gray. Be sure to read the Color-Qumz documentation if you are not happy
with the result. And please understand that the results of Color-Qunrz are seldom as
good as the original 24-bit display. The command looks like this:
The number 1 as the second parameter indicates the pixel interleaving in the 24-bit
image. It should be the same value as you used for the True keyword in the TVRD
Reading and Writing Files with Popular File Fori~tats
command above. The variables i; g, and b are output variables that now contain the
color table vectors.
In fact the color at index 6 has no relationship at all to the colors at 5 and 7; it is just a
co1,or.
So GIF files that come with color tables cannot be resized with bilinear interpolation.
They must be resized by replicating the pixel values that are already in the image. For
example, this image should be resized by using the NoIrzterpolation keyword to TVIrn-
age, like this:
IDL> TVImage, thisImage, /~oInterpolation
But now you have the task of creating a 24-bit image out of this 2D image array and
the color table vectors. The problem is shown in Figure 72, below.
-
image2d R G B image24
Figzcre 72: Witlz JPEGfiles yozc ofterz have the opposite sitziatiorz from a GZFJile.
Here you have to coizstruct a 24-bit image frorzz a 2 0 image arzd tlze color
table vectors.
You construct the 24-bit image by substituting the color table value into the
appropriate plane of the 24-bit image. This is easily accomplished in IDL by
subscripting the color table vector by the 2D image values. The code will look like
this:
IDL> S = Size (image2d, / ~ i m e n s i o n s )
IDL> image24 = BytArr (3, S [Ol , S [l] )
IDL> TVLCT, r , g, b, e et
IDL> image24 [ 0 , * , *l = r [image2d]
IDL> image24 [ 1,* , *l = g [image2d]
IDL> image24 [ 2 , * , *l = b [image2d]
build your own objects) in a later chapter. But for now, just type the following
command to create a DICOM object:
IDL> thisobject = O b j - N ~ W ( ~ I D L £ £ D ~ Cfilename)
O~~,
To see the metadata of the file (or what is sometimes called the data dictionary), you
can dump the file elements with the Dzn1npElemerzts method. The command looks like
this:
This particular file has 93 different data elements, and I don't want to show them all,
but the first 25 elements in the file are shown in Figure 73.
0 : (0002,0000) : UL : META Group Length : 4 : 176
1 : (0002,0001) : OB : META File Meta Version : 2 : 0 1
2 : (0002,0002) : U1 : META Media Stored SOP Class UID : 26 : 1.2.840.10008.5
3 : (0002,0003) : U1 : META Media Stored SOP Instance UID : 4 4 : 1.2.840.113619.2
4 : (0002,0010) : U1 : META Transfer Syntax UID : 18 : 1.2.840.10008.1.2
5 : (0002,0012) : U1 : META Implementation Class UID : 18 : 1.2.840.113619.6.5
6 : (0002,0013) : SH : META Implementation Version Name : 6 : 1-2-5
7 : (0002,0016) : AE : META Source Application Entity Title : 6 : sdc21
8 : (0008,0000) : UL : ID Group Length : 4 : 390
9 : (0008,0001) : UL : ID Length to End (RET) : 4 : 132456
10 : (0008,0008) : CS : ID Image Type : 16 : ORIGINAL\PRIMARY
11 : (0008,0016) : U1 : ID SOP Class UID : 26 : 1.2.840.10008.5.1.4.1.1.4
12 : (0008,0018) : U1 : ID SOP Instance UID : 44 : 1.2.840.113619.2.1.2.139348932
13 : (0008,0020) : DA : ID Study Date : 8 : 19890203
14 : (0008,0021) : DA : ID Series Date : 8 : 19890203
15 : (0008,0023) : DA : ID Image Date : 8 : 19890203
16 : (0008,0030) : TM : ID Study Time : 6 : 092618
17 : (0008,0031) : TM : ID Series Time : 6 : 095819
18 : (0008,0033) : TM : ID Image Time : 6 : 095846
19 : (0008,0050) : SH : ID Accession Number : 0 :
20 : (0008,0060) : CS : ID Modality : 2 : MR
21 : (0008,0070) : L0 : ID Manufacturer : 18 : GE MEDICAL SYSTEMS
22 : (0008,0080) : L0 : ID Institution Name : 28 : THOMAS JEFF UNIVHOSPITAL MRI
23 : (0008,0090) : PN : ID Referring Physician's Name : 4 : HUME
24 : (0008,1010) : SH : ID Station Name : 8 : FOR.IC0
25 : (0008,1030) : L0 : ID Study Description : 4 : KNEE
Figure 73: Thefirst 25 data elemertts irt the MR-Krzee.dcm data file, obtairzedfi.om
duntpirtg tlte elements to tlte commaizd log wirzdo~v.
The first column of numbers is the Reference Number. The second column of
numbers, which are enclosed by parentheses are the Group and Element references.
These are expressed in hexadecimal notation. The third column of letters is the Value
Represeiztatiorz. And what follows on each line is the Description of the data element.
The easiest way to obtain one of these data elements is to use its G ~ o u pand Elernerlt
number with the Getvalue method of the object. The return value of this function will
be a pointer array, with each pointer pointing to a data element having that particular
Group and Eleinelzt number. For example, to obtain the modality of this image, you
can look at data element 20, which has a Group number of 0008 and an Elelnerzt
number of 0060, like this:
IDL> modality = thisObject-~GetValue(100081x,
'0060'~)
IDL> Help, modality
Reading and Writing Files with Popzllnr File Fonnats
Chapter Overview
HDF stands for Hierarchical Data Fornzat. This is one of the three data formats that
are called Scientific Data Formats in IDL. (The others are CDF and netCDF.) The
HDF file format is a multi-object file format for sharing scientific data in a distributed
environment. The format was developed at the National Center for Supercomputing
Applications. The purpose of the HDF format is to provide a machine-independent,
efficient data storage format for the various types of data and metadata (information
that describes the data) commonly used by scientists. The HDF format has been
chosen as the data file format for the NASA EOS (Earth Observing System) program
because of the ease of retrieving, visualizing, analyzing and managing data that is
stored in this format. Many data products, especially those coming from satellite
systems, are now stored in the HDF format. IDL supports version 4.lr3 of the HDF
library as of IDL 5.3.1. HDF-EOS support was also added in the IDL 5.2 version.
If you want to learn more about the HDF format, you can contact the National Center
for Supercomputing Applications directly. Use your favorite World Wide Web
browser to select the HDF home page at this URL:
The HDF Frequently Asked Questions (FAQ) file can be found at this World Wide
Web address:
You will find a lot of good HDF information at this site, including HDF user guides
and other HDF documentation.
In this chapter, you will learn:
How the HDF file format is implemented in IDL
How to read and write scientific data sets (SDS) in the HDF format
How to store metadata with your scientific data sets
How to store color table palettes with your scientific data sets
Reading aizd Writing HDF Data
Data Descriptor
4
Bytes
Figure 74: Each data object in HDF consists of a data descriptor, shown here, arzd
a data element, which is the data itseg
The tag field identifies the type of data stored in the data element. There are over 200
tags defined by the NCSA for general use. You can get a complete list of tags from
their World Wide Web site. You can see a short list of tags you might encounter in
Table 11, below.
The reference number in the data descriptor distinguishes between different data
elements with the same tag. For example, all scientific data sets will have the same
tag, but the combination of tag number and reference number will uniquely determine
a specific SDS in a file. The HDF routines will keep track of reference numbers for
you as you write HDF data objects to a file.
HDF Applicatioit Prograntnting Interface
--
20 1 8-Bit Palette
70 1 SD Dimension Record
702 SD Data
703 SD Scales
704 SD Labels
705 SD Units
706 SD Formats
708 SD Coordinates
73 1 SD Calibration Data
1963 Vdata
1965 Vgoup
Table 11: Commorz HDF tag numbers and their associated rneaizings.
The offset field points to the location of the data element in the file in bytes. The
length field identifies the size of the data element in bytes.
In general, you don't need to know much about the data descriptor except that it
exists. You will find yourself accessing the tag and reference number fields in various
ways. It will help if you know what these numbers refer to and how they are used.
There are five primary data objects allowed in an HDF file. They are mstel- images
(both 8-bit and 24-bit), colorpnlettes, scierztijk data (which are multi-dimensional
arrays and, in fact, could be 8-bit and 24-bit images), nizrzotntiorzs, and Vdntn (which
are tables of data). You see the five primary HDF data objects illustrated in Figure 75,
below. A sixth data object, the Vgroup, does not contain data, but is used to group the
other five primary data objects within an HDF file. Conceptually, a Vgr-oup is like an
IDL structure variable.
An HDF file consists of an HDF file header and any number of these data objects,
each with its data descriptor and data element. Data objects are individually accessible
in the file, even if they are included in larger sets or groups of data. In fact, a single
data object (e.g., a palette) may be included in several data sets or groupings.
VData
Annotations (tables of data)
Figure 75: The five primary HDF data objects. A sixth object, the Vgroup, does not
hold data, but serves to group aizy of these five primary objects within aiz
HDF data file.
constitute what is called an rrpplicntiorz yrogrrrnznzirzg irzterfice or API for HDF files.
All the IDL routines in the HDF API start with the prefix HDF-. You can generally
determine which data object the IDL routine applies to by the letters that follow this
initial prefix. You see an example of this by viewing the IDL prefixes and the
corresponding HDF objects the IDL routines apply to in Table 12, below.
Notice that there are two different APIs for working with scientific data. The older
API uses the prefix HDF-DFSD-. This model was the original scientific data set
(SDS) model developed by NCSA and it allowed access to a single HDF file at any
one time. The new API uses the prefix HDF-SD- and supports a more powerful and
general SDS model that allows access to multiple HDF files simultaneously.It also
incorporates the netCDF data model developed by the Unidata Program Center. The
HDF-DFSD- API was available in older versions of IDL mostly for backward
compatibility with older HDF files. It has now been dropped altogether in IDL 5.3.
The new HDF-SD- API should be used to create new HDF files.
Similarly, annotations should no longer be used in conjunction with SDS objects.
Metadata that was once stored as annotations in the old SDS model is now more
conveniently stored as attributes in the new SDS model. You will learn to use the new
SDS API in this chapter.
Working ~vitlzHDF Files
In this command the variablefileizni?ze is a string with the name of the file. It is often
obtained in a machine-independent way by using an IDL command like
DialogPickfile, like this:
filename = ~ialog-Pickfile(/~rite)
fileID = HDF-Open(filename, /Create, /write)
The keyword Create opens a new file instead of one that already exists. The keyword
Wi-ite puts the file in write access mode.
You may want to be sure that the file is an HDF file before you try to open it. All HDF
files have a "magic cookie" or special number written as the first four bytes of the file.
The HDF magic cookie is the hexadecimal number Oe031301. You can use the
HDF-IsHDF command to read this magic cookie and tell you whether the file is an
HDF file. The function returns a 1 if the file is an HDF file, and a 0 if not. You can use
it like this:
filename = Pickfile(/Write)
IF HDF-IsHDF(fi1ename) THEN $
fileID = HDF-Open(filename, /Write, / ~ e a d )
Notice that the Write and Rend keywords are both set in this open command. In this
access mode, you can both read data from the file and write additional information to
it as well. (An alternative keyword is RdWr, which has the same effect.)
Reading a i d Writing HDF Data
Note that the number of tags in an HDF file is not very useful. In the first place, the
number of tags will probably not be the same as the number of data objects you wrote
to the file because there are a number of data descriptors written for each data object.
These associated data descriptors pertain to attributes of data objects that may or not
be filled in or written explicitly by the user. It is much more useful to know how many
objects with a specific tag number are in the file.
Suppose you are interested in a particular type of data object, perhaps the number of
SDS objects in the file. You can see from Table 11 on page 163 that if you wanted to
know how many SDS objects were in the HDF file, you could look specifically for tag
number 702. You do this with the Tag keyword, like this:
fileID = HDF-Open(fi1ename)
numberOfSDSObjects = HDF-Number(fileID, Tag=702)
SDS dimensions specify the size and shape of a multidimensional array. The number
of dimensions of the SDS array is known as the m ~ ofk the SDS. If you assign the
same dimension name to two dimensions, the SD interface treats both dimensions as
the same data object and any changes made to one will be reflected in the other. The
size of a dimension will always be a positive integer.
Table 13: The ZDL applicatioit prograinrnirtg interface (APZ)for HDF scientific
data sets. The Type columit iderztifies which routirtes are ZDL
procedures as opposed to ZDL functions.
In addition to these attributes, data sets may have these other predefined attributes:
Calibration This attribute stores the scale and offset values used to cali-
brate the data in the SDS.
Coordinate System This is the coordinate system associated with a particular data
set (e.g, "Device" or "Normal")
Fill Value This is a value used to fill areas between non-contiguous
writes to SDS arrays.
Range A two-element vector that describes the minimum and maxi-
mum value of the data set.
Workilrg with Scientific Data Set HDF Files
To open an SDS file for reading and writing, you can type:
sdFileID = HDF-SD-Start ( filename , /RdWr)