0% found this document useful (0 votes)
451 views465 pages

IDL Programming Techniques 2nd Edition

Uploaded by

Ayorinde T Tunde
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
451 views465 pages

IDL Programming Techniques 2nd Edition

Uploaded by

Ayorinde T Tunde
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 465

IDL

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.

Printed in the United States of America.

Published by Fanning Software Consulting


1645 Sheely Drive
Fort Collins, CO 80526
Phone: 970-221-0438
Fax: 970-221-0438
E-Mail: books @ dfanning.com
Internet Address: http:Nwww.dfanning.cod

Printing History
October 2000: First Printing
November 2000: Added information on double buffering of graphics displays.
April 2003: Minor updates to image processing section.

IDL is a registered trademark of Research Systems, Inc.


Macintosh is a registered trademark of Apple Computer, Inc.
The X Window System is a trademark of the Massachusetts Institute of Technology.
Windows and Windows NT are trademarks of Microsoft Corporation.
UNIX is a registered trademark of UNIX System Laboratories.

ISBN 0-9662383-2-X

ISBN 0-9662383-2-X
April 2003
+ Discovering the Possibilitie

Table of Contents

Table of Contents ....................... .............................................. . ..111..


.
Preface .......... ...........I..l..I.ll...~l.......l..........ll.... XVII
..
Preface to the First Edition xvii
Preface to the Second Edition xviii

Getting Started .................... .


...
.........l.mm~..l..ml.........m........~........l.
1
Chapter Overview 1
Philosophy Behind this Book 1
Using this Book 2
Required Version of IDL 2
Working with Colors in the IDL Session 2
Style Conventions Used in the Book 2
Capitalization 3
Comments 3
Line Continuation Characters 4
IDL Programs and Data Files Used in the Book 4
Installing the Program Files 4
Determining Your IDL Home and Current Directory 4
Downloading the Program Files Used with the Book 5
Make Sure Your Coyote Directory is on the IDL Path 5
Copying the Data Files 5
Obtaining Additional Help 6
Working with IDL Commands 6
Anatomy of an IDL Command 6
Positional Parameters 6
Keyword Parameters 7
IDL Procedures and Functions 7

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

Simple Graphical Displays ...................I.I......


nnIa.Im..n..ll ..............
ll~nm.. 19
Chapter Overview 19
Simple Graphical Displays in IDL 19
Creating Line Plots 20
Customizing Graphics Plots 22
Modifying Line Styles and Thicknesses 22
Displaying Data with Symbols Instead of Lines 23
Displaying Data with Lines and Symbols 23
Creating Your Own Plotting Symbols 24
Drawing Line Plots in Color 24
Limiting the Range of Line Plots 25
Changing the Style of Line Plots 26
Plotting Multiple Data Sets on Line Plots 28
Plotting Data on Multiple Axes 29
Creating Surface Plots 29
Customizing Surface Plots 31
Rotating Surface Plots 31
Adding Color to a Surface Plot 32
Modifying the Appearance of a Surface Plot 34
Creating Shaded Surface Plots 34
Changing the Shading Parameters 35
Using Another Data Set For the Shading Values 35
Creating Contour Plots 36
Selecting Contour Levels 38
Modifying a Contour Plot 39
Changing the Appearance of a Contour Plot 39
Adding Color to a Contour Plot 41
Creating a Filled Contour Plot 42
Positioning Graphic Output in the Display Window 44
Setting the Graphic Margins 46
Setting the Graphic Position 46
Setting the Graphic Region 47
Creating Multiple Graphics Plots 47
Leaving Room for Titles with Multiple Graphics Plots 48
Non-Symmetrical Arrangements with !P.Multi 50
Adding Text to Graphical Displays 51
Finding the Names of Available Fonts 52
Adding Text with the XYOutS Command 52
Using XYOutS with Vector Fonts 53
Aligning Text 54
Erasing Text 54
Orienting Text 55
Positioning Text 55
Adding Lines and Symbols to Graphical Displays 56
Adding Color to Your Graphical Displays 57

Working with Image Data ...................


mn~~n~IllllIIIllI~~lm~lm~~m~lmullmllllll~lmml~ 59
Chapter Overview 59
Working with Images 59
Displaying Images 60
An Alternative Image Display Command 62
Scaling Image Data 62
Scaling Images into Different Portions of the Color Table 63
Displaying 8-Bit Images with Different Color Tables on 24-Bit Displays 64
Displaying 24-Bit Images 64
Displaying 24-Bit Images on 24-Bit Displays 65
Displaying 8-Bit Images on 24-Bit Displays 66
Automatic Updating of Graphic Displays When Color Tables are Loaded 66
Controlling Image Display Order 68
Changing Image Size 68
Changing Image Size in Postscript 69
Positioning an Image in the Display Window 69
Positioning Images with Normalized Coordinates 70
Reading Images from the Display Device 72
Obtaining Screen Dumps on 8-Bit Displays 72
Obtaining Screen Dumps on 24-Bit Displays 73
Reading a Portion of the Display 73
Table of Coiiteizts

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

Graphical Display Techniques .................................m..m........llmm 81


Chapter Overview 81
Working with Colors in IDL 81
Using the Indexed versus the RGB Color Model 82
Static versus Dynamic Color Visuals 83
Specifying Colors on an 8-Bit Display 84
Specifying Decomposed Colors on a 24-Bit Display 84
Specifying Undecomposed Colors on a 24-Bit Display
8 6
Determining if Color Decomposition is On or Off 86
Obtaining Device Independent Colors 87
Loading Color Tables on a 24-Bit Display 88
Obtaining a Copy of the Color Table 89
Modifying and Creating Color Tables 89
Saving Your Own Color Tables 91
Creating Your Own Axis Annotations 92
Adjusting Axis Tick Intervals 92
Formatting Axis Annotations 92
Writing a Tick Format Function 94
Handling Missing Data in IDL 95
Setting Up a 3D Coordinate System in IDL 97
Setting Up a 3D Scatter Plot 98
Positioning the 3D Axes Through the Origin of a Plot 99
Combining Simple Graphical Displays 100
Animating Data in IDL 102
Setting Up the Animation Tool 103
Loading the Animation Buffer 103
Running the Animation Tool 103
Controlling the Animation 103
Saving Animation Pixmaps 104
Animating Other Types of Graphic Data 104
Gridding Data for Graphical Display 106
Delaunay Triangulation Method of Gridding 106
Spherical Gridding of Data 108
Using the Cursor with Graphical Displays 109
When Is the Cursor Position Returned? 110
Which Mouse Button Was Used with the Cursor? 111
Annotating Graphics Output with the Cursor 111
Drawing a Box 111
Using the Cursor with Images 112
Using the Cursor in Loops 113
Erasing Annotation From the Display 114
The "Exclusive OR" Method of Erasing Annotation 114
The Device Copy Method of Erasing Annotation 116
Drawing a Rubberband Box 118
Graphics Window Scrolling 119
Graphics Display Tricks in the Z-Graphics Buffer 120
The Z-Graphics Buffer Implementation 121
A Z-Graphics Buffer Example: Two Surfaces 121
Make the Z-Graphics Buffer the Current Device 121
Configure the Z-Graphics Buffer 122
Load the Objects into the Z-Graphics Buffer 122
Take a Picture of the Projection Plane 122
Display the Result on the Display Device 122
Some Z-Graphics Buffer Oddities 123
Warping Images with the Z-Graphics Buffer 123
Transparency Effects in the Z-Graphics Buffer 125
Combining Z-Graphics Buffer Effects with Volume Rendering 126

Reading and Writing Data in IDL ................................................ 129


Chapter Overview 129
Opening a File for Reading or Writing 129
Locating and Selecting Data Files 130
Selecting File Names 130
Selecting Directory Names 131
Finding Files 131
Constructing File Names 131
Obtaining a Logical Unit Number 131
Using Logical Unit Numbers Directly 132
Allowing IDL to Manage Logical Unit Numbers 132
Determining Which Files are Attached to Which LUNs 133
Reading and Writing Formatted Data 133
Writing a Free Format File 133
Reading a Free Format File 134
Rules For Reading Free Format Data 134
Examples of Reading and Writing Free Format Files 136
Reading a Simple Data File 136
Writing a Column-Format Data File 137
Reading a Column-Format Data File 137
Creating a Template for Reading Column-Format Data 139
Writing with an Explicit File Format 140
A Few Common Format Specifiers 140
Writing a Comma Separated Explicitly Formatted Data File 141
Table of Contents

Reading a Comma Separated Explicitly Formatted Data File 141


Reading Formatted Data From a String 141
Reading and Writing Unformatted Data 142
Reading an Unformatted Image Data File 142
Writing an Unformatted Image Data File 143
Reading Unfonnatted Data Files with Headers 144
Problems with Unformatted Data Files 145
Accessing Unformatted Data Files with Associated Variables 145
Advantages of Associated Variables 146
Defining Associated Variables 146
Reading and Writing Files with Popular File Formats 147
Querying Image Files for Information 147
Creating a Graphic Display Program 149
Creating Color GIF Files 150
If the Display Depth is Eight 151
If the Display Depth is Greater than Eight 152
Writing the GIF File 153
Reading a GIF File 153
Creating Color JPEG Files 154
If the Display Depth is Eight 154
If the Display Depth is Greater than Eight 155
Writing the JPEG File 155
Reading a JPEG File l55
Creating Color TIFF Files 156
Reading Dicom Image Files 157
Using the IDLffDicom Object 157

Reading and Writing HDF Data ...................


~.~~l~ll~Ialnll~llnlllll~lllmn 161
Chapter Overview 161
Why Use the HDF Format? 162
Primary HDF Data Objects 162
HDF Application Programming Interface 163
Working with HDF Files 165
Opening HDF Files 165
Closing HDF Files 166
Determining the Number of Tags in an HDF File 166
Working with Scientific Data Set HDF Files 166
Optional Dimension Scales 167
Optional User-Defined Attributes 167
Optional Predefined Attributes 167
Opening HDF Files Containing Scientific Data Sets 169
Closing HDF Files Containing Scientific Data Sets 169
Creating or Selecting Scientific Data Sets 169
Creating a New SDS 169
Selecting an Existing SDS 170

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

Creating Hardcopy Graphics Output .........................Il.......I.l.......175


Chapter Overview 175
Selecting the Graphics Hardcopy Output Device 175
Configuring the Graphics Hardcopy Output Device 176
Determining the Current Device Configuration 176
Common Device Command Keywords 177
Creating the Postscript File 178
Sending Graphics to the Hardcopy Device 179
Printing PostScript Files 180
Printing PostScript Files on Computers Running MacOS 183
Printing PostScript Files on a Windows Computer 181
Producing Encapsulated PostScript Output 181
Encapsulated PostScript Graphic Preview 182
Producing Color PostScript Output 182
Color and Gray Scale Images in PostScript 183
True-Color Images 183
Creating Quality Output on PostScript Devices 184
Similarities Between the Display and PostScript Devices 184
Differences Between the Display and PostScript Devices 185
Problem: PostScript Windows May Have a Different Aspect Ratio 185
Solution: Make the Aspect Ratios of Graphics Windows the Same Size 185
Problem: PostScript Devices Have a Higher Display Resolution 186
Solution: Don't Use Device Coordinates to Position Graphics 187
Problem: PostScript Devices Can Use Different Display Fonts 187
Solution: Take Care in Designing and Positioning Text 187
Problem: PostScript Devices Use Background and Plotting Colors Differently 189
Solution: Understand How PostScript Handles Background and Plotting Colors 190
Problem: PostScript Devices Often Have More Colors Than the Display Device 191
Solution: Be Sure to Scale Your Data Appropriately in PostScript 192
Problem: PostScript Devices Display Images Differently 193
Solution: Size Images with the TV Command 195
Calculating PostScript Offsets in Landscape Mode 198
Configuring the PostScript Device with PSConfig 198
Configuring and Using the Printer Device 200
Positioning Graphics with the Printer Device 202
Outputting Images with the Printer Device 203
Loading Colors in the Printer Device 204
Table of Contents

Color Loading Work-Arounds 205

IDL Programming Fundamentals .....................


.......................207
Chapter Overview 207
Writing an IDL Batch File 207
Writing a Main-Level IDL Program 208
Writing an IDL Procedure 209
Scope of Procedure and Function Variables 210
Creating a Positional Parameter 211
Defining Optional or Required Positional Parameters 212
Defining a Keyword Parameter 213
Using Keyword Abbreviations 213
Defining Optional Keyword Parameters 214
Is the Keyword Defined? 214
Handling Keywords with Binary Properties 215
Passing Undefined Keywords by Keyword Inheritance 216
Creating Output Parameters 217
Passing Infonnation by Reference or by Value 217
Using Keyword Inheritance with Output Parameters 219
Is the Parameter Present? 220
Was the Parameter Used? 220
Writing an IDL Function 221
Square Bracket Notation and Function Calls 222
Reserving Function Names with the Forward-Function Command 223
Using Program Control Statements 223
True and False Expressions in IDL 223
Making Multiple Statements Appear As Single Statements 224
The IF...THEN...ELSE Control Statement 225
The Conditional Expression 226
The FOR Loop Control Statement 226
The WHILE Loop Control Statement 226
The REPEAT...UNTIL Loop Control Statement 227
The BREAK Control Statement 227
The CONTINUE Control Statement 227
The CASE Control Statement 227
The SWITCH Control Statement 228
The GOT0 Control Statement 229
Error Handling Control Statements 229
The On-IOError Control Statement 229
The On-Error Control Statement 230
The Catch Control Statement 230
Error Handling Hierarchy 231
Reporting Errors 232
Generating Errors 233
Tracing Errors 234
Compiling and Running IDL Program Modules 235
Rules for Compiling IDL Program Modules Automatically 236
Structuring Program Files 236
Special Compilation Commands 237

Writing an IDL Graphics Display Program


Chapter Overview 239
The HistoImage Program 240
Writing the Procedure Definition Statement 240
Writing the Error Handling Code 242
Checking for Positional and Keyword Parameters 242
Checking for the Image Positional Parameter 243
Checking for Keyword Parameters 243
Loading the Program Colors 246
Preparing to Draw the Graphics 246
Calculating Graphic Positions in the Window 246
Changing Character Size According To Window Size 247
Calculating the Image Histogram 248
Drawing the Graphics 248
Drawing the Histogram Plot 248
Drawing the Color Bar 251
Drawing the Image Plot 251
Working Around a Printer Device Bug 252
Compiling and Testing the Program 252
Reviewing the HistoImage Program's Advantages 253
The HistoImage Program is Device Independent 255
Using HistoImage in a "Smart" Resizeable Graphics Window 256

Writing a Widget Program ................... ~~.lll~IIIlmlmlllmnmll~lnl~ll~lmlnllml 259


Chapter Overview 259
The Structure of Widget Programs 259
How Do Widget Programs Respond to Events? 261
Writing the Widget Definition Module 261
The Advantage of Mistakes 261
Typing the Code 262
Defining and Creating the Program's Widgets 263
Creating the Top-Level Base Widget 264
Creating Buttons for the Menu Bar 265
Creating the Graphics Window for the Program 265
Realizing the Widgets on the Display 266
Making the Draw Widget the Current Graphics Window 266
Displaying the Graphics in the Draw Widget Window 266
Storing Information Required to Run the Program 267
Using Pointer Variables 268
Using Pointers in the Info Structure 271
Using Widget User Values to Store Program Information
Practicing Good Memory Management 272
Creating the Event Loop and Registering the Program 275
Running the Program 276
Writing the Event Handler Modules 276
Common Fields in Event Structures 277
Event Handler Functions 278
Associating Event Handlers with Widgets 279
Writing the Quit Button Event Handler 279
Writing the Resizeable Graphics Window Event Handler 280
Writing the Cleanup Procedure 282
Running the Program 283
Recovering From Program Errors 283
Adding Color Protection 284
Saving the Color Vectors 285
Setting Up Keyboard Focus Events 285
Modifying the Histo-GUI-TLB-Events Event Handler 286
Buffering the Graphic Display for Smoother Output 287

Widget Programming Techniques ................... nmm188nm~lmlmll ....m..........293


Chapter Overview 293
Adding Image Processing Capability 294
Building the Pull-Down Menu Widgets 294
Writing the Event Handler for the Pull-Down Menu 295
Limitations of the Event Handler as Written 296
Implementing an Undo Capability 298
Adding Color Controls to the Program 301
Building the Pull-Down Menu Widgets 301
Writing the Drawing Colors Event Handler 301
Writing the Image Colors Event Handler 305
Communicating in XColors via Widget Events 306
Importance of Group Leaders 310
Adding File Output Functionality 312
Building the Pull-Down Menu Widgets 312
Writing the File Output Event Handler 313
Creating the GIF File 315
Creating the JPEG File 315
Creating the TIFF File 316
An Alternative Way of Creating GIF, JPEG, and TIFF Files 316
Creating the Postscript File 317
Adding Printer Functionality 320
Creating the Print Pull-Down Menu 320
Writing the Print Event Handler 320
Creating Dialog Form Widgets ....................III.I......I..I....I.....l........325
Chapter Overview 325
Creating a Modal Dialog Form Widget 325
A Blocking Widget Progran1 326
A Modal Widget Program 327
Writing a Modal Dialog Form Widget Definition Module 327
Defining a Modal Top-Level Base 328
Defining Other Widgets 329
Storing Collected Information in Modal Dialogs 330
Creating the Info Structure 331
Creating a Blocked Widget 331
Returning from the Block 331
Writing the Modal Dialog Event Handler Modules 332
Error Handling 333
Testing the Modal Dialog Form Widget Program 335
Using FSC-InputField for Program Input 335
Adding an Open Image Capability to the Histo-GUI Program 337
Adding an Open Button 337
Writing the Open Image Event Handler 338
Creating a Non-Modal Widget Dialog 340
Writing a Non-Modal Dialog Widget Definition Module 341
Notifying Widgets of Program Events 342
Writing the Non-Modal Dialog Event Handler Modules 343
Sending Events to Other Widgets 344
Testing the Readlmage Program 345
Writing the Read Image Event Handler 345

Creating Graphics Display Objects ........................ 349


Chapter Overview 349
A Quick Object Overview 349
The Idea of Data Encapsulation 350
Creating Objects 350
Invoking Object Methods 351
Destroying Objects 353
Creating a New Object Class 353
Defining the Object Class 353
Structure Review 353
Automatic Structure Definition 355
The BoxImage Class Definition 355
Creating Object Lifecycle Methods 356
Creating the Init Method 357
Creating the Cleanup Method 360
Creating the Specific Instance of the Object 360
Lifecycle Methods Must Be Defined When the Object is Created 361
Common Problems When Creating Objects 361
Initializing the Object Using Parameters 361
Creating the Display Method 362
Creating Methods to Set and Get Object Properties 364
Set Property Methods 365
Get Property Methods 367
Creating Methods to Work with Colors in Objects 370
Creating Methods to Extend Object Functionality 372
Object Inheritance 374
Defining the Subclass Object 375
Creating Subclass Lifecycle Methods 376
Attaching Methods to Superclass Objects 378
Oveniding Superclass Methods 378
Creating New Methods for the Subclass Object 380
Object Polymorphism 380
Testing the HistoImage Object 386

Appendix A: Widget Event Structures ..........................................389


Event Structure Definition 389
Common Field Definitions 389
Basic Widget Event Structures 389
Base Widget Event Structure 389
Button Widget Event Structure 390
Draw Widget Event Structure 390
Droplist Widget Event Structure 390
Label Widget Event Structure 390
List Widget Event Structure 390
Slider Widget Event Structure 391
Table Widget Event Structure 391
Character Insertion Event 391
String Insertion Event 391
Delete String Event 391
Text Selection Event 391
Cell Selection Event 391
Row Height Changed Event 392
Column Width Changed Event 392
Invalid Data Event 392
Text Widget Event Structure 392
Character Insertion Event 392
String Insertion Event 392
Delete String Event 392
Text Selection Event 392
Compound Widget Event Structures 393
CW-Animate Event Structure 393
CW-Arcbal Event Structure 393
CW-BGroup Event Structure 393
CW-Clr-Index Event Structure 393
CW-Color-Set Event Structure 393
CW-DefROI Event Structure 393
CW-Field Event Structure 393
CW-Form Event Structure 393

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

Appendix B: Data File Descriptions .................... . . . .B


..m
.B
l. . . . . . . 397
Appendix C: IDL Program Code ....................................................399
IDL Example Progams 399
BoxImageDefine Object Program 399
HDFRead Program 406
HDFWrite Program 407
HistoImage Program 409
Histo-GUI Program 412
HistoImageDefine Object Progam 423
OpenImage Program 428
ReadImage Program 431

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

Preface to the Edition


The problem with writing a book is that it tends to crystallize ideas that are current at
the time the book is written and set them-if not in stone-then certainly in type,
which is almost as hard a substance. But programming ideas are such fluid creatures
that an author finds himself looking at what he created with a skeptical eye about five
minutes after the book is sent off to the printer, and it just gets worse from there. After
almost three years you begin to think to yourself, "Was I in my right mind when I
wrote that nonsense!?"
Apparently so, because people keep buying the book and telling you how wonderful it
all is. But, finally, embarrassment overcomes flatte~yand you set out on that long,
lonely journey again.
The biggest change that has happened in the past three years is that most of us have
moved from 8-bit displays with 256 colors to 24-bit displays with millions of colors.
And while I have been able to tweak the book from one printing run to the next to
"kind o f ' keep up with the changes, I realized not too long ago that I was really writ-
ing my programs in a different way these days. So while the chapters on basic IDL
commands have not changed radically, the last five programming chapters have been
completely re-written to reflect my approach to writing programs that more often than
not live in a 24-bit world, but have to work in an 8-bit world, too.
What this book still lacks, at least to my mind, is a comprehensive review of objects
and object-oriented programming. I may add it some day, or I may write a separate
book on the subject. I seem to vacillate back and forth from one day to the next. I
know in my own programming I almost never write a program that doesn't include an
object of one kind or another. But there is great reluctance on the part of IDL users
who are not full-time programmers (the vast majority) to write object programs. And I
understand this reluctance, because it seems to take you out of the realm of "scientist"
and into the realm of "programmer". Many of us are naturally suspicious of identify-
ing with that crowd.
For those of you in this reluctant majority, the good news is you can still find 80 per-
cent of what you are ever going to want to do with IDL in the pages of this book. The
bad news is you are missing out on some techniques that could make a huge difference
in the quality of the programs you write. We will pull you along, eventually. There is
just too much of value there to ignore forever.
Thanks again to my ever persevering family, and especially to my wife, Carol, who
provides support in so many ways. And I couldn't make a living, let alone write a
book, without the people who attend my IDL programming courses each year. This
book wouldn't be possible without you. But my special thanks go to the folks who fre-
quent the IDL newsgroup (comp.lang.id1-pvwave). I have never met a friendlier, more
helpful community of users in my life. I greatly appreciate your humor, your insight,
and your unstinting devotion to sharing your IDL knowledge with beginners and
experts alike. This work wouldn't be nearly as much fun without you.
Now, if you will excuse me, I'm going to make myself a gin and tonic and sit out on
the back porch of our new house. I've been promising my wife all summer I would.
Fort Collins, Colorado
October, 2000

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

Philosophy Behind this Book


This book grew out of many years of teaching scientists and engineers to use and
program IDL (Interactive Data Language), most of the time while working for
Research Systems, the developers of IDL. As I answered question after question, I
realized most questions fall into a handful of broad categories. The truth is, most of us
want to do pretty much the same things with IDL. We want to analyze and display our
data, write efficient programs to solve our scientific problems, and-most of all-get
our work done quickly. What most of us do ~zotwant to do is read computer software
manuals.
IDL is a big software program that is getting bigger every day. It comes with an
impressive (and heavy) amount of documentation, which no one I know wants to read.
IDL can be intimidating even to experienced users, let alone to someone just starting
out to learn its mysteries. This book is meant to bring IDL under control. It is meant to
teach you 80 percent of what you absolutely need to know to work with IDL on a daily
basis. And, most importantly, it is meant to do that with examples that are easy to
understand. More than anything, it is a book that shows you how to work with IDL.
I envision the audience for this book as people just starting to use IDL and, more
particularly, people who have had to learn to use IDL on their own. Learning IDL well
is a long process. Most of us don't get the time in our jobs to do it properly. I wanted
to write a book that can put IDL in context for both groups of people. To that end, this
Getting Started

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!

Using this Book


I've tried to make each chapter in the book self-contained so that you can pick the
book up and turn to any chapter to learn what is most pressing for you. But I have also
arranged the chapters in the book in more or less the order I teach the material in IDL
programming courses. If you are just starting to learn IDL, it probably makes sense to
start at the beginning and work through the material in the order it is presented. The
material in the later programming chapters builds on concepts and techniques that
were developed in earlier chapters.

Required Version of IDL


I assume you will be using the latest version of IDL in this book. This was version
5.3.1 at the time this book was written. People with earlier versions of IDL will
probably be able to complete most of the programming examples in the book, but I
have made no effort to make the example programs that come with the book (see
below) compatible with earlier versions of the software. The IDL news group
(con~p.lnrzg.idl-pvwnve) is a good source of help if you are having difficulties
modifying your programs to run in earlier versions of IDL.
If you need to upgrade your software, you can find information about Research
Systems and their local IDL distributors, including how to upgrade your software, on
the Research Systems home page at this World Wide Web location:

Working with Colors in the IDb Session


The programming examples in the book have been written to work on either 8-bit or
24-bit color displays. If you ever have trouble getting your colors to be what you
expect them to be, try setting the Decomposed keyword of the Device command to 0.
This fixes about 80 percent of the color problems you are likely to encounter.

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

Style Conventions Used in the Book


I try to use a consistent style throughout the book so that I don't confuse you about the
function or purpose of the text. First, IDL commands that you should type at the IDL
command line or into an IDL editor window are always set in C o u r i e r type face:
Surf ace, data
Using this Book

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

Line Continuation Characters


The line continuation character in IDL is the dollar sign, $. This indicates that the IDL
command is continued on the next command line. (See the example above.) You will
see a lot of line continuation characters in the IDL commands in this book. My advice,
especially for those commands that you should type at the IDL command line, is to
ignore the line continuation characters (leave them out) and just keeping typing the
IDL command on the same command line. For example, you might type the command
above like this:
IDL> info = { r:r, g:g, b:b )
This will make it much easier for you to re-type the command if you make a typing
mistake or if you need to modify the command later.
There are occasions in this book when you should type the IDL commands exactly as
they appear in the book. I'll let you know when this is the case. This will almost
always be when I want you to type a FOR loop at the IDL command line. It is
extremely tricky to write multiple line commands at the IDL command line. You have
to make the IDL interpreter think the commands are a single command. This requires
specific use of line continuation ($) and multiple command (&) characters on the IDL
command line.

IDL Programs and Data Files Used in the Book


A number of IDL program files have been prepared for you to use with this book. The
IDL program files always have a .pro file extension.

Installing the Program Files


This book assumes that you will create a subdirectory named coyote and will put all
the program files there. Often the coyote subdirectory is a subdirectory of the main
IDL directory (i.e., the directory pointed to by the !Dir system variable inside of IDL),
but it does not have to be in this directory. You can create it anywhere. Your IDL lzorne
directory (see below) is another good place to put it. It is a good idea not to work
directly in the coyote subdirectory, but to copy program files from here into your
current working directory as you need them. This way, you always have the original
files available to you should you need to refer to them.
If you choose not to create a coyote subdirectory, then the programs used with the
book assume that the program files are located in your current directory. This
directory is normally the one in which you invoked IDL or, in the case of IDL on a PC
or Macintosh computer, the home directoly specified in the Startup didog of the
Preferences menu.

Determining Your IDL Home and Current Directory


If you are not sure what your IDL home directory is, start IDL and type these
commands as the first commands in your IDL session:
IDL> CD, Current=homeDirectory
IDL> Print, homeDirectory
Your current directory, which does not have to be your home directory, can be found at
any time during your IDL session with the same commands:
IDL> CD, Current=currentDirectory
IDL> Print, currentDirectory
It is probably a good idea not to use the main IDL directory (e.g., the IDL.53 directory
in the case of Windows IDL) as your working directory. It is too easy to delete a file
that may be important to you!
Using this Book

Downloading the Program Files Used with the Book


The program files are available via the Internet or via anonynlous ftp. If you are using
an Internet browser, follow the links starting at the Coyote's Guide to IDL
Pi~grnmnzingweb page. Its World Wide Web address is:

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.

Make Sure Your Coyote Directory is on the IDL Path


No matter where you create the coyote subdirectory or store the book files, you want
to make sure that directory is on your IDL path. The path is given by the !Pat11system
variable in IDL. You will learn more about this system variable later, but for now it is
enough to know that this is a list of subdirectories that IDL searches to resolve
commands it doesn't recognize. You can see your current IDL path by printing this
system variable:
IDL> Print, ! Path
Notice that if you are on a PC these subdirectories are separated by semi-colons; on a
Macintosh or VMS machine, they are separated by commas; and on a UNM machine,
they are separated by colons.
You want to add the coyote directory to the IDL path by typing the command Addpat11
from within the coyote directoly. (If you didn't create a coyote directory, you can type
the AddPnt11 command from within the directory that contains the downloaded book
files.) Use the CD command in IDL to change to the appropriate directory. For
example, if your coyote directory is a subdirectory of your IDL home directory and the
home directo~yis where you are currently located, you can add the coyote directory to
the IDL path by typing these commands:
IDL> CD, l coyote1
IDL> AddPath
You will want to move into the coyote directory (or the directory where your book
files are located) and run the AddPnth program each time you run IDL and work with
this book. You might think of adding the command to your IDL start-up file. Or, you
will want to add the coyote directory to your path permanently. (This is done
differently depending upon your operating system and IDL file configuration. See the
IDL on-line help for information on setting your !Path system variable. If you are
using the IDL Development Environment (IDLDE), you can set the !Path system
variable from the File->Preferences->Pat11tab.)

Copying the Data Files


The data files used with the book are already in the IDL distribution. If you prefer to
collect them in a single directory, you can copy them into any directory you like. The
coyote directory is a likely candidate. To do this, use the program CopyDntn, which is
one of the files you just downloaded. Go into the coyote directory (or the directory
where your book files are located) and just type CopyDntn, like this:
IDL> CD, I./coyote1
IDL> CopyData
Getting Started

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.

Obtaining Additional Help


If you have difficulty installing the program files or if you need help with some other
aspect of IDL programming, check the Coyote's Guide to IDL Progra~?zr?zing web
page. You will find information there about this book and about IDL programming in
general. If worst comes to worst, you will also find a form there that will allow you to
contact me directly. The World Wide Web address of Fanning Software Consulting
and the Coyote's Guide to IDL Progmrnrnir~gis:

Working with IDL Commands


This book is meant to be a doirzg book. I prefer that you read it sitting in front of a
computer rather than in front of a fire. I want you typing commands and seeing what
happens. For that reason, most of the commands in the first half of this book are meant
to be typed at the IDL command line. (If you wish to keep a record of your commands
as you type them, you can create a journal file to record them. See "Creating
Command Journals" on page 8 for additional information.)
IDL has evolved tremendously as a programming language in the 14 years I've been
working with it. But there is still a lot to be gained from learning how to use IDL from
the command line. In particular, you learn to figure things out, to try things, to
experiment with your data. I call it "learning by noodling around." I think it is one of
the best ways to use IDL.
So here is what you need to know to get started. First, you will see a lot of commands
like this in the book:
Contour, peak, lon, lat, XStyle=l, YStyle=l, / ~ o l l o w ,$
Levels=vals, C-Labels= [l,0,1,0 ,0 , 1,1, 01
It helps if you know what you are looking at.

Anatomy of an IDL Command


The word Corztour in the command above is the name of the IDL command or
program you wish to run. It must be spelled out in its entirety. Some command names
can be quite long, but no shortcuts are allowed. The words peak, 1011, and lat in this
command are variables. They are used to pass information into or out of the command
or program. The words XStyle, YStyle, Follo~), Levels, and C-Labels are keywords.
Keywords are by convention optional parameters to the command. Like variables,
they are used to pass information into and out of the command or IDL program.

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 Procedures and Functions


This particular command, the Contour command, is an IDL procedure. IDL
commands will be either procedures, like this one, or functions. Here is an example of
an IDL command, the BytScl command, that is a function:
Getting Started

scaled = BytScl (image, Top=199, Min=O , Max=maxValue)


Notice the difference between the Contour procedure command and the BytScl
function command. First of all, in the function command the positional parameters
and keywords are enclosed in parentheses. In the procedure command the parameters
and keywords are simply listed on the command line. But the most important
difference is that the function command explicitly returns a value, which is placed in a
variable on the left-hand side of the equal sign. This is the fundamental difference
between a function command and a procedure command in IDL.
Function commands always explicitly return a value that must be assigned to a
variable. The return value of a function may be any kind of IDL variable, including
scalars, vectors, and structures. In this case, the return value, scaled, is a byte array of
the same dimensions as the irnage positional parameter.
Sometimes you will see a function command and a procedure command written
together. For example, consider these two commands:
scaled = BytScl(image, Top=199, Min=O, Max=maxValue)
TV, scaled
The first command is a function command and the second command is a procedure
command that uses as its positional parameter the return value of the function. It
would not be unusual in IDL to see these two commands written like this:
TV, BytScl(image, Top=199, Min=O, Max=maxValue)
In this case, the BytScl command must be evaluated first and a value returned. That
return value is used as the positional parameter of the TV command.
It will probably take a while to become familiar enough with the various IDL
commands so that you know immediately which is a procedure and which is a
function, but try to remember this: if you are looking for one value from a command,
the command is probably a function. You will learn how to write IDL procedures and
functions later in this book.

Help with IDL Commands


IDL comes with an extensive on-line help system that can give you extremely helpful
information about IDL commands and their parameters. On-line help is accessed by
simply typing a question mark at the IDL command prompt or by selecting the Help
menu item from the IDL Development Environment pull-down menu. Almost all the
information in the IDL documentation set is available on-line. Check the docs
subdirectory of the main IDL directoly if you are having trouble locating what you
need. To access the IDL on-line help system, simply type a question mark at the IDL
prompt, like this:

Creating Command Journals


You may wish to save a journal or record of the commands you type at the IDL
command line. If so, you can create a jounzalfile. A journal file is an IDL batch file
(see "Writing an IDL Batch File" on page 207 for more details). A journal file is
opened in IDL by using the Jounfnl command and specifying the name of the file you
wish to open. The file will be a new file, open for writing. There is no way to append
to a journal file from the IDL command line. To open a journal file named, for
example, book-commnnds.pro, type this:
IDL> Journal, book-commands
All subsequent commands that you enter at the IDL command line will go into this
journal file.
Creating Va~.iables

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.

Variable Attributes Change Dynamically


One of the most powerful properties of IDL is that most of its commands can work on
data of any data type or organizational structure. This is so because IDL has the ability
to change the data type and organizational structure of a variable at run time. (Like
many powerful things in the world, this ability to change variable attributes
dynamically also has the potential to be extremely dangerous! You need to exercise
care to be sure you know what kind of data you are working with.) For example, it is
essentially pointless to initialize variables in IDL (Like you might in a Fortran or C
program), because the data type of that variable can so easily change. Consider this
example:
num = 3 ; Initialize NUM as a scalar integer.
num = num * 5.2 ; Variable NUM changes to a float!
Here the variable uunz is initialized as an integer and it gets dynamically changed to a
floating point value as the result of the mathematical operation and redefinition of its
Creating Variables

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

Be Careful With Integer Variables


I want to say just a quick word about integer variables so you stay out of trouble using
them. There are two common ways to get into trouble. The first involves integer math.
Consider this example:
result = 1 2 / 5
You may expect result to have a value of 2.4 and be a floating point value, but it is not.
It has a value of 2 and is an integer. Can you think of the reason why? Right, the two
numbers on the right-hand side of the equation are both integers. This is an example of
integer division. In this case, it probably won't be too hard to find your error, but
sometimes this problem can be more subtle.
Suppose, for example, you wanted to know the aspect ratio of an IDL graphics win-
dow. You know that the size of the window (in pixels or integer values) is stored in
two system variables. You might wtite your IDL code like this:

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

READF, lun, temp


data (count) = temp
count = count + 1
ENDWHILE

16-Bit Unsigned Integer UIntArr UIndGen


32-Bit (Long) Unsigned Integer ULonArr ULIndGen
64-Bit Unsigned Integer ULon64Arr UL64IndGen

Floating Point FltArr FIndGen


Double Precision Floating DblArr DIndGen
Complex ComplexArr CIndGen
Double Precision Complex DComplexArr DCIndGen

String StrA1-r SIndGen


Pointer PtrArr None
Object ObjArr None

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

Working with Vectors and Arrays


IDL is a programming language that excels at working with data which is organized in
vectors and assays. (A model for the first version of IDL was APL, an excellent
programming language for working with assays.) To be an effective IDL progsammer
you must know how to perform mathematical operations on assays. You will see many
examples of how to do this in this book, but I wanted to call your attention to a couple
of important points before you get started.

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

Using Array Subscripts


Suppose you want to add another element between the second and third elements in
the assay. Then you can use array subscsipting to help you do this. Assay subscripts
have their lower and upper bound separated by a colon. For example, you specify the
first three elements of the vector above like this:
IDL> P r i n t , v e c t o r ( 0 :2 )

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.

Accessing Elements in Arrays


Suppose you want to access the array element in the first column and second row of
the array you just created. (The element with a value of 4.) You can do so like this:
IDL> P r i n t , a r r a y [O, l ]
Notice that the subscripts expect the column number and then the row number. This is
just the opposite of what you might expect if you are used to working with matrices or
arrays in linear algebra. (Notice also that the column and row number are one less than
you really want. This is because array subscript indices start at 0, not l.)
Column-row subscripting came about as a result of the astronomical image data IDL
was originally written to handle. A row of data was a single scan line of an image.
Storing the data in this fashion made data manipulation fast and accurate. Deciding
whether a program uses column-row subscripting or row-column subscripting is
Working with ZDL Graphics Windows

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]

Extracting Vectors and Subarrays


IDL also makes it easy to extract vectors and subarrays from within arrays. For
example, consider this data array, which is filled with random data:
IDL> data = RandomU(seed, 10, 20)
If you want to pull out the columns 6-10 and rows 12-15, you can type this:
IDL> subarray = data [S:9, 11:141
If you want to plot, for example, just the 8th column of data, you can use the subscript
symbol *: to indicate all of the rows, like this:
IDL> Plot, data[7, *l
To create a vector of the 14th row, you can type:
IDL> vector = data [ * , l31
To create an array of the last 5 rows of the data, type:
IDL> subarray = data[*, 15:19]
IDL> Help, subarray
You see that the subarray is now a 10 column by 5 row array.
You can also use the symbol * to mean "all the rest" of the data. For example, to create
a subarray with the last 5 columns of the data, you can also type this:
IDL> subarray = data [S:*, *l
IDL> Help, subarray
You will learn more about arrays and how to work with them as you work through the
examples in this book.

Working with IDL Graphics Windows


You will learn more about IDL graphics windows as you work through the examples
in this book, but here are a couple of things it is good to know before you get started.

Creating Graphics Windows


First, a graphics window can be created directly with the Window command or
indirectly by issuing a graphics display command when no other window is open. For
example, you can create and open a window by typing this:
Getting Started

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.

Determining the Current Graphics Window


You now have at least three graphics windows open on the display. Only one of those
windows is the current graphics wilzdo~l.The current graphics window is the window
that will receive the output of a graphics command. The graphics window index
number of the current graphics window is always stored in the !D. Wiizdow system
variable. The value of !D. Wirzdotv will be -1 if there are no graphics windows created
and open to draw into.
This means, for example, that if you want to create a window and store its graphics
window index number so you can later delete it or make it the active window (see
details below), you can type something like this:
IDL> Window, / ~ r e e
IDL> thisWindowIndex = ! D . Window

Making a Graphics Window the Current Graphics Window


To make a window the current graphics window (so you can display graphics in it),
you use the WSet command and the window's graphics index number. For example,
suppose you want window 10 to be the current graphics window. You would type:

All subsequent graphics commands will be displayed in window 10.


Note also that a window becomes the current graphics window when it is created.
(This is not true of draw widget windows, however.) A window must be the current
graphics window in order to draw graphics into it.
Working tvitlt ZDL Graphics Windows

Deleting Graphics Windows


Graphics windows are deleted with the WDelete command and the window's graphics
index number. A window does not have to be the current graphics window to be
deleted. For example, to delete window 10, you can type this:
IDL> WDelete, 10
Here is a trick to delete all the graphics windows that exist on the display currently.
Type:
IDL> WHILE !D.Window NE -1 DO WDelete, !D.Window

Positioning and Sizing Graphics Windows


Windows are positioned and sized according to an internal algorithm when they are
created. You can position and size windows when you create them with keywords to
the Wil~Clo~,
command. For example, to create a window that is 200 pixels wide and
300 pixels high, use the XSize and YSize keywords, like this:
IDL> Window, 1, XSize=200, YSize=300
Windows are positioned on the display with respect to the upper left-hand corner of
the display in pixel or device coordinates. To position a window with its upper left-
hand corner at location (75,150) on the display, use the XPos and YPos keywords, like
this:
IDL> Window, 2, XPos=75, YPos=150

Bringing a Graphics Window Forward on the Display


Creating a graphics window gives that window the window focus and also makes it
the current graphics window. That is, the graphics window is now the active window
with respect to the Window Manager. (Just because a graphics window has the
window focus does not mean, in general, that it is the current graphics window.) To
type a command, you have to move the window focus back to the command window.
On some platforms, especially PCs, this causes the graphics window to pop back
behind other windows.
Or sometimes a graphics window just gets behind other windows on the display and
you would like to bring it forward so you can see it. To bring a window forward on the
display, without changing the window focus, use the WSlzow command and the
window's graphics index number, like this:
IDL> WShow, 1
Notice that your cursor and the window focus are still in the command window or
window where you are typing IDL commands.
Bringing a window forward with the WSlzo~) command does not make the window the
current graphics window. If you want to move a window forward and make it the
current graphics window (assuming it is not the current window already), then you
have to type both a WSlzo~vand a WSet command, like this:
IDL> WShow, 2
IDL> WSet, 2
Note that typing WSlzow without a parameter will bring the current graphics window
forward on the display. This is especially helpful when the current graphics window is
obscured and you want to move it forward on the display without removing the focus
from the IDL command window.
IDL> WShow
Getting Started

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.

Putting a Title on a Graphics Window


Sometimes you would like your graphics window to have a more descriptive title than
just its graphics window index number. You can use the Title keyword to put a title on
the window, like this:
IDL> Window, Title='Example IDL Graphics Commands'

Erasing a Graphics Window


To erase the current graphics window, you can use the Erase command, like this:
IDL> Erase
If you want to erase the current graphics display with a particular color index (or 24-
bit color value, if you are on a 24-bit display), you can use the Colol-keyword. For
example, you can erase the current graphics display with a charcoal gray color by
typing this:
IDL> Device, Decomposed=O
IDL> TVLCT, 70, 70, 70, 100
IDL> Erase, Color=100
To erase graphics windows that are not the current graphics window (i.e., the window
indicated by the system variable !D. Wilzdovv), you must first make the window the
current graphics window and then issue the Ernse command.
Simple Graphical Displays

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

Simple Graphical Displays in IDL


A simple graphical display in IDL is an example of what is called a raster graphic.
That is to say, using a Plot, or C01~tozll;or Szlgace command generates an algorithm
that lights up certain pixels in the display window. Such raster graphics have no
persistence. In other words, once IDL has displayed the graphic and lighted up certain
pixels it has no knowledge of what it has done. This means, for example, that IDL has
no way to respond to the user resizing the display window. The graphics display
cannot, in general, be redrawn in such circumstances except by re-issuing the graphics
command over again.
Nevertheless, raster graphics commands are widely used in IDL because they are fast
and simple to use. And, as you will see, many of the limitations often associated with
raster graphic commands can be overcome if you are careful in how you write IDL
programs that use raster graphic commands. This chapter introduces you to the
concepts you will need to know to wi-ite resizeable IDL graphics windows or produce
hardcopy output directly with raster graphic commands. The graphics commands in
this chapter are concerned with what Research Systems calls direct graplzics.
A different type of graphics option-named object graplzics by Research Systems-
was introduced in IDL 5.0. Object graphics commands are considerably harder to use,
but they also give you more power and flexibility in how your IDL graphic programs
Sii~~ple
Grnplzical 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.

Creating Line Plots


The simplest way to generate a line plot is to plot a vector. You can use the LoadDatcr
command to open the Tilne Series Data data set. The LoaclDnta command is one of
the IDL programs that came with this book. (See "IDL Programs and Data Files Used
in the Book" on page 4 for more information.) It is used to load the data sets used in
the programming examples in this book. To see the data sets available to you, type
this:
IDL> curve = ~ o a d ~ a t a o
If you forgot to include the parentheses when you issued the LoadData command
above, you may need to re-compile the LoadData program before it will work prop-
erly. The reason for this is that IDL now thinks "loaddata" is a variable and acts
accordingly when it sees it in a command line. Re-compiling the function will get the
"loaddata" name on IDL's list of function names where it belongs. Type this:
IDL> . Compile LoadData
The Tilne Series Data data set is the first data set on the LoadData list. Click on it. The
data is now loaded into the variable cul-ve. Another way to select this first data set is to
call LoadData like this:
IDL> curve = LoadData (1)
To see how the variable curve is defined, type this:
IDL> Help, curve
CURVE FLOAT = Array [loll

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.

Customizing Graphics Plots


This is a simple line plot, without much information associated with it, aside from the
data itself. But there are many ways to customize and annotate line plots. The Plot
command can be modified with over 50 different keywords. Here are a few of the
things you might want to do:
modify line styles or thicknesses
use symbols with or without lines between them
create your own plotting symbols
add color in your plot to highlight important features
change the length of tick marks or the number of tick intervals
use logarithmic scaling on the plot axes
change the data range to plot just the subset of the data you are interested in
eliminate axes or otherwise change the style of the plot

Modifying Line Styles and Thicknesses


Suppose, for example, you wanted to plot your data with different line styles. For a
line with a long dash line style, you can try this:
IDL> Plot, time, curve, LineStyle=5
Different line styles are selected for line plots by using the LirzeStyle keyword with
one of the index numbers shown in Table 3. For example, if you wished to plot the
curve with dashed line, you set the Linestyle keyword to a value of 2, like this:
IDL> Plot, time, curve, LineStyle=2
Cztstontiziirg Grcrplzics Plots

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

Displaying Data with Symbols Instead of Lines


Suppose you wanted to plot your data with symbols instead of lines. Like the
Linestyle keyword, similar index numbers exist to allow you to choose different
symbols for your line plots. Table 4 shows you the possible index numbers you can
use with the PSyni (Plotting Syr?ibol)keyword. For example, you can draw the plot
with asterisks by setting the PSyr?z keyword to 2, like this:
IDL> Plot, time, curve, PSym=2
Your output should look similar to the output in Figure 3.

Figure 3: A line plot with the data plotted as symbols instead of as a line.

Displaying Data with Lines and Symbols


You can connect your plot symbols with lines by using a negative value for the PSym
keyword. For example, to plot your data with triangles connected by a solid line, type
D L > Plot, time, curve, P S p = - 5
Siirtple Graphical Displays

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.

IDL> Plot, time, curve, PSym=-5, ~ y m ~ i z e = 2 . 0

4 Diamond
5 Tiiangle
6 Square
7 X
8 User defined (with the UserSyn? procedure).

9 This index is not used for anything!


10 Data is plotted in histogram mode.

-PSyn? Negative values of PSYIIIconnect symbols with lines.

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.

Creating Your Own Plotting Symbols


If you feel creative, you can even create your own plotting symbols. The Usedyn?
command is used for this purpose. After you have created your special symbol, you
select it by setting the P S y m keyword equal to 8. Here is an example that creates an
star as a symbol. The vectors x and y define the vertices of the star as offsets from the
origin at (0,O). You can create a filled symbol by setting the Fill keyword on the
UserSyrn command.
IDL> X [O.O, 0.5, -0.8, 0.8, -0.5, 0.01
=
IDL> y = [1.0, -0.8, 0.3, 0.3, -0.8, 1.01
IDL> TvLCT, 255, 255, 0, 150
IDL> UserSym, X, y , Color=150, /Fill
IDL> Device, Decomposed=O
IDL> Plot, time, curve, PSym=-8, SymSize=2.0
Your output should look similar to the output in Figure 4.

Drawing Line Plots in Color


You can draw your line plots in different colors. (Colors are discussed in detail in
"Working with Colors in IDL" on page 81. For now, just type the TvLCT command
below. You will learn exactly what the command means later. Basically, you are
loading three color vectors that describe the red, green, and blue components of the
Cztsto~?zizi~zg
Gr~aplticsPlots

Figzcre 4: A plot with syntbols created by the UsesSynt procedzcre.

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

Limiting the Range of Line Plots


Not all of your data must be plotted on a line plot. You can limit the amount of data
you plot with keywords. For example, to plot just the data that falls between 2 and 4
on the X axis, type:
IDL> Plot, time, curve, XRange= [2,4]
Or, to plot just the portion of the data that has Y values between 10 and 20 and X
values that fall between 2 and 4, type:
IDL> Plot, time, curve, ~ ~ a n g [10,20]
e= , XRange= [2,41
You can reverse the direction of the data by specifying the data range with keywords.
For example, to draw the plot with the Y axis reversed, type this:
IDL> plot, time, curve, YRange= [30 01

Your output should look similar to the illustration in Figure 5, below.


Siitzple Graphical Displays

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.

Changing the Style of Line Plots


It is possible to change many chasacteristics of your plots, including the way they
look. For example, you may not case for the box style of the line plots. If not, you can
change the plot characteristics with the [XYZIStyle keywords. The values you can use
with these keywords to change line plot styles is shown in Table 5. For example, to
eliminate box axes and have just one X axis and one Y axis, type:
IDL> Plot, time, curve, XStyle=8, YStyle=8

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

Plotting Multiple Data Sets on Line Plots


You are not restricted to just a single data set on a line plot. IDL allows you to plot as
many data sets as you like on the same set of axes. The OPlot command is used for
this purpose. Type the following commands. Your output will look like the illustration
in Figure 7.
IDL> Plot, curve
IDL> OPlot, curve/2.0, LineStyle=l
IDL> OPlot , curve/5.0, LineStyle=2

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

Plotting Data on Multiple Axes


Sometimes you wish to have two or more data sets on the same line plot, but you want
the data sets to use different Y axes. It is easy to establish as many axes as you need
with the Axis command. The key to using the Axis command is to use the Save
keyword to save the proper plot scaling parameters (i.e., those stored in the !X.S and
!Y;S system variables) for subsequent plotting calls.
For example, here one plot is drawn and the Axis command along with the Save
keyword is used to establish a second Y axis. The curve in the OPlot command will
use the scaling factors saved by the Axis command to determine its placement on the
plot. The proper commands are these:
IDL> Plot, curve, YStyle=8, YTitle= 'Solid Line' , $
Position = [0.15, 0.15, 0.85, 0.951
IDL> Axis, YAxis=l, YRange= [O,2001 , /save, $
YTitle='Dashed Line'
IDL> OPlot , curve*5, LineStyle=2
The Positiorl keyword is used to position the first plot on the page. To learn more
about the Positioi~keyword, see "Positioning Graphic Output in the Display Window"
on page 44. Your output will look like the illustration in Figure 8.

Creating Surface Plots


Any two-dimensional data set can be displayed as a surface (with automatic hidden-
line removal) in IDL with a single Surface command. First, you must open a data file.
Use the LoadData command to open the Elevation Data data set, like this:
IDL> peak = LoadData (2)
You can see that this is a 41-by-41 floating point array by typing the Help command,
like this:
IDL> Help, peak
This data can be viewed as a surface with a single command:
IDL> Surface, peak, CharSize=l.5
Your output should look similar to the illustration in Figure 9.
Notice that if the Sutj4ace command is given just a single array as an argument it plots
the array as a function of the number of elements (41 in the X and Y directions, in this
case) in the array. (You used the CltarSize keyword to increase the character size so
you can see this easily.) But, as you did before with the Plot command, you can
specify values for the X and Y axes that may have some physical meaning for you. For
example, the X and Y values may be longitude and latitude coordinates. Here we
make a latitude vector that extends from 24 to 48 degrees of latitude and a longitude
vector that extends from -122 to -72 degrees of longitude:
IDL> lat = FIndGen(41) * (24.0/40) + 24
IDL> lon = FindGen(41) * (50.0/40) - 122
Sinzple Graphical Displays

Figure 9: A basic surface plot of elevatiolz data.

IDL> Surface, peak, lon, lat, XTitle='Longitudel, $


YTitle='Latitudel, ZTitle='Elevationl, Charsize=1.5
Your output will look like the illustration in Figure 10.

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.

Customizing Surface Plots


There are over 7 0 different keywords that can modify the appearance of a surface plot.
In fact, many of these keywords you already learned for the Plot command. For
example, you saw in the code above that the same title keywords can be used to
annotate the axes. Notice, however, that when you add a Title keyword that the title is
rotated so that it is always in the XZ plane of the surface plot. For example, type this:
IDL> Surface, peak, lon, lat, ~ ~ i t l e = ~ L o n g i t u d$e ~ ,
~ ~ i t l e = ~ L a t i t u d eTitle='Mt.
', Elbertl, Charsize=1.5
This is not always what you want. If you want the plot title to be in a plane parallel to
the display, you will have to draw the surface with the Surfnce command, and the title
with the XYOutS command. (There is detailed information on the XYOutS command
on page 5 1.) For example, like this:
IDL> Surface, peak, lon, lat, X~itle='Longitude',$
YTitle='Latitude', Charsize=1.5
IDL> XYOutS, 0.5, 0.90, /Normal, Size=2.0, Align=0.5, $
'Mt. Elbertl

Rotating Surface Plots


You may want to rotate the angle at which you view a surface plot. A surface plot can
be rotated about the X axis with the Ax keyword and about the Z axis with the Az
keyword. Rotations are expressed in degrees counterclockwise as you look from
positive values on the axis towards the origin. The Az and h keywords default to 30
degrees if they are omitted. For example, to rotate the surface about the Z axis by 6 0
degrees and about the X axis by 35 degrees, type:
IDL> Surf ace, peak, lon, lat, Az=60, Ax=35, Charsize=l.5
Your output should look like the illustration in Figure 12.
Although direct graphics surface commands are still useful in IDL, many
programmers are finding that surfaces rendered with the object graphics class library
Siittple Graplzical Displays

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.

Adding Color to a Surface Plot


Sometimes you want to add color to the surface plot to emphasize certain features. It is
easy to add color, using many of the same keywords that you used to add color to line
plots. (Colors are discussed in detail in "Working with Colors in IDL" on page 8 1. For
now, just type the TvLCT command below. You will learn exactly what the command
means later. Basically, you are loading three color vectors that describe the red, green,
and blue components of the color triples that describe the charcoal, yellow, and green
colors.) For example, to create a yellow surface plot on a charcoal background, type:
IDL> TvLCT, [70, 255, 01, 170, 255, 2551, [70, 0, 01, 1
IDL> Device, Decomposed=O
IDL> Surf ace, peak, Color=2, Background=l
If you wanted the underpart of the surface to be a different color than the top, say
green, you can use the Bottoln keyword like this:
IDL> Surface, peak, Color=2, Background=l, Bottom=3
If you wanted the axes to be drawn in a different color-green, for example-than the
surface, you have to enter two commands. The first command uses the NoData
keyword so that just the axes are drawn, while the second command draws the surface
itself with the axes turned off. See Table 5 on page 26 for a list of values you can use
with the [XYZIStyle keywords and what they mean.
IDL> Surface, peak, Color=3, /NoData
IDL> Surface, peak, / ~ o ~ r a s eColor=2,
, Bottom=l, $
XStyle=4, YStyle=4, ZStyle=4
Custontiziltg Srirface Plots

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)

Modifying the Appearance of a Surface Plot


There are a number of keywords you can use to modify the appearance or style of a
surface plot. For example, you can display the surface with a skirt. Use the Skirt
keyword to indicate the value where the skirt should be drawn. For example, try these
commands:
IDL> Surface, peak, Skirt=O
IDL> Surface, peak, Skirt=5OO, Az=60
The output of the first command above should look similar to the illustration in
Figure 14.

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

Creating Shaded Surface Plots


It is equally easy to create shaded surface plots of your data. To create a shaded
surface plot using a Gouraud light source shading algorithm, type:
IDL> Shade-Surf, peak
The Slznde-Sutfcommand accepts most of the keywords accepted by the Surjnce
command. So, for example, if you want to rotate the shaded surface, you can type this:
Creatiitg Shaded Szirface Plots

IDL> Shade-S u r f , peak, l o n , l a t , Az=45, A x = 3 0


Your output should look similar to the illustration in Figure 15.

Figure 15: A shaded surface plot of the elevatioiz data usiizg a Gourazcd light sozcrce
shading algoritlzm.

Changing the Shading Parameters


The shading parameters used by the Slzade-Sutfcommand can be changed with the
Set-Shading command. For example, to change the light source from the default of
[0,0,1],in which the light rays are parallel to the Z axis, to [1,0,0],in which the light
rays are parallel to the X axis, type:
IDL> Set-Shading, Light= [ l , 0 , 01
IDL> Shade-Surf , peak
You can also indicate which color table indices should be used from the color table for
shading purposes. For example, suppose you wanted to load the Red Temperature
color table (color table 3) into color indices 100 to 199 and use these for the shading
colors. You could type this:
IDL> LoadCT, 3 , NColors=100, Bottom=100
IDL> Set Shading, Values= [100, 1 9 9 1
IDL> shade-S u r f , peak
Be sure to set the light source location and color value parameters back to their
original values, or you will be confused as you move on with the exercises.
IDL> LoadCT, 5
IDL> set-Shading, Light= [ 0 , 0, l ], Values= [ o , ! D . able-size-l]

Using Another Data Set For the Shading Values


Like with the Su~facecommand, another data set can provide the shading values with
which to color the first. As before, you use the Shades keyword to specify the color
index of each point on the surface. The shading of each pixel is interpolated from the
surrounding data set values. For example, here is what the silow variable looks like as
a shaded surface. Type:
IDL> Shade-Surf, snow
Now use this data set to shade the original elevation data. Type:
Silnple Graphical Displays

IDL> Shade-Surf, peak, Shades=BytScl(snow,Top=!D.Table-Size)


Your output should look like the illustration in Figure 16.

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

Creating Contour Plots


Any two-dimensional data set can be displayed in IDL as a contour plot with a single
Corztot~rcommand. You can use the peak variable if you already have it defined in this
IDL session. If not, use the LoadData command to load the Elevatior?Data data set,
type this:
IDL> peak = LoadData (2)
IDL> Help, peak
This data can be viewed as a contour plot with a single command:
IDL> Contour, peak, CharSize=1.5
Notice that if the Coiztour command is given just the single 2D array as an argument it
plots the data set as a function of the number of elements (41 in each direction) in the
2D array. But, as you did before with the Surface command, you can specify values
for the X and Y axes that may have physical meaning for you. For example, you can
use the longitudinal and latitudinal vectors from before, like this:
Creating Contolir Plots

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.

IDL> lat = FIndGen(41) * (24./ 4 0 ) + 24


IDL> lon = FindGen(41) * 5 0 . 0 / 4 0 - 122
IDL> Contour, peak, lon, lat, XTitle='Longitudel, $
YTitle='Latitudel
Notice that the axes are being autoscaled. You can tell this in a couple of ways. First,
the contour lines do not go to the edges of the contour plot, and you can see that the
labels on the axes are not the same as the minimum and maximum values of the lor~
and lnt vectors.
IDL> Print, Min(1on), Max(1on)
IDLz Print, Min (lat), Max (lat)
To prevent autoscaling of the axes, set the XStyle and YStyle keywords, like this:
IDL> Contour, peak, lon, lat, XTitle='Longitudel, $
YTitle='Latitudel, XStyle=l, YStyle=l
You see the result of this command in the illustration in Figure 18.

-120 -l 10 -100 -90 -80


Longitude

Figure 18: A contozcrplot with axes representing physically meanirzgful quantities.

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.

Selecting Contour Levels


By default, IDL selects six regularly spaced contour intervals (five contour lines) to
contour, but you can change this in several ways. For example, you can tell IDL how
many contour levels to draw with the NLevels keyword. IDL will calculate that
number of regularly spaced contour intervals. For example, to contour a data set with
12 regularly spaced contours, type:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, / ~ o l l o w ,$
NLevels=12
Your output should look similar to the illustration in Figure 19. You can choose up to
29 contour levels.

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)

Modifying a Contour Plot


A contour plot can be modified with the same keywords you used for the Plot and
Suiface commands. But there are also a number of keywords that apply only to the
Contour command. These most often modify the contour lines themselves.
For example, to annotate a contour plot with axis titles, you can type:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, /Follow,
XTitle='Longitudel,YTitle='Latitudet, $
CharSize=1.5, Title='Study Area 13F8g1, NLevels=lO
But, you can also specify annotations on the contour lines themselves by using the
C-Anizotatior~keyword. For example, you could label each contour line with a string
label by typing:
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, / ~ o l l o w ,$
XTitle=lLongitudel,~~itle='Latitude', $
CharSize=1.5, Title='Study Area 13F89', $
C-Ann~tation=[~Low~,'Middle','High'], $
Levels=[200, 500, 8001
Your output should look similar to the illustration in Figure 20.

Changing the Appearance of a Contour Plot


There are any number of ways to modify the appearance of a contour plot. Here are a
few of them. One chasacteristic you can change is the style of the contour lines. (See
Silnple Graplzical Displays

Study Area 13F89


4 0 1 " ' ' ' ' ' ~ ~ ~ ~

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.

Figure 22: The Do~vizhillkeyword is set to show the do~v~thill


direction of the con-
tour plot.

Adding Color to a Contour Plot


There are many ways to add color to a contour plot. (Colors are discussed in detail in
"Working with Colors in IDL" on page 81. For now, just type the TvLCT command
below. You will learn exactly what the command means later. Basically, you are
loading three color vectors that describe the red, green, and blue components of the
color triples that describe the charcoal, yellow, and green colors.) For example, if you
want a yellow contour plot on a charcoal background, you can type:
IDL> TvLCT, [70, 2551, 170, 2551, [70, 01, 1
IDL> Device, Decomposed=O
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, $
NLevels=lO, Color=2, Background=l, / ~ o l l o w
You can also color the individual contour lines using the C-Colors keyword. For
example, if you wanted the contour lines in the plot above to be drawn in green, you
can type:
IDL> TvLCT, 0, 255, 0, 3
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, / ~ o l l o w ,$
NLevels=lO, Color=2, ~ackground=l,C_Colors=3
The C-Colors keyword can also be expressed as a vector of color table indices, which
will be used in a cyclical fashion to draw the contour lines. For example, you can use
Siirrple Graphical Displays

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

Creating a Filled Contour Plot


Sometimes you don't just want to see contour lines, but you would like to see a filled
contour plot. To create a filled contour plot, just use the Fill keyword. Here you first
load 12 colors into the color table for the fill colors. The color indices will be specified
with the C-Colors keyword. For example, type:
IDL> LoadCT, 0
IDL> LoadCT, 4, NColors=12, Bottom=l
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, ill, $
NLevels=12, / ~ o l l o w ,C-Colors=Indgen(l2)+1
Although it is not obvious from the display, there are problems with doing filled
contours in this manner. In fact, there is a "hole" in this contour plot that is filled with
the background color. You can see it more easily if you reverse the background and
plotting colors. (This is what is done in Postscript, as a matter of fact, and what causes
many IDL programmers to pull their hair in frustration.)

Figure 23: The coiztourplot showing the "hole" at the lowest corztour level.

IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, ill, $


Creating n Filled Coiztoztr Plot

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.

Positioning Graphic Output in the Display Window


IDL has several ways to position line plots, surface plots, contour plots and other
graphical plots (map projections, for example) in the display window. To understand
how IDL positions graphics, however, it is important to understand a few definitions.
The graphic positiorl is that location in the display window that is enclosed by the
axes of the plot. The graphic position does not include axes labels, titles, or other
annotation (see Figure 26 below). The graplzic region is that portion of the display
window that includes the graphic position, but it also includes space around the
graphic position for such things as axes labels, plot and axes titles, etc. The graphic
margin is defined as the area in the display window that is rzot the graphic position.
The graphic position may be set by !l?Positiolz system variable or by the Positiol?
keyword to the Plot, Surface, Corztour, or any of the other IDL graphics commands.
Positiorzing Gt.aplzic Ozctpzct in tlze Display Window

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

Setting the Graphic Margins


The graphic margins can be set with the [XYZIM~J-gin keywords in the graphics
command or by setting the Margirz field of the !X, !X and !Z system variables. What is
unusual about the graphic margins is that the units are specified in terms of character
size. The X margin is set by a two-element vector specifying the left and right offsets,
respectively, of the axes from the edge of the display. The Y margin specifies the
bottom and top offsets in a similar way. The default margins are 10 and 3 for the X
axis and 4 and 2 for the Y axis. To see what the current character sizes are in device or
pixel units, type:
IDL> Print, !D.X-Ch-Size, !D.Y-Ch-Size
On a Macintosh, for example, the default character size is 6 pixels in X and 9 pixels in
Y. Thus, a contour plot is drawn with (6 times 10) 60 pixels on its left edge of the plot
and (6 times 3) 18 pixels on its right edge. If the ChalSize keyword is set to 2 on a
Contour command, for example, then there will be 120 pixels on the left edge of the
plot and 36 on the right edge.
For example, to change the graphic margins to three default character units all the way
around the plot, you would type:
IDL> Plot, time, curve, margin= [3,3], margin= [3,3]
Notice, however, that the plot looks very different if you also change the character
size, since graphic margins are specified in terms of character size. Try this:
IDL> !X.Margin = [3,3]
DL> !Y.Margin = [3,31
IDL> Contour, peak, CharSize=2 . 5
IDL> Contour, peak, CharSize=l . S
If you play around a bit with other character sizes, you can see that at larger character
sizes, the characters get very large and the graphics portion of the plot gets very small.
This is not always what you want.
Be sure to set the graphic margins back to their default values before you move on.
Type:

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.

Setting the Graphic Position


Setting the graphic position requires setting a four-element vector giving, in order, the
coordinates [XO, YO, XI, Y l ] of the lower-left and upper-right corners of the graphic in
the display window. These coordinates are always expressed in normalized units
ranging from 0 to 1 (i.e., 0 is always either the left or the bottom of the display
window, and 1 is always either the right or top of the display window).
For example, suppose you wanted your graphic output to be displayed in the top half
of the display window. Then you might set the !P.Positioiz system variable and
display your graphic like this:
IDL> !P.Position = L0.1, 0.5, 0.9, 0.91
IDL> Plot, time, curve
Positiorziizg Graphic Ozitplct in the Display Wii~do~v

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

Setting the Graphic Region


The graphic region is specified in normalized coordinates in the same way as the
graphic position, and can be specified by setting the !PRegiorz system variable. Since
there is not an equivalent keyword that can be set on a graphics command, setting the
graphics region is often less convenient than setting the graphics position. Be sure you
reset the system variable if you want subsequent plots to use the whole display
window normally. For example, to display a plot in the upper two-thirds of your
display window, type:
IDL> !P.Region = [0.1, 0.33, 0.9, 0.91
IDL> Plot, time, curve
To reset the !PRegiorz system variable so that subsequent plots fill the window
normally, type:
IDL> ! P.Region = 0

Creating Multiple Graphics Plots


As you can see, multiple plots can be positioned on the display using the graphic
position and graphic region system variables and keywords discussed above (as long
as the second and all subsequent plots use the NoErase keyword). But it is much easier
to use the !RMulti system variable to create multiple plots on the display. !RMulti is
defined as a five element vector as follows:
!P.Multi[O] The first element of !PMulti contains the number of graphics
plots remniizing to plot on the display or Postscript page.
This is a little non-intuitive, but you will see how it is used in
a moment. It is normally set to 0, meaning that as there are no
more graphics plots remaining to be output on the display, the
next graphic command will erase the display and start with
the first of the multiple graphics plots.
This element specifies the number of graphics columns on
the page.
This element specifies the number of graphics rows on the
page.
This element specifies the number of graphics plots stacked
in the Z direction. (This applies only if you have established a
3D coordinate system.)
Sil?zple Graphical Displays

!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.

Leaving Room for Titles with Multiple Graphics Plots


When IDL calculates the position of the graphics plots, it uses the entire window to
determine how big each plot should be. But sometimes you would like to have extra
Positiortiitg Graphic Outpzct in the Display Window

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.

Four Graphics Plots

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

Non-SymmetricalArrangements with !P.Multi


Plotting with !l?Multi does not have to be symmetrical on the display. For example,
suppose you wanted the surface and shaded surface plot in the left-hand side of the
display window one above the other, And you wanted the right-hand side of the page
to be filled with a contour plot of the same data. You can type this code:
IDL> !P.Multi = [O, 2, 2, 0, 11
IDL> !Y.OMargin= [0,O]
IDL> Surface, peak, lon, lat
IDL> Shade-Surf , peak, lon, lat
IDL> !P.Multi = [l, 2, 1, 0, 01
IDL> Contour, peak, lon, lat, XStyle=l, YStyle=l, NLevels=lO
The first !F!Multi command sets up a two-column by two-row configuration and the
first two of these plots are drawn. The second !l?Multi command sets up a two-
column by one-row confiouration. But notice that !l?Mulfi[O] is set to l. This results
P
in the contour plot going Into the second position in the window instead of the first.
The result is shown in Figure 29, below.

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:

Adding Text to Graphical Displays


Plot annotations and other text can be added to graphical displays in a variety of ways,
the most common of which is via keywords to graphical display commands. The text
that is added can come in any of three font "flavors" or styles, if you like: vectol.forzts
(sometimes called software or Hershey fonts), true-typefonts, and hnrdtvnre fonts.
The type of font is selected by setting the !Z?Fo~ztsystem variable or by setting the
Font keyword on a graphical output command according to Table 6, below.

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

IDL> Plot, time, curve, Font=l, XTitle='Timel,$


YTitle='Signall,Title='Experiment 35F3a1
True-type fonts can be selected with the Device command using the Set-Font and
'IT_Forzr keywords, like this:
IDL> Device, S e t - F ~ n t = ~ C o u r i e r ~/TT
, Font
IDL> Plot, time, curve, Font=l, ~ ~ i t i eTime = , $
YTitle='Signal', Title='Experirnent 35F3a1
To learn more about true-type fonts in IDL, use your on-line help system like this:
IDL> ? fonts
Hardware fonts are selected by setting the !P.Font system variable or the Forzt
keyword to 0. Normally hardware fonts are not used on the display, but are used when
output is sent to a hardcopy output device such as a Postscript printer. Unfortunately,
hardware fonts do not rotate well in 3D space and so were not normally used with 3D
commands like the Surface command.
IDL> Plot, time, curve, Font=O, XTitle='Timel, $
YTitle='Signall,Title='Experirnent 35F3a1

Finding the Names of Available Fonts


You can find the names of available hardware fonts by using the Device command
like this:
IDL> Device, Font='*', Get-FontNames=fontnames
IDL> For j=0,N-Elements (fontnames) -l DO print, fontnames [j1
The names of true-type fonts are found in a similar way, except that the TT'Folzr
keyword is used to select just the available true-type fonts on your system. (You can
add your own true-type fonts to the four families supplied with IDL. See the IDL on-
line help to find out how.)
IDL> ~ e v i c e ,Font=I*l, Get-FontNames=fontnames, / T T - F O ~ ~
IDL> For j= O ,N-Elements (fontnames) -l DO print, f ontnames [j]
The names of available vector fonts are given in Table 7 on page 53.

Adding Text with the XYOutS Command


A very important command in IDL is the XYOutS command ("at an XY location,
OUTput a String"). It is used to place a text string at a specific location on the display.
(The first positional parameter to XYOutS is the X location and the second positional
parameter is the Y location). For example, to add a larger title to a line plot, you can
type commands like this:
IDL> Plot, time, curve, Position=[0.15, 0.15, 0.95, 0.851
IDL> XYOutS, 0.5, 3 2 , 'Results: Experiment 3 5 F 3 a 1 , Size=2.0
Notice that you specified the X and Y location with data coordinates. Also notice that
the Y coordinate is outside the boundary of the plot. By default, the XYOutS procedure
uses the data coordinate system, but the device and rzorr~zalizedcoordinate systems
can also be used if the appropriate keywords are specified.
(The data coordinate system is, naturally, described by the data itself. Device
coordinates are sometimes called pixel coordinates and the device coordinate system
is often used with images. A normalized coordinate system goes from 0 to 1 in each
direction. Normalized coordinates are often used when you want device-independent
graphics output.)
Adding Text to Graphical 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

!7 Complex Greek !l6 Cylillic

!8 Complex Italian !17 Triplex Roman

!9 Math Font !18 Ti-iplex Italian

!10 Special Characters !20 Miscellaneous

!l1 Gothic English !X Revert to entry font

Table 7: The Hershey fonts with correspo~tdirzgindex rzuntber used to select


them within ZDL.

Using XYOutS with Vector Fonts


The XYOcltS command can be used with either vector, true-type, or hardware fonts.
Simply set the appropriate Font keyword value as described above. The discussion
here concerns itself with vector fonts, since that is the font system that is most often
used in direct graphics commands. A list of the available vector fonts along with the
index numbers you use to specify the specific font is given in Table 7.
The major advantage of vector or Hershey fonts, is that they are platform independent
and can be scaled and rotated in 3 0 space. For example, you can write the plot title
above in Triplex Roman characters by typing:
IDL> Plot, time, curve, Position=[0.15, 0.15, 0.95, 0.851
IDL> XYOutS, 0.2, 0.92, '!17Results: Experiment 35F3a!X1, $
Size=2.0, /Normal
The Triplex Roman characters were specified with the !l7 escape sequence. The !X at
the end of the title string caused the font to revert back to the Simplex Roman font that
was in effect before you changed it to Triplex Roman. This reversion step is important
because otherwise the default font gets set to Triplex Roman and all subsequent
strings will be set with Triplex Roman characters. For example, try using a Greek
character as the X title of the plot and try writing the plot title like this:
IDL> Plot, time, curve, XTitle=' ! 7 w 1 , $
Position=[0.15, 0.15, 0.95, 0.851
IDL> XYOutS, 0.2, 0.92, 'Experiment 35F3X1, Size=2.0, /Normal
You can see the results in Figure 30. Notice that now, even though you didn't specify
that it should be so, the title is written in Greek characters. The only way to make it
Sii~zpleGraphical Displays

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.

Table 8: The embedded commands that cart accomplish textpositioitiizg in


strings. These are often used to produce subscripts and superscripts Lz
plot titles, for example.

For example, an equation can be written like this:


IDL> XYOutS, 0.5, 0.5, / ~ o r m a l ,Alignment=0.5, Size=3.0, $
'Y = 3X!U2!N + 5x + 3'
Sintple Graphical Displays

Adding Lines and Symbols to Graphical Displays


Another useful routine for annotating graphical displays is the PlotS command, which
is used to draw symbols or lines on the graphic display. The PlotS command works in
either two- or three-dimensional space.
To draw a line with the PlotS procedure, simply provide it with a vector of X and Y
values that describe XY point pairs that should be connected. For example, to draw a
baseline on the line plot from the point (0, 15) to the point (6, 15), type:
IDL> Window, XSize=500, YSize=400
IDL> Plot, time, curve
IDL> xvalues = [O, 61
IDL> yvalues = [15, 151
IDL> PlotS, xvalues, yvalues, ~ i n e ~ t y l e = 2
Your output should look similar to the output in Figure 3 1, 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.

Adding Color to Your Graphical Displays


Another useful way to annotate your gsaphical displays is to use color. The Polyfill
command is a low-level graphic display command that will fill any arbitrarily shaped
polygon (which can be specified in either two or three dimensions) with a particular
color or pattern. For example, you can use Polyfill to fill the box in the line plot above
with a red color:
LDL> TvLCT, 255, 0, 0, 4
IDL> Erase, Color=l
IDL> Polyfill, box-X-coords, box-y-coords, C010r=4, /Normal
IDL> Plot, time, curve, Background=l, Color=2, / N o ~ r a s e
IDL> PlotS, box-X-coords, box-y-coords, Color=3, /Normal
IDL> XYOutS, 0.5, 0.3, 'Critical Zone', Color=3, Size=2, $
Alignment = 0.5, /Normal
Color can sometimes serve to represent another dimensional property of a data set. For
example, you might display XY data as a 2D plot of circles (polygons), with the color
of each polygon representing some additional property of the data such as temperature
or population density, etc. Let's see how this can be done.
IDL doesn't have a built-in circle-generator, but it is easy enough to write such a
function. Open a text editor and type the code below to create the IDL function named
Circle.
FUNCTION CIRCLE, xcent er, ycenter, radius
points = (2 * !PI / 99.0) * FIndGen(100)
X = xcenter + radius * Cos (points)
y = ycenter + radius * Sin(points)
RETURN, Transpose ( [ [X], [y]1 )
END
The X and Y points that comprise the circle will be returned in a 2 by 100 array. You
can use this as input to the Polyfill command. Save the program as circle.pl-o mzd
coinpile it by typing this command:
IDL> .Compile circle
Next, create some randomly-located X and Y data. (You will set the seed to begin, so
that your results will look like the illustration in Figure 32, below.) Type:
IDL> seed = -3L
IDL> X = RandomU(seed, 30)
IDL> y = RandomU(seed, 30)
Let the Z values be a function of these X and Y values. Type:

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

Working with Images


In reality, any 2D data set of any data type can be thought of as an "image." But to
display image data on an 8-bit display, the image data must be "scaled" into byte
values between 0 and 255. (On a 24-bit display, each of the RGB values that make up
the 24-bit image must be scaled into byte values.) Since images are always displayed
as byte values, they are often stored that way too. But, no matter how they are stored
(and many images these days are stored as 16-bit integers, 32-bit integers, and even
floating point values), they are always displayed as byte values with one or the other
of the two IDL commands that display images: TV and 7'VScl.
To see how this works, you need some image data to work with. Use the LondDntn
command to open the Ali nrzd Dale image data set. You will want to work with the
second image in this two image data set. Type:
IDL> image = LoadData (10)
WL> image = image [ * , *, l]
Working witlz Itttage Data

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.

An Alternative lmage Display Command


Although it almost embarrasses me to say it, I find that I seldom, if ever, use either TV
or TVScl to display image data in the real world. They just contain too many
limitations to work well on different computers, with different graphics cards, etc. You
will learn what I mean by limitations as you work through this chapter. But to be used
correctly and generally, they have to be proceeded by 10-12 additional commands to
check for such things as the type of image, the depth of the graphical display, the color
decomposition state, etc. As always in IDL, when you find yourself typing the same
10 commands over and over again, you soon think to write an IDL program to do the
work for you.
I've written such a program and named it TVI~nnge.It is among the programs you
downloaded to use with this book. You will learn of its advantages as you work
though the examples in this chapter. Of course, in the end TVZ~nngedisplays the image
with the TV command, as it must. But it does a lot of work for you before you get to
that part of the code. One of the things it does is relieves you of having to know the
color decomposition state of your graphics device. So, you would not have to issue the
Device, Deconlposed=O command above to display an image. You could just do so
directly with TVZ~nnge:
IDL> TVImage, scaled
Other commands like WZinnge exist in the IDL community. One I like very much is a
program named ZMDisp, written by Liam Gumley and available from Liam's web
page (http://cinzss.ssec.wisc.edu/-gu~nley/irzde,v.html).I encourage you to look at both
of these programs if you are having trouble displaying images properly in your IDL
code.

Scaling lmage Data


Suppose you are measuring atmospheric pressures and displaying the data as an image
next to a color bar. You probably want to be sure that the image data you collected this
week could be compared with image data you collected last week. In other words, you
want to be sure a particular color, say red, in tlzis data set indicates the same pressure
as the red color in that data set last week.
If you display both this week's and last week's image data with the TVScl command,
you have absolutely no guarantee that the particular red color means the same thing in
both data sets.
The discrepancies arise from two sources. First, you may not be using the same
number of colors in this IDL session today that you were using in your IDL session
last week. Since TVScl scales the image data into the number of colors in your IDL
session, this could introduce errors. Second, you have no guarantee that your data sets
have the same range of data in them. Thus, the scaling with TVScl could have, again,
introduced errors.
To circumvent these problems you want to scale the data sets with the BytScl
command and display them with the TVcommand. To make certain that the number of
colors in your IDL session will not introduce errors, you want to scale the data into the
same number of "bins". And, to make certain the range of data in the data sets doesn't
introduce errors, you want to scale the data into the same data range.
Working with Z~nages

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

Scaling Images into Different Portions of the Color Table


Another reason to know how to scale image data, is to be able to scale your data into
different portions of the color table when using 8-bit display devices. This makes it
possible for images to appear to be displayed with different color tables, or for you to
reserve specific portions of the color table for specific purposes. For example, you
may want to have a portion of the color table reserved for graphics drawing colors.
Note that one of the huge advantages of using 24-bit color displays is that you can use
an unlimited number of color tables all at once. The downside of 24-bit displays is that
you have to re-issue the graphics command (e.g., the TV command) after you change
the color table in order to see the new colors take effect. You will see how to write pro-
grams that can automatically re-issue the graphics command when a new color table is
loaded later in the book.
There is just one physical color table on most 8-bit computers, and it is used in all IDL
graphics windows. But by manipulating this color table you can make it appear that
you have several different color tables loaded simultaneously. You do this by loading
different color tables into different portions of the one physical color table. Perhaps
the easiest way to do this is to use the NColol-s and Bottoln keywords to the LoadCT or
XLoadCT commands.
For example, suppose you want to display the same image in what appears to be two
different color tables. After you have opened a graphics window in IDL, you can tell
how many colors are in the color table in that IDL session by examining the system
variable !D.Table-Size. If you divide this number by two, you know how many colors
to use for each image:
IDL> half = !D.Table-Size / 2

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

Displaying 8-Bit Images with Different Color Tables on 24-Bit Displays


Using different color tables when you are running on a 16-bit or 24-bit display device
is as simple as loading different color tables and displaying the image. For example, if
you are running on a 16-bit or 24-bit device, try this:
IDL> world = LoadData(7)
IDL> Window, 1, Title='Gray Scale Image'
IDL> LoadCT, 0
IDL> TV, world
IDL> Window, 2, Title=' Color Image'
IDL> LoadCT, 5
IDL> TV, world
If both of these images were displayed in gray-scale colors, then you are certainly
running IDL on a 16-bit or 24-bit display with color decomposition on. You will learn
more about color decomposition latel; but for now make sure color decomposition is
off. Type this command, then recall the commands above to get more colorful results.
IDL> Device, Decomposed=O

Displaying 24-Bit Images


True color, or 24-bit, images can also be displayed with the TV command. A 24-bit
image is always a three-dimensional data set, with one of its dimensions set to 3. For
example, the data set could be an 171-by-12-by-3array, in which case the image is said to
be bmzd-ilzterlenved If the image is m-by-3-by-12it is said to be row-ir?terlenved; and
if it is 3-by-m-by-12 it is said to be pixel-interleaved.
To load a 24-bit image, type this command:
IDL> rose = LoadData(l6)
This data set is a pixel-interleaved image. You can see this by typing this command:
IDL> Help, rose
ROSE BYTE = Array [3, 227, 1491
To display a 24-bit image on either an 8-bit or 24-bit display, simply use the True
keyword to indicate what kind of interleaving is being used. True=l means pixel-
interleaved; Dzre=2 means row-interleaved; and True=3 means band-interleaved.
IDL> Window, XSize=227, YSize=149
IDL> TV, rose, True=l ; Pixel-interleaved

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.

Displaying 24-Bit Images on 24-Bit Displays


If you are on a 24-bit display, then the display of 24-bit images is just slightly more
complicated. To correctly display a 24-bit image, color decomposition must be turned
Workirtg with Image Data

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

Displaying 8-Bit Images on 24-Bit Displays


On a 24-bit display, 8-bit images are routed through the color table at the time they are
displayed if color decomposition is turned off. (If color decomposition is turned on,
then the 8-bit image is replicated in each image plane of the device and the result is a
gray-scale image whether you have a color table loaded or not. Alas, not all versions
of IDL work in exactly the same way. The situation I describe is the one in effect with
IDL 5.3 at the time this book was written.) In other words, the pixel value of an 8-bit
image is used as an index to look up the particular red, green, and blue color for that
particular pixel. What this means is that if you change color tables in your IDL
session, you must re-display the 2D image to see the new colors take effect. This is
because color determination is made at the time the image is displayed on a 24-bit
display and because you are using the RGB color model. And note especially that
color decomposition must be turned off, or the color table vectors are ignored and the
8-bit image is nltvnys displayed in gray-scale colors. See "Loading Color Tables on a
24-Bit Display" on page 88 for more information. If you have a 24-bit display, type
these commands:
IDL> world = LoadData (7)
IDL> Window, XSize=360, YSize=360
IDL> LoadCT, 5
XDL> Device, Decomposed=O
IDL> TV, world
To see the image in another color table, load the colors and re-issue the TV command
to route the image pixel values through the color table vectors. Note that the image
colors do izot change when you issue the LondCT command.
IDL> LoadCT, 3
IDL> TV, world

Automatic Updating of Graphic Displays When Color Tables are Loaded


The color table changing tools XLoadCT (which is supplied with IDL) and XColors
(which is supplied with the programs written for this book and which I prefer for
many reasons to XLoadCT) both have the ability to call an IDL program when they
load color table vectors in the hardware color table. For example, open a text editor
and type the following short program. (You can use the editor built into the IDL
Development Environment if you like. Choose File->New->Editor from the menu
bar.)
PRO Display, Image=image, WID=wid
IF N-Elements(wid) EQ 0 THEN wid = !D.Window
WSet, wid
TV, image
END
Workirtg witlz 11nnges

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

Controlling lmage Display Order


Normally, when IDL displays an image, it uses the convention in which the 0th
column and the 0th row of the image are at the lower-left corner of the image. Some
people prefer to have the 0th column and 0th row be at the upper-left corner of the
image. If you prefer the second convention, you can force IDL to use that convention
by setting the system variable !Order. By default !Order-is set to 0. If you would
prefer all your images be displayed with the 0th column and 0th row at the upper-left
corner of the image, set !Order = 1.
If you prefer that just the image you are displaying use the second convention, then
use the Order keyword to either the TV or TVScl command. For example, to see the
two conventions side-by-side, type:
IDL> Window, XSize=192*2, YSize=192
IDL> TVScl, image, Order=O
IDL> TVScl, image, Order=l, 192, 0
You might obtain an image data file from someone else and it will appear upside-
down when it is displayed. This is most often because the person who created the data
file used a different convention for placing the 0th column and row. Try reversing the
Order keyword and see if this corrects the problem.
If you wish to fix the problem permanently and not bother with the Order keyword,
you can easily reverse the order of the pixels in the Y (or second) dimension with the
Reverse command, like this:
IDL> image = Reverse (image, 2)

Changing lmage Size


IDL provides two commands to change an image's size: Rebirz and Corzgrid.
Rebirz is limited in that the newly-created image must have a size that is an integral
multiple or factor of the original image. For example, your variable irnnge may be
resized to 19212 or192*3 elements in either the X or Y direction, but not to 300 or 500
elements. The image may be reduced in size in one direction and increased in size in
the other dimension. For example, to resize the variable irlznge to 384 columns and 96
rows, type this code. Your output will look similar to the illustration in Figure 35.
IDL> Window, XSize=384, YSize=96
IDL> new = Rebin(image, 384, 96)
IDL> TVScl, new

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

IDL> Window, ~ ~ i z e = 1 9 2 / 2YSize=192/2


,
IDL> new = Rebin(image, 96, 96, /Sample)
IDL> TVScl, new
Congl.id is similar to Rebin, except in two ways. First, the number of columns and
rows specified in the new image may be set to any arbitrary number. And second,
nearest neighbor sampling is used by default. If you want to use bilinear interpolation,
you must set the Interp keyword, like this:
IDL> Window, XSize=600, YSize=400
IDL> new = Congrid(image, 600, 400, /1nterp)
IDL> TVScl, new

Changing lmage Size in PostScript


Devices, like the PostScript or Printer device, that have scalable pixels (as opposed to
the fixed-size pixels of your display device) size their images differently. (See
"Differences Between the Display and PostScript Devices" on page 185 for more
detailed information.) In particular, you don't use the Rebir?or Congrid commands to
size images, you use the XSize and YSize keywords to the TV or TVScl command
instead.
For example, instead of resizing the image above to a 600 by 400 size with the
Corzgl-idcommand, if you wanted the image to have an aspect ratio of 6 to 4, you
might do this when you output into a PostScript file:
IDL> thisDevice = !D.Name
IDL> Set Plot, 'PS1
IDLz ~ e v i c e ,XSize=6, YSize=4, / ~ n c h e s
IDL> TVScl, image, XSize=6, YSize=4, /inches
IDL> Set-Plot , this Device
If images are sized and positioned with normalized coordinates as described in the
next section, you can write image display code that is essentially independent of the
graphics display device.
Note that in addition to keeping track of whether you are trying to display an 8-bit or a
24-bit image, the current decomposed state, the required decomposed state, the depth
of the output display device, and whether you are displaying multiple images in the
same window, TVIinnge is also smart enough to know where you are tying to display
the image and can adjust itself accordingly if you are sending output to a PostScript
file or to a printer. Just another reason why I couldn't possibly live without it. Read on
for even more reasons!

Positioning an lmage in the Display Window


Normally when an image is displayed, IDL puts the lower-left corner of the image in
the lower-left corner of the window. But you can move the image to other positions in
the display window by means of additional parameters to the TV and TVScl
commands.
For example, if a second parameter is present, this is interpreted as the posiriorz of the
image in the window. The position is calculated from the size of the window and the
size of the image. See the on-line help for the TV command for the detailed algorithm.
Type:
IDL> ? TV

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.

IDL> Window, XSize=320, YSize=320


IDL> ncolors = ! D . Table-Size
IDL> TVLCT, 255, 255, 0, ncolors-l
IDL> Erase, color=ncolors-l
IDL> colorbar = Replicate (lB,20) # BIndGen(256)
IDL> TV, BytScl(colorbar, Top=ncolors-2), 32, 36
IDL> TV, BytScl(image, Top=ncolors-2), 92, 64

Positioning Images with Normalized Coordinates


It is frequently convenient to position and size images using normalized coordinates.
This is similar to how the Positior~keyword is used with other IDL graphics
commands. (See "Differences Between the Display and Postscript Devices" on
page 185 for additional information.) This is especially true if you are displaying
images in resizeable graphics windows, or using images with other IDL graphics
routines in the same window, or you want to write IDL programs that can be sent to a
Postscript file with little or no trouble. This last point is especially pertinent.
Take, for example, the IDL commands you just typed above to put a color bar next to
an image. While these commands work well in a display window, they don't work at
all if you want similar Postscript output. (See "Differences Between the Display and
Postscl-ipt Devices" on page 185 for a full account of the problems you might face.)
But suppose for a moment you could specify the size and location of an image in the
window with a Positior? keyword. How would this work? Assume you want to put the
image in a window, ally window, and have it fill up, say 80% of the window space. (It
has been pointed out to me that my formulation below uses only 64% of the window
space, but the truth seems so non-intuitive I am loath to correct the mistake. Make it a
"large percent" of the window space, if you like). In terms of normalized coordinates,
you can say the position in the window can be expressed like this:
position = [0.1, 0.1, 0.9, 0.91
But how does this translate to device coordinates, which are normally used for
images? It depends, naturally, on how big the window is. But you can find out how big
the visible portion of the display window is. It is given by the system variables
!D.X-VSize and !D. Y-VSize, expressed in device or pixel units.
So, in terms of pixel coordinates, you can calculate the size the image has to be and
where it has to start in the output window like this:
xsize = (position[2] - positionlO] ) * !D.X VSize
ysize = (position131 - position [l]) * !D.Y - V S ~ Z ~
xstart = position[O] * !D.X-VSize
ystart = position[l] * !D.Y-VSize
The only difference between outputting the image to the display device and outputting
the image to a Postscript file will be how it is sized. You can write the code to display
the image like this:
IF !D.Name EQ 'PS1 THEN $
TV, image, XSize=xsize, YSize=ysize, xstart, ystart $
ELSE $
TV, Congrid(image, xsize, ysize), xstart, ystart
This bit of code will work whether you are outputting the image to the display or to a
Postscript file. But the aspect ratio of the image is not always maintained when you do
this. In fact, you allow the image to fit the shape of the window. This works well for
some applications and not so well for others. In any case, the problem is easily solved
because if you want to keep track of and preserve the aspect ratio of the image you
simply fit one side of the image and adjust the position coordinates of the other
appropriately.
The code to do this has already been written for you in the program TVIr??age,which
you downloaded to use with this book. TVImnge uses the Positiorz keyword to position
and size images for display, either on your display tenninal or in a Postscript file. You
can use the Keep-Aspecf-Ratio keyword if you want the TVImage program to
maintain the aspect ratio of the image it is displaying.
You can reproduce the display above in which the color bar was to the left of the
image using TVInmge, like this:
IDL> Erase, color=ncolors-l
IDL> barposition = [32, 32, 52, 2921 /320.0
IDL> imagePosition = [92, 64, 284, 2561 /320.0
IDL> colorbar = Replicate (lB,20) # BIndGen (256)
IDL> TVImage , BytScl (colorbar, Top=ncolors - 2 ) , $
Position=barPosition
Workiizg with I~nageData

IDL> TVImage, BytScl (image, Top=ncolors-2), $


Position=imagePosition
This is not only much better because it can go into any size window or into a
Postscript file as well as to your display, but it also opens up the possibility of easily
adding additional graphics to this display. For example, here is how easy it is to put a
box and labels around the color bar and the image.
IDL> TVLCT, 255, 255, 255, ncolors-l
IDL> Plot, [0, !D. able-Size] , YRange= [0,!D.Table size] , $
/NoData, Color=O , ~osition=bar~osition, ~ F i c k s = l ,$
/NoErase, XStyle=l, YStyle=l, XTickFormat=' (Al)' , $
YTicks=4
(192), / ~ o ~ a t a$,
IDL> Plot, 1ndGen (192), I n d ~ e n
Position=imagePosition, /NoErase, $
XStyle=l, YStyle=l, Color=O
Your output should look like the illustration in Figure 37.

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.

Reading Images from the Display Device


There are times when you spend a lot of time and issue a lot of commands to get a
graphic display just the way you like it. And sometimes it is convenient to read the
graphic display into an image variable for manipulation or even hardcopy output. This
is the time when you need to know how to get a screen capture of an IDL graphics
window. You can do this with the TVRD command, which reads the contents of an
IDL graphics window into either a 2D or 3D IDL byte array, depending upon the
depth of your display device.

Obtaining Screen Dumps on 8-Bit Displays


To read the entire graphics window on an &bit display, type these commands:
IDL> Window, XSize=250, YSize=250
IDL> TVScl, image
IDL> new-image = TVRD ( )
IDL> Help, new-image
Notice that the newly created variable is now a 250 by 250 byte array.
Working with I~nages

Obtaining Screen Dumps on 24-Bit Displays


If you are running IDL on a 16- or 24-bit display, you don't want to use the TVRD
comnland like shown above. A 16- or 24-bit display has three color channels. If you
use the TVRD command without any arguments, as above, then what you get in the 2D
resulting array is the maximum pixel value in each of those three channels. Unless you
have a gray-scale color table loaded (in which case, each channel has the same value),
this is not what you want or expect.
Moreover, when you do a screen dump on a 24-bit display on a PC or Macintosh
computer, if you have color decomposition turned off, the numbers you read from the
display are back-translated through the color table. (Who thinks this stuff up!?) Thus,
it is essential to make sure you have color decomposition turned on before you take
the screen dump. The commands you want to use are these:
IDL> Device, Decomposed=l
IDL> new-image = TVRD (True=l )
But now notice that you have a 24-bit image, not a 2D 8-bit image, and you will have
to use the appropriate True keyword with the TV command when you display the
image:
IDL> Help, new-image
IDL> Erase
IDL> Device, Decomposed=l
IDL> TV, new-image, True=l
Note that neither the Device command nor the ncre keyword is necessary with TVZln-
age, since the program automatically determines whether it is displaying an 8-bit or
24-bit image and sets the correct color decomposition value and P u e keyword.
IDL> TVImage , new-image

Reading a Portion of the Display


If you want to read just a portion of the window, you can specify the pixel coordinates
of the lower left corner of the part of the window you want and the number of columns
and rows to read. In other words, you can specify a rectangle. For example, if you
wanted to capture just the face in the image above, and you are on an 8-bit display
device, you can type this:
IDL> new-image = TVRD(40, 30, 110, 130)
Or, if you are on a 24-bit display device, you can type this:
IDL> Device, Decomposed=l
IDL> new-image = TVRD(40, 30, 110, 130, True=l)
The image array you obtain from reading the display device can be treated like any
other image in D L . For example, if the screen dump is an 8-bit image, you display it
like this:
IDL> Erase
IDL> TV, new-image
Or, if the screen dump is a 24-bit image, you display it like this:
IDL> Device, Decomposed=l
IDL> TV, new-image, True=l

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

Basic Image Processing in IDL


IDL oliginated as an image processing tool, so it has many image processing
capabilities. What are described in this section are a few of the basic image processing
tools available in IDL.

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

Image smoothing is used in an image processing technique called zi1~slzal-pnzasking.


This is a technique that is used to locate edges in images, or those locations where
pixel values change dramatically. The technique is quite simple: subtract the
smoothed image from the unsmoothed image, like this:
IDL> TV, ( (image - smoothed) + 255) / 2.0, 2*192, 0

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:

This can be created and applied to the image like this:


IDL> kernel = [[1,2,3,2,11, [2,7,11,7,21,$ [3,11,17,11,31,
[2,7,11,7,21, [1,2,3,2,111
Basic Zi~tageProcessing iit ZDL

IDL> TV, Convol (image, kernel, Total (kernel), $


/Edge-Truncate) , 192*2, 0
You can find descriptions of image kernels in almost any good image processing book.

Removing Noise From Images


A common image processing technique is to remove noise from an image. Noise can
accumulate from many sources and often acts to degrade the image. A conlmon form
of noise is salt and pepper noise, in which random pixels in the image have extreme
values. To see how image smoothing deals with this kind of noise, first create a noisy
image. Use the image from before, and type the commands below to turn
approximately 10 percent of its pixels into salt and pepper noise:
IDL> noisy = image
IDL> points = ~andomU(seed,1800) * 192 * 192
IDL> noisy(points) = 255
IDL> points = RandomU(seed, 1800) * 192 * 192
IDL> noisy (points) = 0
Create a window and display the noisy image next to the original, like this:
IDL> Window, XSize=192*3, YSize=192
IDL> TV, image, 0, 0
IDL> TV, noisy, 192, 0
The Medim? command in IDL is an excellent choice to remove salt and pepper noise
from images. The Medial? command is similar to the S~?zootlzcommand, except that it
calculates the median value of the pixel neighborhood instead of the mean value. This
has two important effects. First, it can eliminate extreme values in images. And
second, it does not blur the edges or features of images whose sizes are larger than the
neighborhood. To see how this works, type:
IDL> TV, Median(noisy, 3), 2*192, 0
Your display window should look similar to the illustration in Figure 41.
I

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.

Enhancing the Edges of Images


An image may be sharpened or have its edges enhanced by differentiation. IDL
provides two built-in edge enhancement functions, Roberts and Sobel, and there are
other ways to enhance image edges as well. For example, you can convolve the image
with a Laplacian kernel that looks like this:
Workirtg with Zinage Data

This is often called a Laplacian sharpening operator because it improves contrast at


the edges of images. To see how these methods work, type:
IDL> TV, Sobel(image), 0
IDL> TV, Robert S ( image) , 1
IDL> kernel = [ [l,1, l] , [l,-7,l] , [l,1, l] ]
IDL> TV, Convol(image, kernel), 2
Your display window should look similar to the illustration in Figure 42, below.

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.

Frequency Domain Filtering of Images


Filtering in the frequency domain is a common image and signal processing
technique. It can be used to smooth, sharpen, de-blur, and restore images.
There are three basic steps to frequency domain filtering:
1. The image is transformed from the spatial domain into the frequency domain
using the Fast Fourier Transform (FFT).
2. The transformed image is multiplied by a frequency filter.
3. The filtered image is transformed back to the spatial domain.
These steps are implemented in IDL with the Fast Fourier Transform function FFT.
(The image is transformed from the spatial domain to the frequency domain if the
second positional parameter to the FFT command is a negative 1. The transformation
occurs in the opposite direction if the parameter is a positive 1.) The general form of
the frequency filtering command is this:
filtered-image = FFT(FFT(image, -l)*filter, 1)
In this case, image is either a vector or a two-dimensional image andfilter is a vector
or two-dimensional array designed to filter out certain frequencies in the image.

Building Image Filters


IDL makes it easy to build digital image filters with its array-oriented operators and
functions. (In fact, many programmers take advantage of the Digital-Filter command
that comes with IDL to build digital filters.) Many common filters take advantage of
what is called a frequency image or Euclidean distance map. An Euclidean distance
map for a two-dimensional image is an array of the same size as the image. Each pixel
in the distance map is given a value that is equal to its distance from the nearest corner
of the two-dimensional array. The Dist command in IDL is used to create the
Frequency Doittniiz Filtering of Z~?zages

Euclidean distance map or frequency image. To see a representation of a simple


distance map, type:
IDL> Surface, Dist (40)
A common type of filter to use in frequency domain filte~ingis a Butterworth
frequency filter. The general form of a low-pass Butterworth frequency filter is given
by the equation:
filter = 1 / [l + c(R/R,) 2 n ~
where the constant C is set equal to 1.0 or 0.414 [the value defines the magnitude of
the filter at the point where R=R, as either 50% or l/Sqrt(2)], R is the frequency
image, R, is the nominal filter cutoff frequency (represented as a pixel width in
practice), and n is the order of the filter and is usually 1.
A high-pass Butterworth frequency filter is given by the equation:
filter = 1 / [I + c(R,/R)~"]
To apply frequency domain filtering to an image, use the LoadDnta command to open
the Earth Marltle Corzvection image. This is a 248 by 248 two-dimensional array.
IDL> convec = LoadData(l1)
Type these commands to open a window, load the Standard Gamma I1 color table, and
display the original image in the upper left-hand corner:
IDL> Window, 0, XSize=248*2, YSize=248*2
IDL> LoadCT, 5
IDL> TV, convec, 0, 248
The first step in frequency domain filtering is to transform the image from the spacial
domain into the frequency domain with the FFT function, like this:

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

Working with Colors in IDL


A color in IDL is composed of three specific values. We call these values a color triple
and write the color as (red, green, blue), where the red, green, and blue values
represent the amount of red, green, and blue light that should be assigned that color on
the display. Each value ranges between 0 and 255. Thus, a color may be made up of
256 shades or gradients of red, 256 shades of green, and 256 shades of blue. This
means that there are 256 times 256 times 256. or over 16.7 million colors that can be
Graphical Display Teclzrziqz~es

represented on the display by IDL. To give an example, a yellow color is made up of


bright red and bright green, but no blue color. The color triple that represents a yellow
color is written as (255, 255, 0).
It used to be-with few exceptions-that color triples were accessed in IDL by using
a number, called the i~zdex-,to look up values in a table. These days, with more and
more 24-bit graphics cards available, we are often expressing the color triple directly.
If we do use an index, this look-up table is called a color translation table or-more
often-just a colol- table. A color table consists of three columns of numbers. One
column represents red values, one column represents green values, and one column
represents blue values. Typically, these columns of numbers are represented as
vectors. What you do when you load a color table in IDL is select exactly which
numbers are placed into these columns or vectors. You see an illustration of this
concept in Figure 44.
...........................................................................

Indexed Color Model


8-Bit Image

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.

Using the Indexed versus the RGB Color Model


In addition to knowing that a color is represented as a color triple and that a color table
is often used to determine what that color triple should be, you must also be aware of
two color models that are used in IDL. The Indexed Color Model is used on 8-bit
displays and the RGB Color Model is used on 24-bit displays. (IDL also uses a
modified form of the RGB Color Model on PC's and Macintosh computers that
support 16-bit color.)
Both models can use a color translation table to determine the specific color used on
the display. (RGB Color Models use a color translation table if color decomposition is
Working with Colors in ZDL

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

Visual names will normally be either PseudoColor, Dil-ectcolor, or DueColor: The


visual depth will normally be either 8, 16, or 24, which refers to the number of bits
used to determine a specific color in this visual class.
An 8-bit PseudoColor visual class will indicate that you are using an Indexed Color
Model and using a dynamic color display. A 24-bit TrueColor or DirectColor visual
class will indicate that you are using the RGB Color Model. DirectColor visuals may
sometimes be dynamic color displays, but this is a function of the Window manager
and can occasionally be configured by the user. Usually, DirectColor visuals use a
static color display. TrueColor visuals always use a static color display. (It wasn't
until IDL 5.1 that this behavior was made consistent across all platforms running
IDL.)

Static versus Dynamic Color Visuals


A PseudoColor visual class is a dyrlanzic color visual. What this means is that if you
change the color specified in the color look-up table, any pixel on the display that uses
that color index changes color immediately. In general, loading a new color table will
immediately change the colors of any graphic on the display.
A TrueColor visual class is a static color visual. This means that changing a specific
color in the color table will have absolutely no effect on any graphic or color that is
already on the display, since those colors were expressed directly as RGB triples.
DirectColor visual classes are more difficult to discuss. DirectColor visual classes are
only available on UNM machines. Each machine manufacturer seems to have slightly
different ideas about what a DirectColor visual class means. But in theory the
DirectColor visual class should give you the best of both worlds: a 24-bit color system
that behaves as if it were a dynamic color visual. In practice, I've seldom seen it work
Grrrplzical Display Teclzitiqlces

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

Specifying Colors on an &Bit Display


If you are using the Indexed Color Model, you specify a particular color as an index
into the color table. IDL looks that color index up in the table and uses the values
found in the red, green, and blue columns of the color table as the color triple that
specifies a particular color. For example, suppose you load the color triple
(255,255,0), which specifies a yellow color, into the color table at entry 180. You can
do this with the TVLCT command, like this:
IDL> TVLCT, 255, 255, 0, 180
If you want to draw a plot in the yellow color, you specify the color index with the
Color keyword, like this:
IDL> data = LoadData (l)
IDL> Plot, data, Color=180
Similarly, any image pixel that has a value (i.e., index) of 180 will be displayed in the
same yellow color.
If you are using the Indexed Color Model, you can easily change the color of the plot
by simply loading a new color triple into entry 180 of the color table. For example,
you can load a green color, like this:
IDL> TVLCT, 0, 255, 0, 180
You see the plot color change immediately because the color is indexed to the color
table.

Specifying Decomposed Colors on a 24-Bit Display


If you are on a 24-bit display, however, the situation is slightly more complicated. By
default, IDL uses "decomposed" color when it uses the RGB Color Model. That is to
say, IDL does not treat the color index as a single index into the color table, but it tries
to "decompose" the index into three separate indices into the color table. It does this
by assuming that the index is a 24-bit long integer. IDL uses the lowest eight bits of
the number as a red index, the middle eight bits as the green index, and the highest
Working with Colors in IDL

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.

RGB Color Model


24-Bit Image
Entry Red Green Blue
0 0 0 0
l 1 l 1
2 2
3

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

IDL> Plot, data, C O ~ O ~ = ~ O O F F F F ~Background=I464646'xL


XL,
IDL> XYOutS, 0.5, 0.95, Align=0.5, / ~ o r m a l ,'Plot Title1, $
C0lor=~00FFO0~xL
Since you may not be fluent in either 24-bit notation or in hexadecimal notation, you
might want to use the program Color-24to obtain a 24-bit integer value. This program
came with the other programs you downloaded to use with this book and can convert
any RGB triple (written as an IDL vector of three elements) into the equivalent 24-bit
integer value. For example, on a 24-bit system, if you want to draw the plot in a
yellow color using the Color-24 program, the command looks like this:
IDL> Plot, data, Color=Color24 ( [255,255,0])
If you want to write code that will work on either an 8-bit or a 24-bit display, your
code might look like this:
Device, Get-Visual-Depth=thisDepth
IF thisDepth GT 8 THEN BEGIN
Plot, data, Color=Color24([255,255,0])
ENDIF ELSE BEGIN
TVLCT, 255, 255, 0, 100
Plot, data, Color=100
ENDELSE

Specifying Undecomposed Colors on a 24-Bit Display


You do not have to use decomposed color indexing with the RGB Color Model. For
example, you might want to load color tables like you d o on an 8-bit display and use
the same code on both the 8-bit and 24-bit display. This is possible if you turn color
decomposition off. This is done with the Device command, like this:
IDL> Device, Decomposed=O
In this case, IDL treats the pixel value or color index as if it were an 8-bit color index.
That is to say, the index is used to access the same entry in the red, green, and blue
color table vectors. Thus, on a 24-bit display, using undecomposed color, you can
draw a yellow plot by typing these commands:
IDL> TVLCT, 255, 255, 0, 180
IDL> Plot, data, 180
But-and this is extremely important-the plot color is expressed directly. It is not
indexed to the entry location in the color table. If you change the color table values at
ently 10, the plot colors will be completely unagected.
IDL> TVLCT, 0, 255, 0, 180
You will have to re-issue the Plot command (or, in general, re-display the graphic) to
see the new colors take effect.
IDL> Plot, data, Color=180
On Windows machines prior to IDL 5.1, IDL always displayed 8-bit images on a 24-
bit display as if it were using undecomposed color, no matter how color decomposi-
tion was configured. That is to say, the 8-bit pixel value was used to index the same
entry in all three of the color table vectors. This behavior (a long-standing bug in the
PC version) changed in IDL 5.1 to make it consistent with the behavior on other plat-
forms. Among other things, this change made it just a little harder to write 8-bit image
display programs that work identically in both 8-bit and 24-bit environments.
Determining if Color Decomposition is On or Off
As of IDL 5.1.1 there was no way to tell for sure whether color decomposition on a
24-bit display is turned on or off. This meant that if you wanted to have color
Worlkirlg tvitlz Colors irt ZDL

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).

Obtaining Device Independent Colors


As you can see, working with colors in IDL can be quite complicated. And it is made
even more so by the fact that the way colors work in IDL has been changing with
almost every release of IDL since 5.0. There are a lot of things to keep track of!
To help you make sense of this, and to help you write code that is device independent,
a number of programs have been included with this book that make it easier to write
programs that can run in any IDL environment. In fact, this is one of the goal's of this
book. You have already been introduced to one of these programs: TVI~?zage, a
program that can display 8-bit or 24-bit images in a device independent fashion.
Another of these programs is GetColor, which can be used to obtain a color reference
in a device independent way.
Specifically, GetColor allows you to ask for a particular color by name. You can see
the names of the 16 colors GetColor "knows" about by typing these commands:
IDL> names = ~ e t ~ o l o r ( / N a m e s )
IDL> Print, names
Black Magenta Cyan Yellow Green Red Blue Navy Pink Aqua
Orchid Sky Beige Charcoal Gray White
And you can see the colors themselves, by using a companion program, named
PickColol; to display them:

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

IDL> yellow = GetColor ( l yellow1)


IDL> TVLCT, yellow, 180
IDL> Plot, LoadData (17), Color=180
If you are on a 24-bit device, with color decomposition turned on, you can use the
return value directly, like this:
IDL> Device, Decomposed=l
IDL> yellow = ~ e t C o l o r( yellow1)
IDL> Plot, ~ o a d D a t a(17), Color=yellow
In fact, you could combine the last two commands into one, like this:
IDL> Plot, LoadData (17), Color=GetColor ( l yellow1)
That's easy enough and you have the advantage of seeing in your code exactly which
color you are using, but you still have to be sure of your current decomposed state and
you can't really do that on an 8-bit device or with color decomposition turned off,
since you must first load the color into the color table. Isn't there a way to make
GetColor to do this automatically?
Of course. But what you have to do is tell GetColor where it should load the color in
the color table. So, you must call GetColor with two parameters: the name of the color
and a location in the color table where the color should be loaded. I like to load
drawing colors at the top of the color table, but not at the very top, since many
programs assume that color will be white. I like to load a drawing color in the next to
last element in the color table vector. I would call GetColor like this:
IDL> yellow = G e t C ~ l o r ( ~ y e l l o w ~
!D.Table-Size-2)
,
The return value is either the index number where the color was loaded (i.e.,
!D.Tnble-Size-2), if color decomposition is off or you are on an 8-bit display device,
or a 24-bit value that can be decomposed into the appropriate color if you are on a 24-
bit device and color decomposition is turned on. This means that these two commands
will work appropriately no matter what kind of device we are on or what the current
color decomposition state:
IDL> yellow = G e t C ~ l o r ( ~ y e l l o w!D.Table-Size-2)
~,
IDL> Plot, LoadData (17), Color=yellow
Or, you can even put these two commands together into a single command and have it
work appropriately in all IDL environments. For example:
IDL> Plot, LoadData(l7), $
~ackground=GetColor('charcoal~, !D.Table-S-2 $
C o l ~ r = G e t C o ~ o r ( ~ y e l ~!D.~able-Size-3)
ow~,
Having programs that work correctly everywhere is, of course, a desirable goal in nrzy
kind of programming, not just IDL programming, but it is not always as easily
achieved as this.

Loading Color Tables on a 24-Bit Display


You are aware, now, that when you use the Indexed Color Model on an 8-bit display
that the pixel colors are indexed directly to the color table. In other words, if you
change the values in the color table by loading a color table in IDL, the colors
associated with those indices change too. If you want to display several images at the
same time using different color tables for each, then you must divide the available
indices of the color table into different regions with different colors loaded in each
region. (See "Scaling Images into Different Portions of the Color Table" on page 63.)
As a practical matter, you can probably only have four or five images at most with
different color tables before you run out of index numbers.
Workirzg witlz Colors in ZDL

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.

Obtaining a Copy of the Color Table


There are two ways to obtain a copy of the red, green, and blue values in the current
color table. One way is to declare the common block Colors, either at the main IDL
level if you want the color table available there, or in any IDL procedure or function.
The call looks like this:
COMMON Colors, r-orig, g-orig, b-orig, r-cur, g-cur, b-cur
Note that a color table must have been loaded in the IDL session for the variables in
the common block to be defined.
The convention is to get the colors of the current color table from the first three
variables. Then if you modify those colors, you place the modified color vectors into
the last three variables. To load the color table, you use the TVLCT command. If you
wanted to reverse the colors in the color table, your code might look like this:
IDL> COMMON Colors, r, g, b, rr, gg, bb
IDL> rr = Reverse(r)
IDL> gg = Reverse(g)
IDL> bb = Reverse (b)
IDL> TVLCT, rr, gg, bb
Another way to get the values of the color table is to use the TVLCT command with
the Get keyword, like this:
IDL> TVLCT, red, green, blue, /Get
In this instance, the variables red, gveerz, and blue are output variables and are filled
with the appropriate values from the color table. Note that these variables contain as
many elements as the number of colors you are using in the IDL session. You can
determine how many colors you are using in the IDL session by first opening an IDL
graphics window and then typing:
IDL> Print, ! D . Table-Size

Modifying and Creating Color Tables


There are two principal commands to manipulate the colors in the color table:
XLoadCT and XPnlette. Between the two, you should be able to make the colors in a
color table exactly the way you want them. XLondCT allows you to stretch the colors
in various ways. (Go into Function mode and click the Add Control Poirzt button
several times. Move a control point with the mouse to see how you can affect the color
table.) It also allows you to change the Gamma correction interactively. (A Gamma
Graplzical Display Teclzrziqztes

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

Saving Your Own Color Tables


Suppose you are happy with the color table you just created and you want to save it.
You can get the color values with the TVLCT command, like this:
IDL> TVLCT, r, g, b, /Get
Look at how long these vectors are. Type:
IDL> Help, r, g, b
You see that they are as long as the number of colors that you are using in your IDL
session. But the colors for the color table above are only in the first 150 values.
Redefine the vectors with just that number of values, like this:

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

Creating Your Own Axis Annotations


Sooner or later, the basic axis annotation that IDL applies to axes by default is not
adequate for the display you have in mind. Fortunately, IDL provides a number of
ways to augment the basic annotation properties of axes. This section describes of few
of the techniques you can use to create more complex axis annotations.

Adjusting Axis Tick Intervals


Sometimes IDL's internal axis annotation algorithm will not divide the axis in a way
that is best for your data. You can control the number of major tick intervals with the
[XYZITicks keyword. Load the Erwe Series Data that came with this book to have an
example data set to work with. Type these commands:
IDL> curve = LoadData ( l )
IDL> LoadCT, 5
IDL> Plot, curve
Notice that the X axis is divided into five major tick intervals. You can change this to
ten major intervals by typing this:
IDL> Plot, curve, XTicks=lO
Your output will look like the illustration in Figure 46.
Notice that the number of minor tick marks has also increased, resulting in a bit of a
cluttered look to the axis. Since you are probably not interested in such a fine axis
granularity, you might want to change the number of minor tick marks as well. The
number of minor tick marks is set with the [XYZIMilzor keyword. For example, you
may want just one minor tick mark between each major tick interval.
You might be tempted to set the XMirzor keyword to 1 to get one minor tick mark
between each major interval, but this would be incorrect. If you set the XMiilor
keyword to l all minor tick marks are suppressed. To get the number of minor tick
marks you want, you actually set the XMirzor-keyword to a value that is orze move than
you want. (I didn't write the code!) To get just one minor tick mark, you type this:
IDL> Plot, curve, XTicks=lO, XMinor=2

Formatting Axis Annotations


Another way you can affect axis annotation is to change the way axis labels are
formatted. For example, the X axis labels are now expressed as integers. You might
want to express them always as three-digit integers. You can do this by setting the
XTickFormnt keyword to the specific format you desire. For example, you can type
this:
IDL> Plot, curve, ~TickFormat='
(13.3)
Creatirzg Yozir OWIZ
Axis An~zotntions

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 , , , , , . . , , , , , , . .

MON TUE WED THU FRI SAT


Figure 47: You cart label your axes with strings by meaits of the [XYZITickNaines
keywords.
Graphical Display Tecltiziques

Writing a Tick Format Function


Another way to format tick labels is to write a function to format the tick labels
exactly the way you would like them to be folmatted. If the argument to the
[XYZITickFol-~zatkeywords is a function name, then IDL will call that function when
it applies the annotation to the label.
For example, suppose you want the X labels on this plot to be dates, written like this:
25 MAR 97. You can write a function named Date to perform the formatting for you.
The function must be defined with three and only three positional parameters. These
will be the axis 1zu17zbel;the irzde,~~zulnbel;and the label value. IDL will call the
function with these three positional parameters when it needs to format the axis. The
return value of the function l7zust be a string variable.
The axis ~zunzberis 0, 1, or 2 to indicate whether this is the X, Y, or Z axis,
respectively. The irzde,~~zulnberis the number of the particular axis label. This number
is hardly ever used by the programmer inside the function. The label value is the
normal value that would be used for the axis label. It is your job to use the label value
to calculate or format a new value, which you retui-n as the result of the function. It is
this return value, then, that is used to label the axis for the particular axis index
number.
It is easier to understand how to write this program with an example. Open a text
editor and type this short Date program. (The program is among the files you
downloaded to use with this book.)
FUNCTION DATE, axis, index, value
monthstr = [ l J a n l , l F e b l , ' M a r''A,p r l , ' M a y T , ' J u n l , ' J u l l ,$
' A u g l , ' S e p l , ' O c t l , 'Nov', ' D e c l ]
CalDat, LONG (value), month, day, year
year = StrMid(StrTrim(year,2), 2, 2)
RETURN, StrTrim(day, 2) + + monthStr(month-l) + $
+ year
END
Compile the Date program so that it can be used to format the X axis tick labels in the
code below. Type:
IDL> .Compile date
Notice the CalDat command in this program. This program accepts a Julian number
that represents a particular date and returns the number of the proper day, month, and
year associated with the Julian number. This infoimation is what you use to fonnat the
label properly. To see how this works, type:
IDL> Window, XSize=500, YSize=350
IDL> startDate = Julday (l, 1, 1991)
IDL> endDate = Julday(6, 23, 1995)
IDL> numTicks = 5
IDL> sizecurve = N-Elements (curve)
IDL> (sizecurve)/ (sizecurve-l)
steps = ~ i n d g e n
IDL> dates = startDate + (endDate+l - startDate) * steps
IDL> ! P. Charsize = 0.8
IDL> Plot, dates, curve, XTickFormat='Date1, $
XStyle=l, XTicks=numTicks, $
Position=[0.15, 0.15, 0.85, 0.951
Your output will look similar to the illustration in Figure 48. To learn more about
labeling axes with dates, see the function Label-Date in the IDL library. The
Label-Date function works much like the Date program you just wrote.
Handliizg Missing Data iit ZDL

30

20

10

0
1 Jan 91 24 Nov 91 16 0ct 92 8 Sep 93 1 Aug 94 24 Jun 95

can format tick labels by memzs of a user-~vritterzficnctiort.


Figzcre 48: YOZJ

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.

Handling Missing Data in IDL


Data, unfortunately, does not always come from your instrument in pristine condition.
It is often necessary to manipulate the raw data to put it into a form you can use. In
fact, many times the raw data set isn't even complete. Many things could have
happened. The data collecting instrument shut down for a short period. An electrical
fluctuation caused spurious data values. The operator mis-adjusted a control. What
can you do with this kind of missing or bad data in IDL?
Graphical Display Techniqzres

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.

Setting Up a 3D Coordinate System in IDL


IDL simulates a 3D coordinate system on the 2D display by multiplying each point in
3D space by a transformation matrix. This transformation matrix, if there is one, is
stored in the !P. T system variable. If you want to draw graphics in a 3D space in IDL,
you must first load the proper transformation matrix into the !l? T system variable, and
then you must make sure your graphics output command is multiplied by the matrix
before it appears on the display. This is quite easy to do in practice.
There are several ways to load the 3D transformation matrix into the !P. T system
variable. If you want great control over how the matrix is set up, you can use the T3D
command to set up the 3D coordinate system just exactly the way you want it. But
unless you are doing something elaborate or out of the ordinary, you won't bother
with the T3D command. Instead, you will load the 3D transformation matrix in one of
Graphical Display Techiziques

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.

Setting Up a 3D Scatter Plot


For example, suppose you have randomly-distributed 3D data and you want to display
it as a scatter plot in 3D space. You can get some randomly-distsibuted 3D data to
work with by typing:
IDL> seed = 3L
IDL> X = RandomU(seed, 41)
IDL> y = RandomU (seed, 41)
IDL> z = Exp(-4 * ((X - 0.5)"2 + ( y - 0.5)"2))
To see that this is randomly-distributed data, type:
IDL> Window, XSize=500, YSize=350
IDL> Plot, X, y, PSym=4, SymSize=2.0
In this case, you probably want to have a set of axes to define your 3D space, so the
Su$ace command with the Save keyword set will be a good choice for setting up the
3D transformation matrix. The Save keyword takes the 3D transformation matrix that
is created for the Surjace command and instead of throwing it away as it usually does,
it saves it in the !P. T system variable. You can use the usual axis rotation keywords to
get the 3D space the way you want it. The NoData keyword makes sure only the axes
are displayed. The [XYZIRmtge keywords are needed to set the 3D space up to reflect
the correct range of the real data, not the dummy data being given to the Surfnce
command. Type this:
IDL> Surface, Dist(lO), /save, / ~ o D a t a ,C h a r ~ i z e = 1 . 5 ,$
XRange= [0,l], YRange= [0,l] , ~ ~ a n g [0,
e = l]
This command sets up the normal three surface axes. You may want to include
additional axes. For example, you may want to emphasize the XY plane. You could
add an additional X and Y axis with the Axis command, like this:
IDL> Axis, YAxis=l, 1.0, 0.0, 0.0, /T3D, CharSize=1.5
IDL> Axis, XAxis=l, 0.0, 1.0, 0.0, /T3D, CharSize=1.5
To draw the points in this 3D space, you have to make sure each 3D point is first
multiplied by the transfosmation matrix. This is accomplished by setting the T3D
keyword on the graphics output command. In this case, you will be using the PlotS
command, like this:

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.

IDL> Colorbar, Position= [O .25, 0.9, 0.85, 0.951 , $


~ange=[Min(2), Max (2)] , NColors=100, Bottom=l, $
Color=255, Title='Z Values1

Positioning the 3D Axes Through the Origin of a Plot


Here is another example of setting up a 3D coordinate system. In this example you
have some data that spans the origin. You want the axes that define the data to be
drawn through the origin. Since you don't want to draw the axes that belong to the
data, this is an example where you might want to use the Scale3 command to set up
the 3D space. The Scale3 command uses the same rotation matrix that is used by the
Su$ace command, but it doesn't draw the axes and it loads the transformation matrix
into the !l? T system variable by default.
To create some data for this example, load the 41 by 41 Elevatiorz Data data set with
the LoadDntn command, like this:
IDL> data = LoadData(2)
IDL> data = data - (Max(data) / 2.0)
IDLz X = FIndGen(41) - 20.0
IDL> y = FIndGen(41)*2.0 - 41.0
Set up the 3D space with the Scale3 command. To make the output as wide as
possible, turn off the normal margins of the plot. Before you do so, save the current
system variable set-up so it is easily restored later. Type:
IDL> Window, XSize=5OO1 ~ S i z e = 3 5 0
IDL> Save, /System-Variables, File='system.savl
IDL> !X.Margin = 0 & !Y.Margin = 0 & !Z.Margin = 0
IDL> Scale3, XRange= [Min( X ) , Max (X)] , Ax=45, Az=45, $
YRange= [Min(y), Max(y) 1 , ZRange= [O, Max(data)]
Now draw the surface plot of the data. Be sure to turn off the axis display on the
Su$ace command with the [XYZIStyle keywords and force the Surface command to
use the 3D transformation matrix you just created rather than calculating its own.
Type:
IDL> Surface, data, X, y, /T3D, XStyle=4, YStyle=4, ZStyle=4
Now draw the axes through the origin. Type:
Graplzicnl Display Teclzrliqztes

IDL> TVLCT, 255, 255, 0, 1


IDL> Axis, 0, 0, 0, Color=l, / T ~ D ,Charsize=2, XAxis=l
IDL> Axis, 0, 0, 0, Color=l, / T ~ D ,Charsize=2, YAxis=l
IDL> Axis, 0, 0, 0, Color=l, / T ~ D ,Charsize=2, ZAxis=l
Your output should look like the illustration in Figure 53.

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'

Combining Simple Graphical Displays


It is easy to take advantage of what you know so far about positioning graphics and
setting up 3D coordinate systems to start to combine graphical displays in various
ways. For example, it is often interesting to display an image together with a contour
plot of the same data. To see how easy this is to do, open the 512 by 512 Brnirz X-Rny
data set by typing this:
IDL> brain = LoadData ( 9 )
We can load this into roughly 80 percent of the current graphics window with the
TVIrnnge command you downloaded to use with this book. But remember that this
could distort the image if the current graphics window is not perfectly square. To
preserve the aspect ratio of the image, and still try to fit the image into about 80
percent of the window, set the Keep-Aspect-Ratio keyword, like this:
IDL> Window, XSize=450, YSize=350
IDL> LoadCT, 3
IDL> thisposition = [0.2, 0.2, 0.8, 0.81
IDL> $
TVImage, brain, ~osition=this~osition,
Keep-Aspect-Ratio=l
Conlbining Sinzple Graplzical Displays

When the Keep-Aspect-Ratio is set with TVIr~znge,the Positior~keyword becomes an


output parameter. In other words, the variable tlzisPositiorz now holds the normalized
position coordinates that describes the position of the image in the window. These are
different from the coordinates you put in because the positions had to be changed to
keep the aspect ratio of the image unchanged. You can see the new position
coordinates by printing the variable, like this:
IDL> Print, thisposition
You can use these new position coordinates to draw a contour plot of the data right on
top of the image that is already in the window. Be sure to use the NoErase keyword to
keep the contour plot from erasing the display the way it usually does. The XStyle and
YStyle keywords are necessary to keep the axes from autoscaling. Type:
IDL> Contour, brain, XStyle=l, YStyle=l, NLevels=8, $
Position=thisPosition, /NoErase
Your output should look similar to the illustration in Figure 54.

Figure 54: It is easy to combine images with corttourplots iiz ZDL.

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.

Figure 55: A combination shaded surface plot and contour plot.

Other combinations of graphics output commands are also possible. You are limited
only by your imagination.

Animating Data in IDL


Another powerful graphical technique for visualizing your data is data animation.
Quite often you can see information in an animation that is difficult or impossible to
see looking at your data in other ways. If you don't have a 3D data set open, you can
open the 80 by 100 by 57 MRI Head data set with the LoadData command, like this:
IDL> head = LoadData ( 8 )
Animation is done in IDL with the XZrzterAizimate command. This command actually
invokes an IDL widget program that is the main animation tool in IDL. XIrzterArzimate
must be called three times. (l)Once to set up the animation tool and, in particular, the
size of the animation frame buffer. (2) Once to load the animation tool. And, (3) one
final time to run the animation sequence.
When a 3D animation array is available, XZrzterArziinate is particularly easy to set up
and run. As an example, you will animate the MRI Head data set in the Z direction.
That is, the animation will run from the bottom of the head to the top along the Z axis.
At the moment each frame of data in the Z direction is a 80 by 100 array. This is a
little small for a good animation, but you will deal with that problem in a moment.
Anilnatiltg Data in ZDL

Setting Up the Animation Tool


First, set up the animation tool. There will be 57 frames in this animation, with each
frame an 80 by 100 image. The frame buffer, then, will be set to 80 by 100 by 57. It is
set up like this. Notice the Sl~owlonrlkeyword. This keyword is set so you can see the
animation as it is loading. It is not necessary to show the user these steps if for some
reason you don't want to. Type:
IDL> XInterAnimate, Set= [80,100,57], /Showload

Loading the Animation Buffer


The next step is to load the animation buffer with data. In this case, you are going to
load the animation tool buffer from the 3D image data set you already have available.
This command, which is always in a loop, looks like this:
IDL> FOR j=O, 56 DO $
X~nteranimate, Frame=j, 1mage=head[*,*,j1
As this command executes you will see each frame loading into the animation buffer.
This is not the animation.

Running the Animation Tool


The animation is sun by typing the XI~zternnii?zntecommand one final time, like this:
IDL> XInteranimate
The animation tool should look similar to the illustration in Figure 56.

Figure 56: The XZizterAnimate animatioiz tool, with the MRI Head data set loaded.

Controlling the Animation


Experiment with some of the animation controls. You can determine how the
animation frames loop, the speed of the animation, and you can even change color
Graphical Display Techiriqztes

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

Saving Animation Pixmaps


One of the reasons the animation tool is as fast as it is, is that it uses pixmaps and the
device copy technique to perform the animation. (See "The Device Copy Method of
Erasing Annotation" on page 116 for additional information about this technique.)
Normally, these pixmaps are deleted when you click the End Arzimntiolz button. If you
keep the pixmaps in memory, you can immediately start a new animation sequence at
any time by just typing the word Xi~lterAizinznte.The way to keep the pixmaps in
memory is to use the Keep-Pixnznps keyword when you start the animation, like this:
IDL> XInterAnimate, Set=[80,100,57], /Showload
IDL> FOR j=O, 56 DO $
XInteranimate, ~ r a m e = j ,~mage=head[*,*,j]
IDLz XInteranimate, 50, / ~ e e p - ~ i x m a p s
You can now exit the animation program and start it up again by just typing this:
IDL> XInterAnimate
You will want to be sure you delete the pixmaps when you are finished with them.
You d o that with the Close keyword, like this:
IDL> XInterAnimate, /Close

Animating Other Types of Graphic Data


Data that is in 3D arrays is not the only type of data that can be animated in the
animation tool. In fact, many times what you want to animate is what you put into an
IDL graphics window. The Xi~ztel-Arzinzatetool has the ability to take a snap-shot of an
IDL graphics window and store that as a frame in the animation buffer. This is done
with the Window keyword rather than the Iinnge keyword.
You can see how this is done with the data set you have open now. This little 80 by
100 image that you are displaying is quite small. You probably want it to be larger.
But you probably don't want to create another, larger array to store the data. This
would be an inefficient use of IDL memory. However, you could make each image
larger by re-binning the image when it is displayed. This would take no additional
memory in IDL.
Suppose you want each animation frame to be 256 by 320 and you would like to
include the frame number on the animation. You might set up your animation code
like this. The idea is to fill up the graphics window with whatever graphics we want to
display, and load the animation loop pixmaps by copying the contents of the window.
Anirrzating Data in ZDL

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

Gridding Data for Graphical Display


Many of the IDL graphical display routines (e.g., Sulfice, Contoul; SIzncle-Sul$ etc.)
require data to be arranged in a 2D gridded array. (The data doesn't have to be
regrrlnrly glidded, generally.) But, occasionally, this is not the kind of data you have
available to you. For example "station data" comes from randomly located collection
points. This kind of data must be gridded before it can be displayed.
To load this kind of randomly distributed XYZ data to work with these exercises, use
the LondDatn command to load the Rmzdornly Distributed (XYZ) data set. This data
set contains three 41-element vectors representing station latitude and longitude
coordinates, and a sampled value. Type: l

IDL> data = L o a d D a t a (14)


IDL> lon = data[O,*I
IDL> l a t = data [ l , *]
IDL> value = d a t a [ 2 , * l
You can plot the latitude and longitude vectors on a grid so you can see they are in fact
randomly distributed. Type:
IDL> LoadCT, 0
IDL> TVLCT, [ 7 0 , 2 5 5 , 0 ] , [ 7 0 , 2 5 5 , 2 5 5 1 , [ 7 0 , 0 , 0 1 , 1
IDL> Window, X S i z e = 4 0 0 , . YSize=4OO
IDL> P l o t , l o n , l a t , / Y N O Z ~ ~ O /, ~ o D a t a , B a c k g r o u n d = l
IDL> P l o t s , l o n , l a t , PSym=5, C o l o r = 2 , S y m S i z e = l . 5

Your output will look similar to the illustration in Figure 58.

Figure 58: Rattdomly distributed latitztde and lortgitude coordirtates.

Delaunay Triangulation Method of Gridding


IDL uses a method of gridding data called the Delaunny trinrzgulariorz rnetlzod. This
algorithm is not the best algorithm ever devised for gridding data, but it has the
advantage that it is fast, relatively straightforward, and widely recognized as an
acceptable gridding method. It depends upon creating a set of Delaunay triangles from
the X and Y coordinates, such that no triangle contains the vertex of another triangle
within its boundary. The Delaunay method involves interpolating regularly gridded
values from the values associated with the vertices of the triangles.
The Triangulate command is used to create the Delaunay set of triangles. The inputs
to the command are the XY coordinates of the triangle indices. The command looks
Ani~tzatiizgData in ZDL

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.

Spherical Gridding of Data


If this were really latitude and longitude data, you would not want to be gridding it on
a flat surface. The surface of the Earth is more spherical. The gridding routines
Triangulate and TriGrid also allow you to apply spherical gridding to your data. In
this case, the triangles that are produced are spherical triangles.
To put this data on a map projection, type:
IDL> Map-Set, /Orthographic, /Grid, /Continents, / ~ a b e l ,$
/Isotropic, 35, -100, Color=l
IDL> Plots, lon, lat, PSym=4, Color=2
To grid this data with spherical gridding many of the same parameters can be used as
before, although this time different keywords are used in the commands. Be sure to set
the Degrees keyword, or the commands will calculate the spherical triangles in
radians. The commands might look like this:
IDL> Triangulate, lon, lat, Sphere=angles, $
Using the Czcrsor with Graphical Displays

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.

Using the Cursor with Graphical Displays


One of the reasons data is displayed visually is so the user can interact with it in one
way or another. One way users like to interact with data is to use the mouse cursor to
Grrrphical Display Teclzrriqltes

select or annotate portions of their data. This kind of interaction is easily


accomplished in IDL using the Cursor command.
To see how the Cursor command works, load the Tirne Series data set with the
LondDntn command like this:
IDL> curve = LoadData (l)
Display the curve by typing these commands:
IDLr Window, XSize=400, YSize=400
IDL> LoadCT, 0
IDL> yellow = G e t C ~ l o r ( ~ y e l l o w1)
~,
IDL> Plot, curve
The Cursor command accepts two arguments. These must be variables in which the
position of the cursor when a mouse button is pushed is recorded. The C ~ ~ r s o r
comnland requires that the cursor be located in the current graphics window. (This is
the window pointed to by the system variable !D. Wir.tdow.)For example, if you type
this command, IDL will be waiting for you to move your cursor into the current
graphics window (window index 0 if you typed the commands above) and click the
mouse button down. When you do, IDL will put the position of the cursor into the
variables xlocntiorz and ylocntion. Type:
IDL> Cursor, xlocation, yLocation
If you print these values out, you will see that the values are given in data coordinate
space. That is the xlocntioiz values will be between 0 and 100 and the yhcntiorz
values will be between 0 and 30. (At least they will be if you clicked the mouse inside
the plot boundaries. What happens if you do not?) The Cursor command returns data
coordinate positions by default.
IDL> Print, xLocation, yLocation

When Is the Cursor Position Returned?


It would appear from the commands above that the cursor position is returned when
the mouse button is pushed down, but this is not always the case. In fact, when the
Cursor command reports its position is determined by keywords to the Cursor
command. These keywords are:
Change The position is reported when there is a chnrzge in the cur-
sor's position, or when the user moves the cursor.
Down The position is returned when the mouse button is pushed
now1z.
NoWait The position is returned immediately when the Cursor com-
mand is executed. There is no delay or wait for mouse but-
tons. This keyword is sometimes used in loops when objects
are being moved on the display.
The position is returned not when the mouse button is clicked
down, but when it comes up or is released.
Wait The Cursor command waits for the button to be pressed to
report its position. As long as the button is pressed down, this
keyword causes the Cursor command to act as though it had
been invoked with the NoWnit keyword. This is the default
behavior for the Cursor command.
Be careful to use the proper keyword with the Cursor command, especially if you are
using the Cursor command in a loop. Users sometimes get into the habit of thinking
that the default behavior for the Cursor command is to only report back when the cur-
Using tlze Czcrsor witlt Graphical Displays

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.

Which Mouse Button Was Used with the Cursor?


In addition to setting the behavior of the cursor, you also sometimes want to know
which mouse button was used to respond to the Cursor command. For example, you
may want to do one thing if the light mouse button was used and something different
if the left mouse button was used in response to the Cursor comn~and.
You can determine which button was used with the Cursor command by examining
the Buttor?field of the !Mouse system variable. (Older versions of IDL used the value
of the !Err system variable for this same purpose.) This field is an integer bit map.
Valid values for the B~lttonfield and their meanings are as follows:
!Mouse.Button = 0 No button has currently been used.
!Mouse.Button = 1 The left mouse button was used with the C L I ~ Scommand.
OI.
!Mouse.Button = 2 The middle mouse button was used.
!Mouse.Button = 4 The right mouse button was used.

Annotating Graphics Output with the Cursor


One way the Cursor command might be used is to allow the user to interactively place
symbols on a line plot. For example, type the commands below exactly as they appear
here. When you hit the final carriage return, click your mouse five times in the current
graphics window. Five symbols will be placed in the window. (If you make a typing
mistake in the code below, start again with the first line when you correct it.) Type:
IDL> FOR j = O , 4 DO BEGIN $
IDL> Cursor, x l o c , y l o c , o own
& $
IDL> PlotS, x l o c , y l o c , PSym=4, ~ y m S i z e = 2 ,$
Color=yellow & ENDFOR

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

Using the Cursor with Images


Normally when you are working with image data and using the Cursor command, you
want the cursor locations in device coordinates rather than data coordinates. This is
because there is usually a simple relationship (most of the time one-to-one) between
the device coordinate and the equivalent location in the image. To see how this works,
open the 360 by 360 World Elevation data set with the LoadData command, like this:
IDL> image = LoadData (7)
Display the image and load some colors like this:
IDL> topcolor = !D.Table-Size-l
IDL> LoadCT, 3, NColors=!D.Table-Size-l
IDL> yellow = G e t C ~ l o r ( ~ y e l l o wtopcolor)
~,
IDL> Window, XSize=360, YSize=360
IDL> TVImage, BytScl(image, Top=!D.Table-Size-2)
Now use the cursor to select a particular column and row in the image. Notice the
Device keyword in the Cursor and PlotS commands. This is to be sure the coordinates
are in device coordinates and not data coordinates. Draw a cross-hair at that location.
(Be sure to click in the image window after you type the Cursor command.) Type:
Usiizg tlze Czcrsor with Grapltical Displays

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.

Row Profile Column Profile


250 200

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.

Using the Cursor in Loops


Sometimes you want to use the Cursor command in a loop. For example, you might
want to know the value of each individual image pixel as you select it with the cursor.
Here is a simple loop that continues until you click the right or rniclrlle mouse button to
get out of it. Open a text editor and type these commands exactly as you see here.
yellow = GetC~lor(~yellow~, !D.Table-Size-2)
LoadCT, 3, NColors=!D.Table-Size-2
TVImage, BytScl(image, Top=!D.Table-Size-3)
!Mouse.Button = 1
REPEAT BEGIN
Cursor, col, row, /Down, /~evice
Print, l Pixel Value : l , image [col, row]
ENDREP UNTIL !Mouse.Button NE 1
END
G~.aplzical
Display Techitiqzies

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.

Erasing Annotation From the Display


Using the cursor to place annotations on the graphical display the way you have been
doing tends to beg the question: "But, how do I erase what I just put there!" There are
two preferred ways to erase annotations. I call these the exclusive OR method and the
device copy method. Of the two, the device copy method gives more professional
looking results, in my opinion. Both are presented here, but the focus will be on the
device copy technique.

The "Exclusive OR" Method of Erasing Annotation


The exclusive OR method of erasing annotation works on the basis of what are called
grpl?icsfulzctiorzs. A graphics function is a bit-level operation on two numbers. These
numbers are associated with the pixel that is already on the display (this is the so-
called destirtntiorl pixel) and the pixel you wish to put in that same location (this is the
so-called source pixel).
Normally, the graphics function IDL uses is called SOURCE. In this graphics
function, IDL ignores the value of the destination pixel and just puts the value of the
source pixel at the pixel location. But if the graphics function is changed to XOR
(exclusive OR) IDL does a bit-wise comparison of the bits of the destination pixel
with the source pixel. This has the effect of "flipping" those bits of the destination
pixel that are set in the source pixel. In other words, if the binary representations of
the destination and source pixel are 01100101 and 11111111, respectively, then the
binary representation of the destination pixel after the XOR operation is 10011010.
(The true XOR story is more complicated than this, because it only really works this
way if IDL has 256 colors in contiguous locations in the color lookup table, and this is
seldom the case. Most people just think of XOR mode as drawing in the "opposite"
color and leave it at that. In true XOR mode you could predict what color you would
be drawing with, but this is not true with this mode under most circumstances. This is
the reason most professional IDL programmers prefer the device copy technique.)
The graphics function in effect at any particular time is set with the Device command
and the Set-Graphics-Function keyword. SOURCE mode is graphics function 3; XOR
mode is graphics function 6. At the moment IDL is in its default SOURCE mode.
While you are in this mode, re-display the image in the image window. Type:
IDL> TVImage, BytScl(image, Top=!D.Table-Size-3)
Now, select XOR mode, like this:
IDL> Device, Set_Graphics_Function=6
You will draw a box on the image like this:
Erasing Aiznotatiort Froni the Display

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

; Restore normal graphics function


Device, set-~raphics-~unction=3
END
Save the file as loop2.pro. (You can find loop2.pro among the files you downloaded
to use with this book.) To compile and run this main-level program, type:

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.

The Device Copy Method of Erasing Annotation


The device copy technique uses pixnznp M ~ ~ ~ Z ~ to O Ierase
V S annotation that you put on
the display. A pixmap window is identical to any other IDL graphics window, except
that it doesn't exist on your display. In fact, it exists in the video RAM of your display
device. In other words, it exists in memory. But in every other respect, it is like a
normal IDL graphics window: it is created with the Wilzd0Ml command, it is made
active with the WSet command, it is deleted with the WDelete command, etc. You
draw graphics in a pixmap window in exactly the same way you draw graphics in a
normal IDL graphics windows (e.g., with Plot, Su~face,TV, and other graphics output
commands).
The device copy technique involves copying a rectangular area from one window
(called the source window) and pasting the rectangle into another window (called the
destirzation window). The source and destination window can sometimes be the same
window, as you will see in a moment. You see an illustration of the device copy
technique in Figure 65.

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

Cursor, col, row, /~evice,/Down


Plots, [col,col] , [O,3601 , /Device, Color=yellow
PlotS, [O,3601 , [row,row] , /~evice,Color=yellow
Print, Pixel Value : ' , image [col, row]
; Loop.
REPEAT BEGIN
; Get new cursor location.
Cursor, colnew, rownew, ro own, /Device
; Erase o l d cross-hair.
Device, Copy=[O, 0, 360, 360, 0, 0, 11
Print, 'Pixel Value: l , image(colnew, rownew)
; Draw new cross-hair.
PlotS, [colnew,colnew], [0,360], /~evice,Color=yellow
PlotS, [O,3601 , [rownew,rownew], /~evice,Color=yellow
ENDREP UNTIL !Mouse.Button NE 1
;Erase the final cross-hair and d e l e t e p i m a p .
Device, Copy=[O, 0, 360', 360, 0, 0, l]
WDelete, 1
END
Save the file as loop3.pro. (This file is among those you downloaded to use with this
book.) To compile and run this main-level program, type:

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

Drawing a Rubberband Box


The device copy technique is an excellent one for drawing rubberband selection boxes
and other shapes on the display. (A rubber band box is a box that has one fixed corner
and one dynamic corner that follows the cursor around.) In fact, your Loop3 program
can be easily modified. Copy the loop3.pro program into a file named rubberbox.pro.
(The loop3.pro file is among those you downloaded to use with this book.) Make the
modifications below to see how easy it is to create a rubberband box.
yellow = GetColor(~yellowt,!D.Table-Size-2)
LoadCT, 3, NColors=!D.Table-Size-3
TVImage, BytScl(image, Top=!D.Table-Size-3)
!Mouse.Button = 1
; Create a pixmap window and display image in it.
Window, 1, /~ixmap,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 (static corner of box).
Erasing Anizotatioil From the Display

Cursor, sx, sy, /~evice,/Down


; Loop.
REPEAT BEGIN
; Get new cursor location (dynamic corner of box).
Cursor, dx, dy, /Wait, /~evice
; Erase the old box.
Device, copy= [o, 0, 360, 360, 0, 0, 11
; Draw the new box.
sx] , [sy,dy,dy,sy,sy] , /~evice,$
Plots, [sx,sx,dx,h,
Color=yellow

ENDREP UNTIL !Mouse.Button NE 1


; Erase the final box and delete the pixmap.
Device, Copy=[O, 0, 360, 360, 0, 0, 11
WDelete, 1
END
To run this program, type:
IDL> .RUN rubberbox
(This file is among those you downloaded to use with this book.)

Graphics Window Scrolling


Another good application of the device copy technique is to implement window
scrolling. In this example you will scroll the image in the graphics display window
with the device copy technique. The image will scroll four columns at a time from left
to right. The algorithm you will use is this: (l)Copy the last four columns on the right
of the window into a small pixmap window that is just four columns wide and 360
rows tall, then (2) Move the entire contents of the display window (minus the four
columns you just copied) over to the right four columns in the same window (i.e., the
source window and the destination window are identical), and finally (3) Copy the
contents of the pixmap window into the first four columns on the left of the display
window. Open a text editor and type the commands below. Name your program
scroll.pro. (This program is among those you downloaded to use with this book.)
Type:
; Open a pixmap window 4 columns wide.
Window, 1, /~ixmap,XSize=4, YSize=360
FOR j=0,360/4 DO BEGIN
; Copy four columns on right of display into pixmap.

; Make the display window the active window.


WSet, 0
; Move window contents over 4 columns.
Device, Copy=[O, 0, 356, 360, 4, 0, 01
; Copy pixmap contents into display window on left.
Device, Copy=[O, 0, 4, 360, 0, 0, 11
ENDFOR
Graplzical Display Teclziziqzces

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?

Graphics Display Tricks in the Z-Graphics Buffer


You can think of the Z-graphics buffer in IDL as a three-dimensional box in which 3D
objects can be deposited without regard to their "solidity." The box has the ability to
keep track of the "depth" of an object in a 16-bit depth buffer. One side of the box is a
projection plane. You can think of rays of light going through each pixel in the
projection plane and eventually encountering a solid object in the box. The pixel value
the light ray encounters is the value that is "projected" onto the projection plane. In
this way, the Z-graphics buffer can take care of hidden surface and line removal
automatically. You see an illustration of this concept in Figure 66.

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 Z-Graphics Buffer Implementation


The Z-graphics buffer is implemented in software in IDL as another graphics output
device, similar to the Postscript device or your nornlal X, Win, or Mac device. Thus,
to write to the Z-gaphics buffer you must make it the current graphics output device
with the Set-Plot command. As with other gsaphics output devices, the Z-graphics
buffer is configured with the Device command and appropriate keywords.
Two keywords that are often used with the Z-graphics buffer are Set-Colors and
Set-Resolution. The keywords are defined like this:
Set-Colors The number of colors in the Z-graphics buffer. By default the
Z-graphics buffer uses 256 colors. This is seldom the number
of colors in your IDL session. If you want the output from the
Z-graphics buffer to have the same number of colors as your
display device, you will need to set this keyword.
Set-Resolution The projection plane of the Z-graphics buffer is normally set
to 640 pixels wide and 480 pixels high. You should set the
resolution of the Z-graphics buffer to the size of the graphics
window you want to display the output in.
Device, Set-~esolution=[400, 4001

A Z-Graphics Buffer Example: Two Surfaces


To see how the Z-graphic buffer works, create two objects named peak and saddle,
like this. (The commands to implement this example can be found in the file
fivosui$pro that you downloaded to use with this book.)
IDL> peak = Shift (Dist(20, 16) , 10, 8)
IDL> peak = Exp ( - (peak / 5 ) A 2)
IDL> saddle = Shift(peak, 6, 0) + Shift(peak, -6, 0) / 2B
You are going to combine these two 3D objects in the Z-graphics buffer, but first you
might like to see what these objects look like on their own. You will display them with
different color tables in two windows. First, load a blue and red color table in different
portions of the color lookup table. Type:
IDL> colors = !D.Table-Size/2
IDL> LoadCT, 1, NColors=colors
IDL> LoadCT, 3, NColors=colors, Bottom=colors-l
Create a window and display the shaded surface plot of the first object. Notice that the
Set-Slznding command is used to restrict the shading values to a particular portion of
the color lookup table. Type:
IDL> Window, 1, XSize=300, YSize=300
IDL> Set-Shading, Values= [O,colors-l]
IDL> Device, Decomposed=O
IDL> Shade-Surf, peak, ZRange=[O.O, 1.21
Display the second object in its own display window. Use a different portion of the
color lookup table for the shading parameters. Type:
IDL> Window, 2, XSize=300, YSize=300
IDL> Set-Shading, Values=[colors, 2*colors-l]
IDL> Shade-Surf , saddle, ZRange= [O . O r 1.21

Make the 2-Graphics Buffer the Current Device


To combine these two objects in the Z-graphics buffer, you must make the Z-graphics
buffer the current graphics display device. This is done with the Set-Plot command.
Graphical Display Tech~ziqrtes

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

Configure the Z-Graphics Buffer


Next, you must configure the Z-graphics device to your specifications. In this case,
you want to restrict the number of colors and you want to make the buffer resolution
equivalent to the size of the current graphics display windows. Type:
IDL> Device, Set~Colors=2*colors,set-~esolution=[300,300]

Load the Objects into the Z-Graphics Buffer


Now, put the two objects into the Z-graphics buffer. Notice that you don't see
anything happening as you type these commands. The output is going into the Z-
graphics buffer in memory, not to the display device. Type:
IDL> set-Shading, Values= [O,colors-l]
IDL> Shade-Surf, peak, ZRange=[O.O, 1.21
IDL> Set-Shading, Values= [colors, 2*colors-l]
IDL> Shade-Surf , saddle, ZRange= [O. 0, 1.21 , / ~ o ~ r a s e

Take a Picture of the Projection Plane


Next, take a "snap-shot" of the projection plane. This is done with the TVRD
command, like this:
IDL> picture = TVRD()

Display the Result on the Display Device


Finally, return to your display device, open a new window to display the result, and
display the "picture," like this:
IDL> Set-Plot, thisDevice
IDL> Window, 3, XSize=300, YSize=300
IDL> TV, picture
Your output should look similar to the illustration in Figure 67.

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

Some 2-Graphics Buffer Oddities


Look carefully at the output in, say, windows l and 3. Look particularly at the axes
labels. Notice that the axes annotations are written slightly larger in window 3, the
output that came from the Z-graphics buffer. The Z-gsaphics buffer for some reason
uses a different default character size than IDL does when it is displaying gaphics in a
display window.
This simple fact can cause you untold hours of difficulty if you don't realize it when
you are setting up a 3D coordinate space in the Z-graphics buffer and combining IDL
graphics commands in the Z-graphics buffer. This is primarily because plot margins
are based on default character size and plot margins are not the same on the display
and in the Z-graphics buffer. They are alrnost the same. But it is the "almost" that will
drive you crazy.
A rule of thumb that is erion~iouslyhelpful, is to always set the !R Charsize system
variable if you are going to be doing gsaphics in the Z-graphics buffer. For example,
like this:
IDL> ! P . Charsize = 1.0
Just to give you an example, look at the illustration in Figure 67. The axes on this plot
were not created in the Z-graphics buffer because then they would have been rendered
in screen resolution (i.e., as an image) and I wanted to render them in PostScript
resolution. If the !P Clzarsize keyword had riot been set prior to rendering the shaded
surfaces and adding the axes later, it would have been impossible to get the axes to
line up in the correct position in the final output.

Warping Images with the 2-Graphics Buffer


One of the more powerful techniques to use with the Z-graphics buffer is to use it to
display slices of a 3D data set. This is possible because of the ability to warp images
onto a polygon plane with the Z-graphics buffer. To see how this works, open the 80
by 100 by 57 3D MRI Head Scar1 data set with the LoadDnta command, like this:
IDL> head = LoadDat a ( 8 )
You may want to open a journal file to capture these commands as you type them,
since there are many of them and you have to get them exactly right. A journal file
will enable you to make changes and re-run the commands easily. (This journal file
has already been created for you and can be found in the file warpirzg.pro that you
downloaded to use with this book.)
IDL> Journal, warping.pro
In general, the dimensions or size of a variable are found with the Size command in
IDL. You need to know the sizes of the three dimensions in order to define the proper
image plane. In this case, the "size" is going to be one less than the true size of the
dimension, because you want to use this number as an index into an array, and IDL
uses zero-based indexing. Type:
IDL> S = Size(head)
IDL> xs = S [l] - 1
IDL> ys = s[21 - 1
IDL> zs = S [31 - 1
Suppose you want to display the three orthogonal slices at the center of this data set,
say through the 3D point (40, 50,27). You can define these points, like this:
IDL> xpt = 4 0
IDL> ypt = 5 0
IDL> zpt = 2 7
Graphical Display Techniqztes

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

coordinates associated with each vertex of the polygon. The I~nnge-Intel-pkeyword


specifies that bilinear interpolation occurs rather than nearest neighbor re-sampling as
the image is warped into the polygon. The T3D keyword ensures that the polygon is
represented in 3D space by applying the 3D transformation matrix to the final output.
Type:
IDL> Polyfill, xplane, / T ~ D ,Pattern=ximage, /image-~nterp, $
Image-Coord=[ [0,01, [ O , zsl, [ys, zsl , [ys, 01 l
IDL> Polyfill, yplane, / T ~ D ,~attern=yimage,/image-~nterp, $
Image-Coord=[ [0,01, [O, zsl, [xs, zsl, [xs, 01 l
IDL> Polyfill, zplane, / T ~ D ,~attern=zimage,/image-~nterp, $
Image-Coord= [ [O,01 , [xs, 01, [xs, ysl , [O, ysl l
Finally, take a snap-shot of the projection plane, and display the result, like this:
IDL> picture = TVRD()
IDL> Set-Plot , thisDevice
IDL> Window, XSize=400, YSize=400
IDL> TV, picture
If you opened a journal file, close it now:
IDL> Journal
Your output should look like the illustration in Figure 68. If it doesn't, modify the
code in your journal file with a text editor to fix the problem. To re-lun the code, save
the file and type this:

Figzcre 68: Arz example of warpirzg irnage data into plartes irz the 2-graphics buffer.

Transparency Effects in the Z-Graphics Buffer


Notice that each slice in Figure 68 has quite a lot of black around the outside edges of
the slice. This is not part of the image. Rather, it is part of the background noise.
One of the nice features of the Z-graphics buffer is that you can apply transparency
effects in it. For example, if you set the Tra~?spnrentkeyword on the Polyfill
command above to roughly 20 or 25, then all those pixels below that value in the
image will be transparent. You can see what this looks like by typing this. (You may
want to start another journal file. If you closed the last journal file, give this file a new
name. Unfortunately, it is not possible to append to a journal file. Call the new journal
Graphical Display Tecltrziqztes

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:

Combining Z-Graphics Buffer Effects with Volume Rendering


Z-graphics buffer effects are often combined with volume rendering techniques to
make vivid visual displays of data. For example, suppose you wanted to create an iso-
surface of this data set. (An iso-surface is a surface that has the same value
everywhere. It is like a three-dimensional contour plot of the data.) Start a journal file
named isosu&ce.pro. (A copy of this journal file is among the files you downloaded
to use with this book.)
IDL> Journal, isosurface.pro
To create an iso-surface, first use the Shade-Voluine command to create a list of
vertices and polygons that describe the surface. In the command below, the Low
keyword is set so that all the values greater than the iso-surface value will be enclosed
by the iso-surface. The variables vertices and polygoizs are output variables. They will
be used in the subsequent PolySlzade command to render the surface. A look at the
histogram of the head data suggests a value of 50 will be a suitable contouiing level.
Type:
IDL> Plot, Histogram (head), Max_Value=5000
IDL> Shade-Volume, head, 50, vertices, polygons, /LOW
The iso-surface is rendered with the PolyShczde command. Be sure to use the 3D
transformation that you set up earlier, like this:
IDL> Scale3, XRange= [O ,xsl , YRange= [ 0,ysl , ZRange= [O,zsl
IDL> isosurface = ~olyShade(vertices,polygons, /T3D)
IDL> LoadCT, 0, ~Colors=topColor+l
IDL> TV, isosurface
Now, combine this iso-surface with the Z slice through the data set that you took
earlier in the Z-graphics buffer. Notice that you are truncating the head data in the Z
direction. Type:
IDL> Shade-Volume, head(*,*,O:zpt), 50, vertices, $
polygons, /LOW
Graphics Display Tricks ilt the Z-Grapltics Bziffer

IDL> isosurface = ~oly~hade(vertices, polygons, / T ~ D )


IDL> isosurface (Where(isosurface EQ 0)) = topColor+l
IDL> TvLCT, 70, 70, 70, topColor+l
IDL> Set-Plot , ' 2' , /Copy
IDL> TV, isosurface
IDL> Scale3, XRange= [Orxsl , YRange= [Orysl , ZRange= [Orzsl
IDL> Polyfill, zplane, / T ~ D ,~attern=zimage,/image-~nterp, $
Image-Coord=[ [0,01, [xs, 01 , [xs, ysl, [Or ysl l , $
Transparent=2 5
IDL> picture = TVRD()
IDL> Set-Plot , this~evice
IDL> TV, picture
IDL> Journal
Your output should look like the illustration in Figure 69. If it doesn't, modify your
journal file and l-un it over again by typing this:
IDL> Qisosurface

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

Opening a File for Reading or Writing


All input and output in IDL is done on logical unit numbers. You can think of a logical
unit number as a pipe or conduit that is connected between IDL and the data file that
you want to read from or write to. To read or write data from a file, you must first
attach a logical unit number to a specific file. This is the purpose of the three Operz
commands in IDL:
OperzR Operz a file for Reading.
Open W Open a file for Writing.
Open U Open a file for Updating (i.e., reading andtor writing)
The syntax of these three commands is exactly the same: the name of the command,
followed by a logical unit number and the name of the file to attach to that logical unit
number. For example, to attach a file named temnp596.dat to logical unit number 20 so
you can write to it, you might type this:
OpenW, 2 0 , 'temp596.dat1
Reading and Writing Data iiz ZDL

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.

Locating and Selecting Data Files


One of the reasons IDL is widely used is because it runs on many different computer
operating systems. But because different operating systems have different file naming
conventions (and, in particular, different ways of specifying subdirectories), this also
presents a challenge in specifying file names in a machine independent fashion.
Fortunately, IDL provides some tools to make this job easier for you.

Selecting File Names


Perhaps the easiest way to get a machine independent file name is to use the Picyile
dialog. This IDL command allows you to interactively pick a file name from a list of
file names using the machine's native graphical file selection dialog. For example, to
pick a file name from a list of the .pro files in your local directory you can type this
command:
IDL> filename = Dialog-Pickfile(Filter='*.prol , /Read)
Note that this command was named Pic@le in versions of IDL prior to version 5 .
IDL 5.2 introduced the ability to use Dinlog-Pickfile to pick multiple files (if they
reside in the same directory) by using the Multiple keyword. Use the normal platform-
dependent way of selecting files. For example, on Windows machines select the first
file normally, then use either a Shift-Click to select all the files between the first file
and the second file or a Colztrol-Click to select an additional file.
IDL> filename = ~ialog-~ickfile(Filter='*.pro', / ~ e a d ,$
/Multiple)
If you want to open the file for writing instead of reading, use the Write keyword
instead of the Read keyword in the dialog. You might even want to suggest a default
filename, Like this:
IDL> outf ile = Dialog-Pickf ile (File= 'default. dat , /Write)
What is returned from this dialog is the absolute path to the selected file in the form
the machine IDL is running on expects. That is to say, it uses the native file name
syntax. You can see this is so by typing this command:
IDL> Help, filename, out£ ile
Notice that the Dinlog-Pickfile dialog has a Cnllcel button. If the Cnrzcel button is
selected, the dialog returns a null string. You always want to check to see if the name
that comes back is null before you proceed to open the file for reading or writing.
IDL> I F out£ile EQ THEN Print, Whoops !
Opeiliitg a File for Reading or Writing

Selecting Directory Names


Dinlog-Pickfile was improved in IDL 5.1 so that it can also be used to select a
directory name rather than a file name. Set the Directory keyword so that only
directories and not files are listed in the selection window:
IDL> directory = Dialog-Pickfile(/Directory)

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.

Constructing File Names


A third useful IDL command for getting a machine independent file name is the
Filepnth command. For example, suppose you wanted to open the file gnlaay.dnt,
which is in the exnnzples/rlntn subdirectory of the main IDL directory. You can
construct a machine independent absolute path name to that file by typing this:
, $
IDL> galaxy = Filepath ( 'galaxy.dat '
Subdirect~ry=[~examples~,~data~])
If you want to start in some other directory other than the main IDL directory, you can
use the Root-Dir keyword to specify name of the starting directoly. For example, to
construct a path name to the same file in the coyote subdirectory of your current
directory, you might type this:
IDL> CD, Current=thisDir
IDL> galaxy = Filepath('galaxy.datl,~ o o t - ~ i r = t h i s ~ i $r ,
S~bdirectory=~coyote')
Note that the Filepnrlz command doesn't actuallyfiizd the file, it just constructs a file
path name. The name can be constructed even if the file doesn't exist on the machine.

Obtaining a Logical Unit Number


In IDL all file input and output is done on a logical unit number. The purpose of an
Operz command is to attach a specific file-indicated by its file name-with a logical
unit number. There are 128 logical unit numbers available for you to use. These are
separated into two categories.
Reading artd WritirzgData in ZDL

- -- -- -- -
Logical U~ritNumbers Purposes
1-99 These numbers can be used directly in Open commands

100-128 These numbers are obtained and managed by


the Get-Lun and Free-Lzrn 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.

Using Logical Unit Numbers Directly


To use a logical unit number directly, you just choose a number within the range of 1-
99 and use it with an Operl command. You are responsible for choosing a number that
is not currently in use. For example, you could open the gnlnay..cint file in the CO-yote
subdirectory of your current directory on logical unit number 5 by typing this:
IDL> CD, Current=thisDir
(Root-Dir=thisDir, $
IDL> f ilename = ~ i l e p a t h
Subdirectory='coyote', 'galaxy.datl)
IDL> OpenR, 5 , f ilename
Once a logical unit number in the range of 1-99 is assigned to a file, it cannot be
reassigned until the logical unit number is closed or you exit IDL (which
automatically closes all open logical unit numbers).
When you are finished with the logical unit number (i.e., you don't want to read or
write to that file anymore) you close it and make it available to be used again with the
Close command:
IDL> Close, 5

Allowing IDL to Manage Logical Unit Numbers


Most of the time (and especially in IDL programs) it is better to let IDL manage the
logical unit numbers. The commands Get-Lurz and Free-Lurz are used for this
purpose. There are two ways to let IDL obtain a logical unit number
You can use the Get-Lun command directly, like this:
IDL> Get-Lun, lun
IDL> OpenR, lun, filename
Or, you can do it indirectly, with the G e t L u n keyword to the Operz command, Like
this:
IDL> OpenR, lun, f ilename, /Get-Lun
This command performs an implicit G e t L u n command and puts the resulting logical
unit number in the variable lurz. This is the way the logical unit number is most often
obtained, especially in IDL programs. (Note that the variable name does not have to
be lurz. You can give it any name you like. If you open several files, you will want
several different names.)
When you are finished with the logical unit number (i.e., you don't want to read or
write to that file anymore) you close it with the Free-Lurz command, like this:
IDL> Free-Lun, lun
The advantage of using the Get-Lurz and Free-Lun commands is that you don't have
to keep track of which logical unit numbers are available or unused. The Get-Lun
procedure is guaranteed to return a valid logical unit number (assuming you have
Reading and Writiiting Fonttatted Data

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.

Determining Which Files are Attached to Which LUNs


You can easily determine which files are attached to which logical unit number by
using the Help command with the Files keyword, like this:
IDL> Help, / F i l e s

Reading and Writing Formatted Data


IDL distinguishes between two types of formatted files with regard to reading and
wdting data: a free file format and an explicit file format. Formatted files are
sometimes called ASCII files or plain text files.
Free File Fonnnt A free format file uses either commas or whitespace (tabs and
spaces) to distinguish each element in the file. It is a more
informal format than an explicit file format.
Explicit File Foi*nlnt An explicit foimat file is formatted according to mles speci-
fied in a format statement. The IDL format statement is simi-
lar to format statements you might write in a FORTRAN or C
program.

Writing a Free Format File


Writing a free format file in IDL is extremely easy. You simply use the PrirztF
command to write variables into the file. This is exactly equivalent to using the Prirzt
command to write variables to the display. IDL automatically puts white space
between each piece of data written into the file.
For example, type these commands to create some data to write into a file:
IDL> a r r a y = FIndGen(25)
IDL> vector = [33.6, 77.21
IDL> scalar = 5
IDL> t e x t = [ a r r a y , v e c t o r 1, s c a l a r l 1
IDL> header = ' T e s t d a t a f i l e . '
IDL> c r e a t e d = 'Created: ' + SysTime ( )
Next, open a file for writing by attaching a logical unit number (which you will have
IDL select for you and put into the variable lurz) to a particular file. Type this:
IDL> OpenW, l u n , ' t e s t . d a t l , /Get-Lun
Finally, write the data into the file and close the data file. Type this:
IDL> PrintF, l u n , header
IDL> PrintF, l u n , c r e a t e d
IDL> PrintF, l u n , a r r a y , v e c t o r , scalar, text
IDL> Free-Lun, l u n
Open the file with a text editor and examine it. It will look similar to this:
Test data f i l e
Created: Wed May 21 11:43:56 1997
0.00000 1.00000 2.00000 3.00000 4.00000 5.00000
6.00000 7.00000 8.00000 9.00000 10.0000 11.0000
12.0000 13.0000 14.0000 15.0000 16.0000 17.0000
18.0000 19.0000 20.0000 21.0000 22.0000 23.0000
Rending nrld Writing Data in ZDL

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.

Reading a Free Format File


Many ASCII files are free format files. For example, files you save from within a
spreadsheet program are often free format files. A special kind of free format data is
data you read in from the keyboard or from standard input.
In IDL, there are two commands that will let you read free fo~matteddata.
Rend Reads free format data from the standard input or keyboard.
RendF Reads free format data from a file.
Rules For Reading Free Format Data
IDL uses the following seven rules to read free format data from either the keyboard
or from a file.
1. If reading into a string variable, all characters remaining on the current line are
read into the variable.
Look at the first line of your data file. There are three words on the line. What this rule
means is that once IDL starts to read into a string variable it doesn't stop until it gets to
the end of the line. The reason for this is that of all the data types in IDL, string
variables may be any size and, also, blank characters are perfectly good ASCII
characters.
Suppose you open the data file for reading and try just to read a single word like this:
IDL> OpenR, l u n , ' t e s t . d a t l , / G e t - ~ u n
IDL> word =
IDL> R e a d F , l u n , word
IDL> Print, word

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

2. Input data must be separated by comnlas or whitespace (spaces or tabs).


This is exactly the kind of data that now faces you in the test.dnt data file. IDL has put
five spaces between each element of the nrrny variable.
3. Input is performed on scalar variables. At-says and sttuctures are tseated as collec-
tions of scalar valiables.
What this means is that if the variable you are reading into contains, say, 10 elements,
then IDL will try to read 10 separate values from the data file. It will use the next two
mles to detennine where those values are located in the file.
4. If the cui-sent input line is empty, and there are still variables left requiring input,
read another line.
5. If the current input line is not empty, but there are no variables left requiling input,
ignore the remainder of the line.
To see what this means, try this:
IDL> d a t a = F l t A r r ( 8 )
IDL> ReadF, l u n , d a t a
IDL> P r i n t , d a t a
You see this:

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

Examples of Reading and Writing Free Format Files


The easiest way to learn how to read and write data in IDL is to see some examples.
Here are a few that illustrate some of the common IDL techniques for reading headers,
working with columnar data, and processing the data once it is read into IDL.

Reading a Simple Data File


Start by reading all the data in the testadatfile that you just created. First, create the
variables that you will be reading from the file. Type this:
IDL> header = StrArr(2) ; Two header lines.
IDL> data = FltArr(5,5) ; Floating point array
IDL> vector = IntArr (2) ; Two-element integer vector.
IDL> scalar = 0 . 0 ; Floating-point scalar.
IDL> string-var = l l ; A string variable.
Notice that the data variable is going to be a 5-by-5 array in this case. It was put into
the file as a 25-element vector. Reading the data this way is equivalent to reading a 25-
element vector and reformatting that vector into a 5-by-5 array. Remember that data in
IDL is stored in row ordec
Notice, too, that you can't read the text at the end of the file into a three-element string
array (which is what you wrote into the data file in the first place) because of mle 1,
above. You will have to read this into a scalar string variable and then parse the text
strings out of the variable later with IDL string processing commands.
You can read the data from the file all at once, Type this:
IDL> OpenR, lun, 'test.dat1, e et Lun
IDL> ReadF , lun, header, data, vector, scalar, st ring-var
IDL> Free-Lun, lun
To convert the string variable containing the end-of-file text to a three-element string
array, you start by trimming the blank characters from both ends of the scalar variable.
Type this:

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> blank = Byte (l111)


IDL> Print, blank
IDL> Help, blank
Note that the return value from the Byte command operation on a string is ahvays an
array. In this case an array of one element. S o you are not confused later on, turn this
into a scalar value, like this:
IDL> blank = blank101
You see the blank character has an ASCII value of 32,
Use the Where command to reveal the blank characters in the byte array, like this:
W L > vals = Where(thisArray EQ blank)
Finally, you are ready to turn your string into a three-element string array, like this:
S = StrArr (3)
[O:vals[Ol
S [o] = string (this~rray -11 )
S [l] = String (thisArray[vals [Ol +l:vals 111 -11)
S [2] = string(this~rray [vals[l]+l:* I )

Writing a Column-Format Data File


It is not uncommon to see data stored in files in columns. It is a good idea to know
how to read and write this sort of data in IDL, but IDL has a surprise for the unwary
programmer. To see what this is, start by writing a column-format data file. Load data
into IDL for writing by typing this command:
IDL> data = LoadData (15)
This data set is a structure that has three fields: lot, lo~z,and tenlp, each a 41-element
floating point vector. Extract the vectors from the structure by typing these
commands:
IDL> lat = data.lat
IDL> lon = data.lon
IDL> temp = data.temp
Next, open a data file for writing, like this:
IDL> OpenW, lun, l column.dat l , /Get-Lun
You want to write three columns of data into this file. Using free format output, this
can be done in a loop, like this:
IDL> PrintF, lun, 'Column data: LAT, LON, TEMP1
IDL> FOR j=0,40 DO PrintF, lun, lat [j], lon[j] , temp [j]
W L > Free-Lun, lun
The first four lines of your colunz~z.dotfile should look something like this:
Column data: LAT, LON, TEMP
33.9840 -86.9405 36.9465
26.2072 -121.615 20.1868
42.1539 -103.733 231.604

Reading a Column-Format Data File


So far, so good. The problem comes when you try to read this column-format data into
IDL. You might--quite naturally-try to do it like this. First, create the variables that
you will read the data into. Type:
W L > header = l l
IDL> thisLat = FltArr (41)
IDL> thisLon = FltArr (41)
Reading and Wiiti~tg
Data in ZDL

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

Execute the code in the text file by typing this:


IDL> .Run l o o p r e a d
You can see that this works by printing the values of the original vectors and the
vectors you just read and seeing that they are the same. For example, type this:
IDL> P r i n t , l a t , t h i s L a t
While this solution works, it may not be the best solution, primarily because it
involves a loop, which is inherently slow in IDL. With 41 iterations speed is
essentially irrelevant. But if there were 41,000 iterations, execution speed might
matter.
If it does, then a better solution is to read the data all at once into a 3 by 41 array, and
then pull the vectors out of this larger array using the array processing commands
Reading arzd Writing Fon?zattedData

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

Creating a Template for Reading Column-Format Data


Since many people have column-format data files, IDL 5 introduced new routines to
make it easier to read this type of data file. The help is in the form of two new
commands: ASCII-Template and ReaLASCII. The ASCII-Template command is a
widget program that takes you through the steps necessary to define your column data.
You can give each column of data a name, tell IDL what type of data it is, and even
skip data columns if you like. The result of running the program is a "template" of the
data file in the form of an IDL structure variable. This template can be passed to the
Read-ASCIZ command and the data will be read according to the template
specifications. The result is an IDL structure variable with fields named and typed
according to your specifications. To see how it can be used with the file above, type:
IDL> fileTemplate = ASCII-Template ( column. dat )
Follow the directions on the widget dialog form that appears on the display. There are
three "pages" to the form. On the first page you see a representative sample of lines
from the data file, with their line numbers on the left. In the text widget labeled Data
Starts at Line: type in the number 2. This will allow the one line header to be skipped
in this file. Click the Next button in the bottom right corner of the widget to move to
the next page.
Notice on this page that the number of fields per line is listed as three and that the
White Space button is selected for the data delimiter. This information is correct, so
just hit the Next button to get to the final page.
This is the page where the columns in the data set can be named and their data type
specified. Notice that if you want to skip one or more of the columns in the data set
that the Type droplist in the upper right-hand corner of the display can be set to a type
of Skip Field. Name the three fields Lntitude, Longitude, and Temperature,
Reading and Writing Data iit ZDL

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.

Writing with an Explicit File Format


To read and write an explicit file format, use the same ReadF and PrirztF commands
that were used for free format files, except now the file format is explicitly stated with
a Fonnat keyword. (The Fonnat keyword can also be used with the Read and Prirzt
commands when reading and writing to standard input and output.)
The syntax of the Forrnat keyword is similar to the format specifications you may
have used in a FORTRAN program. Although the format specifications can be quite
complex, here are a few of the most common. You will immediately recognize them if
you have written any FORTRAN code to read or write data.

A Few Common Format Specifiers


There are many format specifiers in IDL. Here are a few of the most common ones.
Consider the vector data, defined like this:
IDL> data = FIndGen (20)
Specifies integer data. Print the vector out as two-digit inte-
gers, five numbers on each line, each number separated by
two spaces:
IDL> thisFormat = (5(12, 2x) , / )
IDL> Print, data, Format=thisFormat
F Specifies floating point data. Write the data out as floating
values with two digits to the right of the decimal place, one
value per line. (Be sure to include enough space in the num-
ber for the decimal point itself.)
IDL> thisFormat = ' (F5.2)
IDL> Print, data, Format=thisFormat
D Specifies double precision data. Write out five numbers per
line, four spaces between each number, in double precision
values with 10 digits to the right of the decimal point.
IDL> thisFormat = ( 5 (D13.10, 4x) ) l
Reading artd Writing Fortnatted Data

IDL> Print, data*3.2959382, Format=thisFormat


Specifies floating point data using scientific notation (e.g.,
116.36E4).
IDL> thisFormat = (E10.3)
IDL> Print, data*lOE3, Format=thisFormat
Specifies character data. Convert the number to strings and
write them out as four-character strings, each separated by 2
blank spaces, with four numbers to a line:
IDL> thisFormat = (4(A4, 2x) ) '
IDL> Print, StrTrim(data,2), Format=thisFormat
nX Skip n character spaces.
Writing a Comma Separated Explicitly Formatted Data File
Sometimes data files must be written with an explicit format so they can be read by
other software. A comma separated data file is a common example of such a file. For
example, you may want the data vectors you read above to be written to such a file.
Here is how to do it. Open a file namedfornznt.dat for writing, like this:
IDL> OpenW, lun, lformat.datl, et-Lun
Create a string variable that is the comma, like this:
IDL> comma = ,
Now, write the file out, using an explicit file format of a floating value 10 digits wide
with three digits to the right of the decimal, followed by a comma and two spaces, like
this:
IDL> thisFormat = '(F10.3, Al, 2x, F10.3, Al, 2x, F10.3)'
IDL> FOR j=0,40 DO PrintF, lun, thisLat[j], comma, $
thisLon [j] , comma, thisTemp [ j 1 , Format=thisFormat
IDL> Free-Lun, lun
The first three lines of the data file looks like this:

Reading a Comma Separated Explicitly Formatted Data File


To read the data file you just created as an explicitly formatted data file, type this:
IDL> OpenR, lun, format . dat , et-~un
IDL> thisFormat = (2(F10.3, 3x) , F10.3) '
IDL> array = FltArr(3, 41)
IDL> ReadF, lun, array, Format=thisFormat
IDL> Free-Lun, lun
It is as simple as that. Notice that you skipped over the commas by treating them as
spaces in the format specifier above.

Reading Formatted Data From a String


The useful IDL command, ReadS, can read free format or explicitly formatted input
from a string variable instead of from a file. ReadS uses the same rules for reading
formatted data that the commands Rend and ReadF use. In other words, using Rends
is like reading from a data file, except that the read occurs on a string variable.
Reading and Writiitg Data in ZDL

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)

Reading and Writing Unformatted Data


Sooner or later, data begins to overwhelm either you or your machine. In either case,
you begin to think about better ways to store it. Unformatted data (sometimes called
binary data) is much more compact than formatted data and is often used with large
data files. There are two commands to read and write unformatted data that are the
equivalent of the ReadF and PrirztF commands used earlier to read and write
formatted data files. These are the ReadU and WriteU commands.
Unformatted data files are basically stored as one long stream of bytes in a file. What
those bytes mean (i.e., how those bytes are translated into data of a particular data type
and organization) is hard to figure out unless you know what was put into the file in
the first place. Inside a file, one byte looks pretty much like any other. Sense is made
of the bytes by reading the bytes into the properly typed and structurally organized
variables. In principle, this is easy to do, since most data types have a defined byte
size. Floating point values, for example, are each four bytes in size. IDL integers are
each two bytes, and so on.
To read unformatted data files, simply define your variables, open the file for reading,
and read the bytes into those variables-one after the other-with the ReadU
command. Each variable reads as many bytes out of the file as it requires, given its
data type and organizational structure. A five-element floating point vector, for
example, will read five (number of elements) times four (number of bytes in a floating
point value), for a total of twenty bytes, from the file.

Reading an Unformatted Image Data File


For example, suppose you want to read one of the several unformatted image data files
located in the IDL examples/data subdirectory. These files happen to contain byte
data, but they could just as easily contain integer or floating point data. Here are
commands that can be used to open one of these files, the galaay.dat file.
IDL> filename = ~ i l e p a t h ! ~= i r ,$
(~oot-~ir
~ubDirectory=[~examples', 'data'], 'galaxy.datl)
IDL> OpenR, lun, filename, /Get-Lun
This particular image is organized as a 256 by 256 array. If you didn't know this ahead
of time, you would probably have a hard time reading the data properly, since there is
nothing in the data file that indicates how the bytes should be organized. (Which is, by
the way, an excellent reason to learn about HDF files in the next chapter. HDF files
Reading m d Writing Uizforltzatted Data

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

Writing an Unformatted Image Data File


Suppose you performed some image processing on this image and you wanted to save
the results of the processing in another data file. For example, you could perform a
Sobel edge enhancement operation on the image, like this:
IDL> edge = Sobel ( i m a g e )
The Sobel operation has the effect of not only providing an image that has its edges
enhanced, but the returned image is no longer of byte type. In fact, it is integer data, as
you can see by typing this:
IDL> H e l p , i m a g e , edge
Integers are two-byte values in IDL, so if this data is written into a data file, the file
will be twice the size of the original byte data file. This might be confusing to the
person who is trying to read the processed image data file, so you might want to put
some information into the file to give the user some help about the kind of data in the
file and how the data should be organized. This file information might be defined like
this:
IDL> f i l e I n f o = 'Sobe1 E d g e E n h a n c e d , 2 5 6 by 2 5 6 INTEGERS1
This string will be written into the file in front of the image data.
But wait. The stringfilelizfo is just a string of bytes, too. In this case, it is a string 31
bytes long. If you don't know this about the unfonnatted file, then it will be very
difficult to read the data out of the file later on. Imagine, for example, that you thought
the file information string was 30 bytes long. Every integer (two bytes) that you read
from the file after reading the file information string will be the wrong value
completely!
Reading and Writing Data iit ZDL

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.

Reading Unformatted Data Files with Headers


Suppose you want to read the data file you just created above. There are 512 bytes of
header information, followed by 256*256'"'. bytes of image data. You would like the
header information as a string, of course, because it contains textual information about
what kind of data is in the file.
With formatted data, header variables can be created either as null strings or as an
array of null stings and then entire lines of a data file can be read into the header
variable all at once. This is not possible with unformatted files. In fact, the rule on
unformatted string data is that when reading strings out of a file, just exactly enough
bytes are read to fill the current length of the string. Thus, a header defined as a null
string would read rzo bytes out of an unformatted file!
This means that you must know the length of string you are reading before you try to
read it out of the file. In the case of the process.dat file you just created, the header is
512 bytes long. You could use the String and Replicate commands to create a blank
string of the proper length to read into, like before, but it's often easier to read the
header into a byte array variable and then convert that to a string later. The code might
look like this:
IDL> OpenR, lun, 'process.datl, /Get-Lun
IDL> header = BytArr (512)
IDL> ReadU, lun, header
IDL> Print, String (header)
Sobel Edge Enhanced, 256 by 256 INTEGERS
From this information the proper data array can be created and the image data read
from the file and displayed, like this:
IDL> edgeImage = IntArr (256, 256)
IDL> ReadU, lun, edgeImage
IDL> Free-Lun, lun
IDL> Window, XSize=256, YSize=256
IDL> TV, edgeImage
Readirtg and Writing Unfoi*i?tatterlData

Problems with Unformatted Data Files


Unfortunately, as nice as unformatted data is to work with, there are also problems
associated with using it. For one thing, unformatted data is extremely machine
specific. Data written on a Sun computer cannot always be read on an SGI or HP
computer and certainly not on a PC or Macintosh computer, without considerable
mucking around. (The Byteorder command can be used to address many of these
problems and the new Swap-lf_Big-Endinrz and Swq-g-Little-E11dia17 keywords
introduced in IDL 5.1 for the Open commands are absolutely essential when writing
code to read binary data on a variety of machine architectures.)
To make it possible to transport unformatted data across machine architectures, IDL
suppoi-ts XDR, or external Data Representation, file format. The XDR format is a
public domain data format created by Sun Microsystems. It is available on almost all
modern computers. It stores a small amount of metadata (extra infosmation about the
data itself) in a binary file. But XDR files are still compact.
If files are wlitten in the XDR unfosmatted fosmat, then data files can be transferred
between computers easily. In other words, XDR unformatted files become pot-table
across machine architectures.
To read or write a file in the XDR format, the file must be opened with the XDR
keyword set. For example, to write the process.rlnt file above as an XDR file you
would type this:
IDL> OpenW, lun, 'process.datV, et-~un, /XDR
The normal WriteU command is used to write data to the file, like this:
IDL> WriteU, lun, header, edge
IDL> Free-Lun, lun
The length of stlings in XDR files is stored and restored along with the string itself.
This means you don't have to initialize string variables to their correct length like in
normal unformatted file. For example, to open and read the information in this XDR
file, you can type this:
IDL> OpenR, lun, 'process.dat , /XDR
IDL> thisHeader = l
IDL> thisData = IntArr (256, 256)
IDL> ReadU, lun, thisHeader, thisData
IDL> Free-Lun, lun

Accessing Unformatted Data Files with Associated Variables


Large unformatted data files often consist of a series of repeating units. For example, a
satellite may take a 5 12 by 600 floating point image every half hour and store the
images one after the other in a single data file that is downloaded at regular intervals.
It is not unusual to see data files that contain 50-100 MBytes of data in them. An IDL
associated variable is often the best (sometimes only) way to work with the type of
data.
An associated variable is a variable that maps the organizational structure of an IDL
array or structure variable onto the contents of a data file. The file is treated as an array
of these repeating units. The first unit has an index of 0, the second an index of 1, and
so on. An associated variable does not keep the entire data set in memory like a
normal variable. Instead, when an associated variable is referenced, IDL performs the
requested input or output on just that portion of the data required, and this is what goes
into memory.
Reading and Writing Datcl in ZDL

Advantages of Associated Variables


There are several advantages to the associated variable method:
1 . File input and output occurs when the valiable is used in an expression. A separate
read or write command is not necessary.
2. The size of the data set is not limited by memory, as it can be sometimes for large
data sets. Data sets too large for physical memory can be handled easily by break-
ing the data into "chunks" of data.
3. There is no need to declare the number of assays or stsuctures that are mapped
onto the data set ahead of time.
4. Associated variables are the most efficient fonn of 110.
Defining Associated Variables
To define and use an associated va~iable,open the data file in the usual way and then
use the Assoc command to create the associated variable. For example, you can open
the file aDnol7~z.datin the exanzples/dntn subdisectory of the main IDL directory, like
this:
IDL> filename = Filepath(Root-Dir=!Dir, 'abnorm.datl,$
SubDire~tory=[~examples','data~])
IDL> OpenR, lun, filename, /Get-~un
This file contains 16 images or frames, each of which is a 64 by 64 byte assay. Create
the associated variable for this data set like this:
IDL> image = Assoc (lun, BytArr (64, 64))
The first argument to the Assoc command is the logical unit number of the file that is
associated with the variable image. The second argument is a description of the unit
that is repeated in the file.
Although it is not the case with this file, these files often have header infonnation in
front of the repeating file unit. If that is the case, a third positional argument can be
given to the Assoc command that specifies the size of this header or offset into the file.
For example, suppose that the first 4096 bytes of this abnonn.dat file was header
infonnation and you wished to skip this header in the file. Then the Assoc command
could be written like this:
IDL> image2 = Assoc (lun, BytArr (64, 64) , 4096)
Note that you now have two variables, image and intnge2, associated with the same
data file. This is perfectly legal in IDL and, in fact, can be a good way to access unfor-
matted data that does not have uniformly-sized repeating units in it. By changing the
offset into the file it is possible to use the associated variable method as a sort of ran-
dom-access method of reading and writing data.
To display the fifth frame or image in variable named inzage above, type this:
IDL> Device, Decomposed=O
IDL> TvScl, image(4)
The data is read from the data file into a temporary variable, which is displayed and
then discarded by IDL. No explicit RendU command is required and no pennanent
memory has been used in IDL to work with this image. If you wish to make a variable
from an associated variable, you create the variable in the usual way. For example,
you can type this:
IDL> image5 = image (4)
IDL> TV, Rebin(image5, 256, 256)
Reading and Writing Files with Popular File Forinats

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

Reading and Writing Files with Popular File Formats


So far, this chapter has talked about the general ways IDL can read and write data
files. These low-level capabilities make it possible to read and write many kinds of
data files in IDL. But there are many other data file formats you will probably want to
know how to read and write, too. Among these are formats like GIF, JPEG, and TIFF,
which are often used to share data with colleagues across the office or across the
world. You will also want to know how to create Postscript files, which will almost
certainly be required if you wish to publish your graphical output in hardcopy.
IDL has the ability to read and write many popular file formats. A list of these formats
is shown in Table 10. In this section you will learn how to read and write GIF, JPEG
TIFF, and DICOM medical image files. I choose these formats because if you know
how to read and write each of these, which are all slightly different from one another,
you will know how to read or write almost any of the formats IDL provides. In the
following chapter, I introduce you to HDF format files which, along with CDF and
net-CDF file formats, make up what we call the "scientific data file formats". These
formats have the capability of storing "meta-data", or information about the data
itself, in the same file with the data. And then in the chapter after that, you learn how
to write Postscript output, either sending the output directly to the printer or by saving
the output in Postscript files.

Querying Image Files for Information


In IDL 5.2, RSI made it much easier to gather information about data stored in popular
file formats by introducing file "query" commands. These commands allow you to
query the data file without having to actually read the data. The commands are able to
access the metadata (information about the data) that is stored inside the file along
with the image data itself. Here is a list of the new file query commands: Query-BMP,
Query-DICOM, Queiy-GIF, Query-JPEG, Quely-PICK Queiy-PNG, Query-PPM,
Query-SRF, Quety-TIFF, and QuexWAV.
Reading and Writing Data in ZDL

GIF (Removed in IDL 5.4) Read-GIF Write-GIF

HDF See HDF library See HDF libraly


HDF-EOS See HDF libra~y See HDF library

Interfile Read-Interfile None

JPEG Read-JPEG Write-JPEG


netCDF See netCDF library See netCDF library

PICT Read-PICT Write-PICT

PBM/PPM Read-PPM Wsite-PPM

PNG Read-PNG Write-PNG

Postscript None PS or PRINTER device

Sun Raster-files Read-SRF Write-SRF

SYLK Read-SYLK Write-SYLK

TIFFIGeoTIFF ReadTIFF Write-TIFF

WAVE Read-WAVE Write-WAV E

X1 1-bitmap Read-X1 1Bitmap None

XWD Read-XWD None

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

To see what kind of information was returned, type:


IDL> Help, fileinfo, /Structure
You see printed out the following information:
* * Structure <1364998>, 7 tags, length=36, refs=l:
CHANNELS LONG 3
DIMENSIONS LONG Array [2]
HAS-PALETTE INT 0
IMAGE-INDEX LONG 0
NUM-IMAGES LONG 1
PIXEL-TYPE INT 1
TYPE STRING l JPEG '
You can see that there is one image in this file (NUM?-Ir.lzage=l),that it is byte type
data (Pixel-Type=l), and that it is a 24-bit image (Clznrznels=3). The size of the image
can be seen by printing out the dimensions field, like this:
IDL> Print, fileinfo.dimensions
227 149
Other image query routines will have similar fields in their return structures.

Creating a Graphic Display Program


To begin learning how to read and write files in popular file formats, let's first write a
program to display a graphic. Open a new text editor file and type the following
commands. If you prefer not to type his file, it is already written for you and among
the programs you downloaded to use with this book. It is named colui~znm~g.pl-o. The
purpose of the program is to display an image, with the average value of each column
of image data plotted above it. You will learn how to write a graphic display program
later (see "Writing an IDL Graphics Display Program" on page 239, for example). For
now, just observe that this program uses techniques that have already been explored in
earlier chapters of this book.
PRO ColumnAvg, image
On-Error, 2
; Make sure an image is available.
IF N-Elements(image) EQ 0 THEN image = LoadData(7)
; Find the size of the image.
ndims = Size(image, /N-Dimensions)
IF ndims NE 2 THEN Message, 'Image parameter must be 2D.I
S = Size(image, /~imensions)
xsize = S [0]
ysize = S [l]
; Load colors for graphic.
backcolor = GetColor('grayl,!D.Table-Size-2)
axiscolor = GetColor('navyl,!D.Table-Size-3)
datacolor = GetColor('green~,!D.Table-Size-4)
LoadCT, 33, NColors=!D.Table-Size-4
; Save system variables.
theFont = ! p.Font
theplots = !p.Multi
; Set system variables and create the data
Reading arid Wi.iting Data iiz ZDL

; Draw the plots.


Plot, columnData, / ~ o D a t a ,Color=axisColor, $
Background=backColor, XStyle=l, XRange=[O,xsize-l], $
Title=lColumnAverage Value', XTitle='Column Number'
OPlot, columnData, Color=dataColor
TVImage, BytScl(image, Top=!D.Table-Size-5)
; Restore system variables.
!P.Multi = theplots
! P. Font = theFont
END
Save the file as columrznvg.pm, compile it, and run it like this:
IDL> .Compile columnavg
IDL> Window, Title= l Column Average Plot ' , XSize=400, $
YSize=400
IDL> ColumnAvg
The output should look like the illustration in Figure 70, below.

Creating Color GlF Files


GIF files are often used to publish graphical information on the World Wide Web. If
you want to share your graphical results with colleagues, sooner or later you will want
to read or write a GIF file.
Note that starting in IDL 5.4 you must have a letter from the GIF file format patent
holder (Compuserve) granting you premission to create GIF files. Research Systems
will supply you with the proper license for GIF file formation upon receipt of such a
letter. Contact Research Systems technical support for complete information.
GIF files are written in IDL with the Write-GIF command, which has the form:
Write-Gif, filename, image2d, r, g, b
Where the variablefilerznme is the name of the output file, irnnge2d is a 2D byte image
array, and the variables r, g, and b are the color table vectors that are required to be
present with the 2D image array if the image is to be displayed in color.
The filename is often obtained from the user by means of the Dinlog-Pickfile
command, like this:
IDL> filename = ~ialog-pick£ile (/write, $
File='columnavg.gifl,Filter='*.gifl, $
Title='Select GIF File for Writing...')
Notice that a default file name is given. This is done in a quixotic attempt to keep the
user from having to type the name. Perhaps they can just click the Accept button.
(Research has shown that a typical computer user will make a least four typing errors
when typing something as long as a file name. It is good to keep this in mind when
you are writing your IDL programs. Ask a user to select a file name with the mouse,
rather than type it, whenever possible.)
The next step is to obtain the 2D image array (as byte values) and the color table
vectors that go with the image. How you do this depends upon what kind of display
device you are on, and in particular, it depends upon the depth (how many bits your
Reading and Writing Files with Populnr File Forirtats

Column Average Value

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.

If the Display Depth is Eight


If you are on an 8-bit display device, your job is fairly simple. You can obtain the
current color table vectors by running the CololAvg program and then using the
TVLCT command with the Get keyword, like this:
IDL> TVLCT, r, g, g, e et
The color vectors must be 256 elements long. These are probably not that long if you
are running IDL on an 8-bit display, but don't worry. If they are not long enough when
you write the GIF file, IDL will pad them with zeros for you. If you want to take full
advantage of all 256 colors, consider loading your color table and obtaining the color
vectors in the Z-graphics buffer, which has 256 colors available by default. See
"Graphics Display Tricks in the Z-Graphics Buffer" on page 120 for additional
information. Another alternative is to obtain the color table vectors from an
IDLgrPalette object.
Reading and Writing Data in IDL

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.

Writing the GIF File


The final step, in either case, is to write the GIF file, like this:
IDL> Write-GIF, filename, image2d, r, g, b
That's all that is necessaly. You don't need to obtain a logical unit number or anything
else. All of these details are handled by the Write-CIF command.
If you have an application that can open and read a GIF file, try to read this one you
just created. Many World Wide Web browsers support reading GIF files. See if your
browser has an Open File button with which you can try to read this GIF file.

Reading a GIF File


To read the GIF file you just created in IDL, simply use the ReaLCIF command to
read the image and color vectors out of the GIF file, like this:
IDL> Read-GIF, filename, thisImage, rr, gg, bb
The variables tl~isImage,17; gg, and bb are all output variables that are filled by the
Read-CIF program.
Clear the graphics window and load a gray-scale color table, so you can see what
happens next as you display this image. Type this:
IDL> Erase
Now, display the image you just read out of the GIF file, like this:
IDL> Device, Decomposed=O
IDL> TV, thisImage
Chances are what you see in the graphics window is not what you expected. The
colors will probably look strange. This is because the color table the GIF image uses is
not like the color table you had loaded previously. To make the image appear in its
correct colors you must load the color table associated with the GIF image. Type this:
IDL> TVLCT, rr, gg, bb
IDL> TV, thi sImage
You should now see the original image in the display window.
But now have a look at the colors you have loaded in your color table. Use the
program CIizdex, which is one of the programs you downloaded to use with this book,
to view the current color table colors:
IDL> CIndex
This color table doesn't look anything like the ones normally used in IDL. In fact, it is
more like the color tables used in the IDL object graphics system. And there are
consequences of that. For example, try using the TVIitzage command, like this:
IDL> Window, XSize=500, YSize=450
IDL> TVImage , thisImage
Whoops! What has happened here!?
What has happened is that TVImage interpolates image values as it resizes images to
fit into a window. So, for example, if adjacent pixels have values of 5 and 7 and a third
pixel is added between them, the pixel will have a value of 6. In most color tables, the
colors at indices 5,6, and 7 will be pretty much the same. But that is not the case here.
Reading and Writing Data in ZDL

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

Creating Color JPEG Files


Another file format that is often used to share graphical results on the World Wide
Web is the JPEG format. The JPEG format is called a lossy compression format. That
is, the image data is compressed when it goes into the file and some of the information
content of the data is lost and cannot be recovered. The amount of compression, and
thus the amount of information that is lost and the quality of the output image, can be
set using a quality index value. Values range from 0 (poor quality with much
information content lost) to 100 (excellent quality with little or no information content
lost). Normally, the quality index is set to about 75, which provides an adequate
amount of compression without a noticeable loss of information content and image
quality.
A color JPEG image will be a 24-bit image. That is, the image will be a 3D byte array,
in which one of the dimensions will be a 3. The placement of this dimension will
determine if the image is pixel-interleaved (3, m, n), row-interleaved (m, 3, n), or
band-interleaved (m, n, 3). The JPEG file is written with the Write-JPEG command in
IDL. The general form of the command looks like this:
Write-JPEG, filename, image24, Quality=75, True=l
The variablefilerzarne is the name of the output file, the variable image24 is a 24-bit
image with the color information built in, the Quality keyword is a value from 0 to 100
that selects the amount of image compression (and, hence, image quality), and the
True keyword selects the type of pixel interleaving.
As with the GIF file above, how you create the JPEG file depends upon the screen
depth of the machine running IDL. To set the stage, type these commands:
IDL> Window, Title='Column Average P l o t T , XSize=400, $
YSize=400
IDL> CO lumnAvg
First, you obtain a file name for the output file:
IDL> filename = ~ i a l o g - ~ i c k f i l e ( / ~ r i t e$,
File=lcolumnavg.jpgT,Filter='*.jpgl, $
~itle='SelectJPEG ~ i l efor Writing...')
Next, you find the depth of the current display device, by tying this:
D L > Device, Get-Visual-Depth=thisDepth & Print, thisDepth
What you do next depends on this value. And, in fact, the problem is just the opposite
of the problem we just dealt with in creating a GIF file. Here life is easy if we are on a
24-bit display, but more difficult if we are on an 8-bit display.

If the Display Depth is Eight


If you are on an 8-bit display device, you obtain the 2D image array and the color table
vectors like this:
IDL> TVLCT, r, g, b, /Get
IDL> image2d = TVRD ( )
Rending nrld Writing Files with Popular File Forinnts

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]

If the Display Depth is Greater than Eight


If the display depth is greater than eight, then you can obtain the 24-bit image directly.
Be sure to set color decomposition on, however, to avoid the back-translation of the
image value through the color table that you get on PC and Macintosh platforms.
IDL> Device, Decomposed=l
IDL> image24 = TVRD (True=l)

Writing the JPEG File


The final step, in either case, is to write the JPEG file, like this:
IDL> Write-JPEG, filename, image24, Quality=75, True=l
If you have an application that can open and read a JPEG file, try to read the one you
just created. Many World Wide Web browsers support reading JPEG files. See if your
browser has an Operz File button with which you can try to read this JPEG file.

Reading a JPEG File


To read and display a JPEG file in IDL, use the Read-JPEG command, like this:
IDL> Read-JPEG, filename, thisImage
Reading and WiaitingData in ZDL

The variable tllisI~~mge


is, of course, a 24-bit image and how it is displayed will
depend on the depth of your display device. For example, if you are on an 8-bit
display, you might display the graphic with these commands:
IDL> image2d = Color-Quan(thisImage, 1, r, g, b)
IDL> TVLCT, r, g, b
IDL> TV, image2d
Or, you can do this color quantization directly when you read the data from the file
with keywords to the Read-JPEG command, like this:
IDL> Read-JPEG, filename, image2d, colortable, $
Colors=!D.Table-Size, Dither=l, /Two-pass-Quantize
The colortable parameter is an N - b y 3 array containing the resulting color table
vectors. The Colors keyword indicates how many colors the 24-bit image should be
quantized into. It should be a value from 8 to 256. The Ditlzer keyword selects the
Floyd-Steinberg dithering method, which distributes the color quantization error to
surrounding pixels and results in high-quality results. The Ti~o~Pass-Qunlztize
keyword makes the color quantization a two-step process, which also results in better
color quantization and higher image quality.
To display the data, type these commands:
IDL> Erase
IDL> TVLCT, colortable
IDL> TV, image2d
If you are displaying the 24-bit image on a 24-bit display, then you can just read the
file and display the graphic directly:
IDL> Read-JPEG, filename, thisImage
IDL> Device, Decomposed=l
IDL> TV, thisImage, True=l
Note that neither the Device command nor the True keyword is necessary with TVZ1n-
age, since the program automatically determines whether it is displaying an 8-bit or
24-bit image and sets the correct color decomposition value and True keyword.
IDL> Read-JPEG, filename, image24
IDL> TVImage , image24
Note, too, that if I use the TVImnge command, I don't even have to worry the depth of
my display screen. TVItnage takes all of this into account.

Creating Color TIFF Files


The process of creating color TIFF files is identical to the process of creating color
JPEG files, with one important exception. (You can find the process described in
"Creating Color JPEG Files" on page 154.) The exception is that when TIFF files
created in IDL are displayed in other applications they are often displayed upside-
down. There is an Order parameter on the Write-TIFF command (corresponding to
the Order keyword used on a TV command or the !Order system variable) that can
supposedly be used to change the order in which the image is displayed, but I have
found that this value is rarely effective in practice. For this reason, I am in the habit of
physically flipping the Y values of the image before I write the output to a file. This is
easily done in IDL with the Reverse command.
For example, I almost always obtain or create a 24-bit image that is pixel-interleaved,
meaning that it is a 3-by-m-by-M array. I wish to flip the Y dimension of this image,
which is the third image dimension. Suppose I wish to read the JPEG image I created
above and save it as a TIFF file in IDL. I can type these commands:
Reading and Writing Files with Poprclar File Fointats

IDL> Read JPEG, filename, image24


IDL> basename = StrMid(filename, 0, StrLen(fi1ename) -4)
IDL> Write-TIFF, basename + ' . t i f l ,Reverse(image24,3), 1
Opening the file in, for example, Photoshop results in the image being displayed
upright.

Reading Dicom Image Files


I include a short introduction to reading DICOM Part 10 image files because the way
it is done is slightly different (the functionality is written as an object) than the other
file formats and because I think we will see more of this kind of functionality in the
future. (DXF files are also read using an object.) Also, there are some limitations in
DICOM files that you should know about. The first important limitation-as of IDL
5.3-is that you can only read DICOM files in IDL, you cannot write them.
First of all, even though the DICOM functionality is implemented in IDL as an object,
you may not have to know this to read a simple DICOM image file. For example, if
you are only interested in the images in a file, and not the other metadata, such as
patient name, medical modalities, details of how the machine was calibrated, etc., then
you can simply use the Query-Dicorn and RenrE_Dicol-11commands to inquire about
and read the images.
For example, suppose you want to read the nz1._kizee,dc1nimage file distributed with
IDL. You can type these commands:
IDL> filename = Filepath(lmr-knee.dcm', $
SubDir=['examples','datal])
IDL> ok = Query-Dicom ( filename, f ileInf o)
IDL> IF ok THEN Help, fileInfo, /Structure
* * Structure <1366108>, 7 tags, length=36, refs=l:
CHANNELS LONG 1
DIMENSIONS LONG Array [ 2 1
HAS-PALETTE INT 0
NUM_IMAGES LONG 1
IMAGE-INDEX LONG 0
PIXEL-TYPE INT 2
TYPE STRING DICOM'
You see that there is just a single image in this data file. Some DICOM files contain
multiple images. For example, I've seen DICOM files that contain a small thumbnail
image (usually 64-by-64) and the larger actual image. In that case, the Dinzerzsior~s
field only shows the dimensions of the first image, not the second. Although the
Nurn-Irnnges field will indicate that there are two images present. You can read the
second image by setting the IrnageIrzdex keyword to 1 in the Rend-Dicorn command
below.
To read and display this image, type these commands:
IDL> image = Read-~icom( filename , image-1ndex=O)
IDL> Window, XSize=fileInfo.Dimensions[0], $
~~ize=fileInfo.Dimensions[l]
IDL> LoadCT, 0
IDL> TV, image

Using the IDLffDicom Object


To access the actual metadata of the DICOM file, rather than just the image data, you
must use the IDLmiconz object. You will learn more about objects (including how to
Reading artd Writing Data iiz ZDL

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

MODAL1 TY POINTER = Array [l]


Notice the way the G I U Land
I ~ Elernenf numbers are written as hexadecimal numbers
in IDL. You access the element by de-referencing the pointer, like this:
IDL> FOR j=O,N-Elements(moda1ity)-lDO Print, *modality[j]
You can access other data elements in a similar fashion. For example, to obtain the
image data element, which always has a Group number of 7FE0 and an Elernenf
number of 0010, you type this:

And to display the image you can type this:


IDL> Window, XSize=400, YSize=400
IDL> TVImage, *imagePtr [O]
When you are finished with objects they should be destroyed.
IDL> Obj-Destroy, thisObj ect
But this doesn't destroy the pointers you might have created. These should also be
freed up when you are finished with them. Otherwise, you can have memory leakage
on the heap.
IDL> Ptr-Free, modality
IDL> Ptr-Free, imagePtr
Reading and Writing HDF Data

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

Why Use the HDF Format?


In today's world, scientists generate and process data from many different sources.
They work on different computers. They use different kinds of software. They think of
data differently. One scientist may be interested in data that is physically stored in
separate files, but is related conceptually. Another may wish to consider only the data
stored in a particular format in a particular file.
HDF addresses these problems by describing a fo~matby which virtually any kind of
data can be stored in a machine-independent way in one or many separate files. The
HDF file format is supported on most of the computer operating systems scientists use
today. HDF files allow different types of data to exist in the same file. For example, it
is possible to have symbolic, numerical, and graphical data within the same HDF file.
Simply put, the HDF file format allows scientists to communicate about science and
not about the details of file sharing.
But more than that, HDF files are sew-descl-ibing. For every piece of data in an HDF
file, there can be metadata associated with it. Metadata is infornation that describes
the data itself. It can be such information as how the instsument that collected the data
was calibrated, what the scales for the axes should be, what dimensions should be
used with the data, or where the actual data is stored (it can be in a physically separate
file). This makes it possible to work with data that was collected by colleagues who
may have long since retired or moved to other jobs.

Primary HDF Data Objects


A piece of data in an HDF file is said to be a dntn object. A data object contains two
parts: a data desc~iptorand a darn elenzerzt. The data descriptor contains information
about the type, location, and size of a data element. The data element contains the data
itself. All data descriptors are 12 bytes long and they contain four fields. You see an
illustration of a data descriptor in Figure 74, below.

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

--

Tag Number Meaning


200 8-Bit Image Dimensions

20 1 8-Bit Palette

202 8-Bit Raster Image

70 1 SD Dimension Record

702 SD Data

703 SD Scales

704 SD Labels

705 SD Units

706 SD Formats

707 SD MaxIMin Values

708 SD Coordinates

73 1 SD Calibration Data

732 SD Fill Value

1962 Vdata Description

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.

HDF Application Programming Interface


At a low level, HDF is a physical format for storing scientific data. At a high level,
HDF is a collection of utilities for manipulating and viewing data objects in HDF files.
These utilities are in the form of IDL procedures and functions, which in the aggregate
Reading and Writing HDF Data

HDF Data Objects


Raster Images
(&bit and 24-bit) Palettes
I I h

Scientific Data Sets


(multi-dimensional arrays)

VData
Annotations (tables of data)

This HDF file contains an example


of each HDF data type.

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

HDF Data Object IDL Prefix


24-bit raster image HDFDF24-
annotation data HDF-DFAN-
palette data HDF-DFP-
8-bit raster image HDF-DFR8-
scientific data HDF-SD-
Vdata HDF-VD-
Vgroup HDF-VG-
Table 12: It is possible to detemitze ~vhichIDL routines apply to whiclz HDF data
objects by lookirzg at the p r e f ? of the IDL rozctiizes zcsed in tlze HDF
applicatiort programnzilzg interface.

Working with HDF Files


The HDF programming model specifies that an HDF file be opened, manipulated, and
then explicitly closed. To open and close files, you must be familiar with the HDFfile
iclentifier. HDF files are uniquely identified by either a file name or a file
identification number, usually called the fileID orfi1eHandle in IDL documentation
and code.

Opening HDF Files


For example, to open a new HDF file for writing, you might type:

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

Closing HDF Files


You will see how to read and write data to HDF files in just a moment. But to close an
HDF file that was opened with the HDF-Open command, you just use the
HDF-Close command, like this:

Determining the Number of Tags in an HDF File


Once you have an HDF file open, you will want to know something about what is in
the file. You will learn several ways to collect information from the HDF file in this
chapter, but one of the ways you can learn about what is in the file is by determining
how many tags are in the file. (Remember that each data object has a data descriptor
with a tag field.)
You can imagine that if you opened an HDF file and knew absolutely nothing about it
one way to proceed would be to find out how many tags or data descriptors were in the
file, go to each one in turn, and use other API routines to inquire about each data
object. Fortunately, this is not necessary usually. Most of the time you will have a very
good idea about what is in an HDF file.
To determine how many tags are in an HDF file, you might use the HDF-Nuinber
command, like this:

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)

Working with Scientific Data Set HDF Files


The most popular API for HDF files is the SD or scientific data set interface. A
scientific data set (SDS) is any multidimensional array. Since this kind of data is used
extensively, it is a natural way of storing and manipulating data from within IDL. You
can see a synopsis of the IDL SD API in Table 13, below.
Each SDS is associated with a dimension record and a data type. The SDS data model
supports dimension scales as well as predefined and user-defined attributes. When an
SDS object is created the number and size of the dimensions that define its shape are
specified as well as the array's data type. The SDS object has an SDS rznme, which is a
case-sensitive string. Names are assigned when the SDS is created and cannot be
changed. The HDF interface will assign a name if you don't supply one when the SDS
is created. SDS names do not have to be unique in an HDF file, but if they are not it
will be difficult to sort out which SDS you want to interact with.
Working witlt Scientific Data Set HDF Files

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.

Optional Dimension Scales


Your SDS can optionally have a dimension scale associated with it. A dimension scale
is a sequence of numbers that divides the dimension into intervals. For example, a 2D
array of temperature values might have two dimension scales associated with it. One
might represent the longitudinal values of the 2D grid and the other might represent
the latitudinal values.

Optional User-Defined Attributes


Your SDS can also have optional user-defined attributes. Attributes contain auxiliary
information about a file or SDS. They are the so-called inetadata associated with HDF
files. It is data that describes the data. You can define any number of attributes.
Attributes can be attached to three types of objects: files, data sets, and dimensions.
These are referred to, respectively, asfile attributes, data set attributes, and diiner~sion
attl-ibutes.
File Attributes These attributes describe an entire file. They generally con-
tain information about the data in the file a s a whole. They
are sometimes referred to as global attributes.
Data Set Attributes These attributes describe features of the individual SDS.
Because their scope is limited to a specific SDS, they are
often called local attributes.
Dimension Attributes These attributes describe features of the individual SDS
dimensions. For example, the unit of a dimension is one of its
attributes.
Each object has its own attribute count, which identifies the
number of attributes associated with the object. The attribute
count begins at 0 and is increased by one for each additional
attribute associated with an object. Thus, each attribute has
its own "index", which is used to retrieve the information
contained in the attribute. Attribute names are treated like
dimension names, so you will want to provide meaningful
names for your attributes.
The IDL S D interface uses the same commands to access file and S D S attributes, so
you have to be careful to use the proper identifier. File identifiers access file attributes
and SDS identifiers access data set attributes. Dimension attributes are set with a
separate command.

Optional Predefined Attributes


SDS data sets and dimensions can be assigned optional predefined attributes. For
example, data sets and dimensions may have these predefined attributes:
Format A string that contains the format for the object data. The for-
mat will be used for printing or display of the data.
Label A label can be thought of as an independent variable name
for the object. Labels are often used as search keys.
Unit Units specify what the numbers associated with the data
mean.
Rending and Writing HDF Datn

Predefined Attributes HDF-SD-SetInfo P Writes predefined SDS attributes.

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

Opening HDF Files Containing Scientific Data Sets


If the HDF file you are going to be working with contains scientific data sets (or you
are only interested in the SDS data objects and information associated with them in
the file), you can open the file and initiate the SDS API at the same time with the
HDF-SD-Start command. For example, to open a new file for storing SDS objects,
you can type:

To open an SDS file for reading and writing, you can type:
sdFileID = HDF-SD-Start ( filename , /RdWr)

Closing HDF Files Containing Scientific Data Sets


Closing HDF files that were opened with the HDF-SD-Start command is relatively
straightforward, too. You use the HDFSD-End command. The only complication is
that if you have created or selected any SDS objects in the course of data
manipulation, you must terminate this access before you close the file. (You will learn
how to create and select SDS objects with the HDF-SD-Create and HDF-SD-Select
comnlands below.) If you don't terminate access to each SDS there is a chance the
data in the file will be corrupted when the file is closed. Access to the SDS object is
terminated with the H D F - S D - E I ~ ~ A C Ccommand.
~SS
The correct sequence of open, manipulation, and close commands for SDS HDF files
will look something like this:
sdFileID = HDF-SD-Start(filename, / ~ d w r )
newSDS-ID1 = HDF-SD-Create(sdFileID, 'New D a t a 1 , $