Beginner's IDL II




The objective of this course is to give an overview of the IDL environment at the beginner's level, assuming that the audience has little or no prior exposure to IDL. Here we will cover the basics of IDL, with an emphasis on using IDL as a tool for developing IDL programs. The course aims at to provide a survey of the various IDL features. It is expected that the audience will do the accompanying exercise and continue to explore the concepts using the resources provided. In-depth discussion on IDL features will be provided as individual chapters to be posted weekly at the website.




Programming in IDL


There are three types of IDL programs one can write:


Here we will focus on the first two. The IDL widgets will be discussed at a later time.


Variables


Variable names must start with a letter, but can include letters, digits, underscore characters, or dollar signs. A variable name can have as many as 255 characters. The size of a variable is limited only by the computer and operating system you are using.

Valid variable names:

read6_$ file
only_8_bit
ComputerType
variable2


Invalid variable names:

name.last
third%file
4th_list
$temp



Data Types


The valid data types (the name in parentheses is the name of an IDL function that can force a variable to have that particular data type):

Undefined
Byte (Byte)
Integer (Fix)
Long Integer (Long)
Floating Point (Float)
Double Precision Floating Point (Double)
Single Precision Complex (Complex)
Double Precision Complex (DComplex)
String (String)

Initializing Scalar Variables

byte_var = 0B
integer_var = 0
long_integer = 0L
float_var = 0.0
double_var = 0.0D
complex_var = Complex(0.0, 0.0)
dcomplex_var = DComplex(0.0D, 0.0D)
string_var = ' '




Array


Array can have up to 8 dimensions. Vectors and arrays of the different variable data types can be set or initialized with the following built-in IDL routines:

BytArr Create a byte array of the specified dimensions:
IDL> array = BytArr(256,256,10)
IntArr Create an integer array of the specified dimensions. For example, to create an integer vector of 10 elements: IDL> vector = IntArr(10)
LonArr Create a long integer array of the specified dimensions.
FltArr Create a floating point array of the specified dimensions.
DblArr Create a double precision floating point array of the specified dimensions.
ComplexArr Create a single precision complex array of the specified dimension.
DComplexArr Create a double precision complex array of the specified dimensions.
StrArr Create a string array of the specified dimensions.



Dynamic variable typing


IDL variables are typed dynamically, i.e., a variable will be typed to the same data type as the assigned value. Dynamic typing means that the expression on the right hand side of an assignment statement must be evaluated before a data type can be assigned to the variable on the left hand side of the statement. For example, the following will not work if you want to initialize an integer array to the value 1:

IDL> array = IntArr(10,12)
IDL> array = 1


If you print out the variable array, instead of getting a bunch of 1s, you will get

IDL> print,array
1


The reason is that when you type in array = 1, IDL thinks that the variable array is now defined as the integer 1.

The correct way to initialize an integer array to 1 is this:

IDL> array = IntArr(10,12)
IDL> array = array + 1


In this case, the second command adds the value of 1 to each element in the variable named array.

Another way to create an integer array with all the elements set to a value of 1 is to use the IDL Make_Array function:

IDL> array = Make_Array( 10, 12, Value=1, /Int)


In evaluating expressions, the variable data type that preserves the most precision is maintained. For example:

IDL> value = 16/5
IDL> print,value
3
IDL> help,value
VALUE INT = 3


The expression results in value being an integer scalar with a value of 3 (integer division). Note that you can learn the type of your variables by using the Help command.

Consider another example:

IDL> value = 16/5.
IDL> print,value
3.20000
IDL> help,value
VALUE FLOAT = 3.20000



Short Integers


The integer variables in IDL are short integers, i.e., they are two bytes in length. In many programming languages, integers are four bytes in length. Short integers sometimes surprise you in two different ways:

A counting variable exceeds the value capable of being expressed by a short integer. For example, the integer counter goes beyond the value 32, 767.
Since most programming languages use four-byte integers, passing data back and forth between IDL and an external program can cause your application to crash.

Always use long integers when in doubt.


Writing IDL Programs


Types of IDL programs
Main Program A main program unit consists of a sequence of IDL commands that ends in an End statement. There can be only one main program unit active in IDL at any time
Procedure A procedure is a self-contained sequence of IDL commands that performs a well-defined task. A procedure is identified by a procedure definition statement:

PRO procedure_name, a1, a2, .., an

To compile this procedure, type:
IDL> .compile procedure_name

To run this procedure, type:
IDL> procedure_name, a, b, ...,
Function A function is a self-contained sequence of IDL commands that performs a well-deinfed task and returns a value to the calling program unit when it is executed. There is always a RETURN statement in a function, and one data value (of the defined type) is returned. A function is identified by a function definition statement:

FUNCTION function_name, p1, p2, ..., pn

To compile the function, type:
IDL> .compile function_name

To use the function, type:
IDL> variable = function_name(a, b, ...)
IDL> print, variable


Procedures and functions may have a virtually unlimited number of arguments or parameters. There are two types of parameters: Positional and Keyword.

Positional PRO procedure, positional1, positional2, positional3
Keyword PRO procedure, positional1, positional2, Keyword=keyword_variable

It is absolutely essential to distinguish between the keyword name (Keyword, on the left hand side) and the variable that holds the value of the keyword (keyword_variable, on the right hand side).



Passing Parameters to Procedures and Functions


Parameters are passed to procedures and functions by value or by reference.


Parameters passed by value must be input parameters only, i.e., information can only pass from the calling routine to the procedure or function. Parameters passed by value cannot be modified in the calling routine by the called procedure or function.

Parameters passed by reference can be either input or output parameters, i.e., the information communicated between the calling routine and the procedure and function can be passed back and forth. Parameters passed by reference may be modified in the colling routine by the called proceudre or function.

Determining the number of parameters used in a call

To determine the number of parameters used in a call to a procedure or function, use the N_Params function inside the code for the routine. The function returns the number of positional parameters used in the call:

numberOfParameters = N_Params()

Determine if a keyword is passed

To determine if a keyword was used in the call, use the N_Elements function, which returns the number of elements contained in its argument.The key point about using N_Elements is that it returns 0 if the argument is undefined.


Program Control Statements

The BEGIN ... END Statement Blocks


For those who are familiar with C or Java, the BEGIN ... END statement blocks is just like the brackets {}. For example:

If (something_is_true) then begin

x = 2
y = 3

endif else begin

x=4
y=5

endelse


Notice that BEGIN blocks are ended with different kinds of END statements. Here is a list of the different types of END statements:

ENDCASE
ENDELSE
ENDFOR
ENDIF
ENDREPEAT
ENDWHILE


Control Statements

The IF ... THEN ... ELSE statements


The IF ... THEN ... ELSE control statement provides logical test. The ELSE part of the statement is optional:

IF (y eq 3) then x=2
IF (something_is_true) then x=2 else x=3

The FOR loops


The FOR loop executes a statement, or statements, a specified number of times:

for j=0,9 do number(j) = sin(region(j)*!Pi)


It is also possible to specify the counter increment. For example, to increment the counter by 2:

for j=0,20,2 do begin

number(j)=sin(region(j)*!Pi)
region(j)=0

endfor

The WHILE loops


The WHILE loop execute a statement while a test condition remains true:

while (cnt ne 0) do begin

print,cnt
cnt = cnt - 1

endwhile


A true condition in IDL is represented as:

A false condition is any condition that is not true.

The REPEAT ... UNTIL loops


The REPEAT ... UNTIL statement is similar to the WHILE statement. In this case a statement, or statements, is repeated until the test condition is true.

REPEAT num = num + 2 UNTIL (num gt 20)

The CASE statements


The CASE statement is used to select one, and only one, statement for execution, depending upon the value of the test expression. The ELSE clause of the CASE statement is optional.

CASE test OF

0: print, 'Value is 0'
1: x = test
else: y = test

ENDCASE


Notice that the ELSE clause, if it is used, requires a colon after it, unlike the ELSE clause in an IF statement.

The GOTO statement


IDL allows you to use GOTO statements in your program. The GOTO statement allows you to jump to a program label:

GOTO, Finish
...
...
Finish: RETURN


Notice that the label requires a colon after the label name.

Common Blocks


IDL allows you to create common blocks of variables. Any program unit can gain access to variables in the common block by declaring the common block in the program unit. The common block is declared by specifying a common block name and the list of variables that belong in the common block:

COMMON common_block_name, var1, var2, ..., varn

For example, to create a common block name date, with the variables day, month, and year:

COMMON date, day, month, year


Compiling and Using User-Written Routines


To compile a user-written routine, use the .Run, .RNew, or .Compile executive commands.

IDL> .run routine.pro
IDL> .rnew routine.pro
IDL> .compile routine.pro


The filename is case sensitive on UNIX operating systems.

After a routine is compiled, it is used exactly as you use any other IDL procedure or function.

Rules for compiling procedures and functions

The first rule to remember is that .Run doesn't run anything except a main program! And IDL runs a main program immediately after it compiles it. Any program module below a main program could not possibly get compiled in IDL.

.Compile is used to compile procedures and functions, and it doesn't run anything.


If you simply type the name of a procedure or function, and a module by that name has not been previously compiled, IDL will automatically search for a file with the same name as the module it is trying to resolve, following the IDL file path. If IDL finds a file with the same name and a .pro extension, it will compile the file until it gets to a module that has the same name as the file. At that point, it will run the module.

If you simply type the name of a routine, IDL will only compile the file down to the routine that has the same name as the file. Any routines below it in the file will not be compiled. In a file that contains multiple modules, the module that will have the same name as the file must be the last module in the file.


Also note that in case of any problem, the RETALL command will return you to the IDL main program level.


Writing a Short IDL Program


The best way to learn to write an IDL program is to simply write one.

Here we will write a program named Plot_It that is similar to the built-in IDL command Plot. Also, we will provide the ability to plot the data with a particular color index that we will specify with a Color keyword. We will allow the ability to plot the data either as a line or as plus (+) symbols. We will select the symbol drawing method by means of a Symbol keyword.

Step 1: Defining the name of the procedure and its parameters

Using your favorite text editor, create this file: plot_it.pro

We will create this program as a procedure with two input parameters and two keywords.

PRO Plot_It, p1, p2, Color=color, Symbol=yes


Here, p1, p2 are the input parameters, and Color is the keyword with color as the keyword variable. Similarly, Symbol is the keyword and yes is the symbol keyword variable.

Step 2: Handling Errors

By default, when an error occurs in a program module, IDL is left in the context of the program that caused the error. To make the program to return to the main program level, use the On_Error procedure:

On_Error, 1 ; Return to the main program level.

Step 3: Checking for the Number of Positional Parameters

We want this program to behave like Plot, in that we want the possibility of either specifying just the dependent data, or both the dependent and independent data together. In order to do so, we will need to know how many positional parameters were provided at runtime. We will use the function N_Parms to figure it out:

np = N_Params()


There are 4 possibilities here: (1) Plot_It was called with no parameters, (2) it was called with one parameter, (3) it was called with two parameters, or (4) it was called with more than two parameters. The program will have to check for these possibilities, and we will use the CASE statement here:

case np of

0: begin

print,'Incorrect Number of Parameters'
return

1: begin

dep = p1
indep = findgen(n_elements(p1))
end

2: begin

dep = p2
indep = p1

end

endcase

Step 4: Checking for Keyword Parameters

If the user specified a keyword parameter for the Color keyword, then we are all set. Else, we will have to define a default color.

if (n_elements(color) eq 0) then color = !D.N_Colors-1


Step 5: Drawing the Plot

We first load a color table, and use the Plot command to draw a plot without data (because we want the data line drawn in a color that may be different from the axes colors). We will overplot our data in the color we want to use with the OPlot command. Notice how we use the PSym keyword of the OPlot command to handle the input from the Symbol keyword in Plot_It. If the user of Plot_It uses the Symbol keyword, the variable yes will be defined and non-zero, in which case PSym will be set equal to 1 (+ signs). If Symbol is not used, the variable yes will be undefined in Plot_It and PSym will be set to 0 (no symbol).

loadct, 5
plot, indep, dep, /nodata
oplot, indep, dep, Color = color, PSym = Keyword_Set(yes)

The completed program code

PRO Plot_It, P1, P2, Color=color, Symbol = yes

On_Error, 1
np = N_Params()
CASE np OF

0: begin
print,'Incorrect Number of Parameters'
return
end
1: begin
dep = p1
indep = findgen(n_elements(p1))
end
2: begin
dep = p2
indep = p1
end

endcase
if (n_elements(color) eq 0) then color = !D.N_Colors-1
loadct, 5
plot, indep, dep, /nodata
oplot, indep, dep, Color = color, PSym = Keyword_Set(yes)

end

Compiling and Running the Program


Save the file and then inside IDL, compile and run the program. You will have to first create a dataset and pass it to the program. Test and make sure it works in all its possibilities.



Exercise

Convert the steps from the first exercise into an IDL program. This program will take in 5 arguments: the name of the input file (strings need to be placed within quotes), the number of rows, the number of columns, the smooth window size, and a keyword to allow the user to decide whether or not a continuum should be fitted to the data.

The program will not need to generate a postscript plot. Generate the postscript yourself by using the program you just wrote.

Finally, modify the program to allow you to toggle between velocity and wavelength scale.

A sample solution is available here (after 11/16/98).



Paul Lee
updated: 11/16/98