Some IDL Tools

Here are some tools I wrote in the IDL language from Research Systems, Inc.

Color management

.Xdefaults

First off, this only matters if you run IDL on displays with different characteristics (e.g. 8-bit and 24-bit color); the rest of the class can go ahead to the next section.

IDL recently (around version 6.2) changed how it uses and stores defaults. Things that used to be manageable no longer are. The right way to do this was to have an .Xdefaults which set the appropriate values for whatever display you were currently using (of course ssh completely breaks Xdefaults because programs don't know what display they are running on). The new IDL scheme of storing things in files in .idl means customizing settings based on display is impossible.

As far as I can tell, the only thing to do is to call a routine from .idl_startup that is full of getenv()'s and spawn,'xdpyinfo...' that figures out where it is running and has hardcoded values for graphics device type and number of colors (or reads these from environment variables).

Given that such severe changes happened in a minor release (between 6.0 and 6.2), I imagine that it will change more soon.

col_define

IDL is great, but its facilities for managing colors are horrible. In particular there is no way to use colors which works on both 8 bit and 24 bit graphics without major kludging. I wrote col_define.pro to solve this problem.

The routine determines what the depth of the graphics device is and defines a system variable !col that you use to specify colors. For example, plot,x,y,color=!col.green will generate a plot that is green or as close to green as possible in the current color table. All the colors defined in a normal rgb.txt are available, e.g. oplot,x,y,col=!col.darkseagreen3.

The new system variable also emulates a color table on 24-bit graphics devices. To emulate a 32-element color table using IDL's "rainbow" color table (number 39), run col_define,39,32. The color table elements are stored in a vector !col.ct. Plot using the color table like this: for i=0,31 do oplot,x[i,*],col=!col.ct[i]. The output will be identical whether you are using 8-bit or 24-bit graphics.

Here is an example.

It should now work for 16-bit graphics too. IDL with 16-bit graphics under XFree86 is very strange. Actually, it is even weirder than I thought: see comments in code. The handling of 16 bit graphics has changed IDL 5.5. I have not tested the code to see what it does now.

TVSCL for 24-bit graphics

In IDL on 24-bit graphics devices, tvscl makes grey plots. The routine mytvscl makes color plots on a 24-bit graphics display, with the option of picking a color table and number of colors by passing options. All options and behavior of tvscl are (should be) operational.

Try

tvscl,dist(100),0
mytvscl,dist(100),1
mytvscl,dist(100),2,ct=1
mytvscl,dist(100),3,n=8

to see the difference.

More little stuff

This is mostly dumb little stuff. If it wasn't put into little routines I would just keep writing the same little bits over and over.

Find the phase of a complex number (oddly, atan(z) did this for complex z in IDL 5.4; it doesn't in 5.5)

function cmphase,z,degrees=degrees
   
   If n_params() eq 0 then begin
      print,"  usage:  cmphase(z[,/degrees]"
      return,0
   endif
   
   phase = atan(imaginary(z),float(z))
   if keyword_set(degrees) then  $
     phase=phase/!pi*180
   return,phase
end

Square root of complex numbers.

function ccsqrt,z
  ;; This function calculates the square root of the elements in the
  ;; array z.  Moreover, if z is approximately smooth, then the
  ;; returned array will be approximately smooth.  This is done by
  ;; arbitrarily choosing which root to return.  In any case, the
  ;; square of the value returned will equal z.
  
  r = abs(z)
  th = cmphase(z)
  nz = n_elements(z)
  
  ;; easy part is the sqare root of the amplitude
  sr = sqrt(r)
  
  ;; we want the phase to vary approx continuously and go above 2pi or
  ;; wherever it needs to go. 
  
  ;; find phase jumps.
  p = round((th[1:nz-1]-th[0:nz-2])/(2*!pi))
  pt = total(p,/cumulative)
  
  th = th-[0,pt]*2*!pi
  
  sz = sr*exp(complex(0,th/2))
  return,sz
end

Convert number to string padded with zeros to a certain length. Useful for generating file names in a loop, as in openw,unit,basename+zpstring(index,3).

function zpstring,num,lngth
;; Returns the integer num in a string  with the length padded with
;; zeros on the  left for a total length of lngth
  if not ( (size(num))(1) eq 2 and (size(lngth))(1) eq 2 ) then begin
    print,'  usage: astring=zpstring(num,lngth)'
    print,'   e.g.: print,zpstring(2,3)'
    print,'         002'
    return,''
  endif
  
  ndig = floor(alog10(num))+1
  if ndig gt lngth then begin
    print,'zpstring: input ',strtrim(string(num),2), $
        ' is longer than length=',strtrim(string(lngth),2)
    return,''
  endif
  
  if lngth gt ndig then begin
    s1 = string(bytarr(lngth-ndig)+48B)
  endif else begin
    s1 = ''
  endelse
  
  s2 = strtrim(string(num),2)
  
  return,s1+s2
end

What value of i gives array[i] closest to value?

function findindex,array,value
  if n_params() eq 0 then begin
    print,'  usage: z=findindex(array,value)'
    return,-1
  endif
  dummy = min(abs(array-value),indy) 
  return,indy
end

Make idl beep. Useful when running long analysis. IDL> LongRoutine & bleep,n=3 will make the terminal beep three times when the routine returns, letting me know that I should pay attention to it.

pro bleep,n=n
  ;; this beeps.  Duh.
  if n_elements(n) eq 0 then $
      n = 1
  for i=1,n do begin
    print,string([7B]),format='(1A,$)'
    wait,0.8
  endfor
  ;; it is less useful if terminal is set to visual bell
  return
end

Put a list in random order. This implementation is actually slightly clever. Or maybe there are just a lot of dumb ways to implement this. (OK, I just read that this is the Fisher-Yates algorithm as implemented by Durstenfeld. But I swear I thought it up myself.)

pro randomize,a
  n = n_elements(a) 
  if n lt 1 then begin
    print,' usage: randomize,SomeArray'
    print,'   Elements of SomeArray are placed in random order'
    return
  endif
  for i=n-1,1,-1 do begin
    ;; pick at random one of the first i elements
    indy = floor(randomu(seed)*(i+1))
    ;; move this element to the ith position
    tmp = a[i]
    a[i] = a[indy]
    a[indy] = tmp
  endfor
end

Empty the keyboard buffer. Useful to run before you wait for keypress, because get_kbrd(1) will take a key press from the buffer, if there is one; it does not necessarily wait for a new key press.

pro empty_keyboard_buffer
   repeat begin
      ans = get_kbrd(0)
   endrep until ans eq ''
   return
end

A Hint

I can never remember the arguments to routines, so I find it useful to have a routine called with no arguments print a usage message and return harmlessly. If a routine is more likely to be called from another routine, rather than from the command line, the check for improper arguments emits a message instead, stopping everything in its tracks to give you a chance to figure out what went wrong.

Last updated Dec 13 2007.