17

Apr

2014

Nesting a hash within itself

Author: Benjamin Foreback

Lists and hashes have always been one of my favorite tools in IDL due to their versatile nature. A cool trick is that a hash can be stored recursively within itself. For example:


h = hash()
h['myself'] = h


With only level, this may not be particularly useful. However, this trick can be powerful for creating loops of information. Take days of the week, for instance. For fun, I'll use a DICTIONARY instead of a traditional hash, which has the convenience of the dot syntax for access.


Monday = dictionary('today', 'Monday')
Tuesday = dictionary('today', 'Tuesday')
Wednesday = dictionary('today', 'Wednesday')
Thursday = dictionary('today', 'Thursday')
Friday = dictionary('today', 'Friday')
Saturday = dictionary('today', 'Saturday')
Sunday = dictionary('today', 'Sunday')


Now define each day's tomorrow:


Monday.tomorrow = Tuesday
Tuesday.tomorrow = Wednesday
Wednesday.tomorrow = Thursday
Thursday.tomorrow = Friday
Friday.tomorrow = Saturday
Saturday.tomorrow = Sunday
Sunday.tomorrow = Monday


We can now loop through these days indefinitely. If we take Monday and request "tomorrow" seven times, we get Monday back again:


Next_Monday = Monday.tomorrow.tomorrow.tomorrow.tomorrow.tomorrow.tomorrow.tomorrow


We can store additional information in each of these hashes/dictionaries, such as a schedule for each day. 


Monday.Morning = 'Math 251, 9:00-10:00, Room 304'
Tuesday.Morning = 'Writing 301, 10:00-11:30, Room 211'

etc.

If today = Monday, then we can get today's morning schedule calling today.Morning. At the end of the day on Monday, we can say today = today.tomorrow, and tomorrow is the new today. Likewise, if we want today's morning schedule, we call today.Morning

Loops, routines, and cycles, are very common in the world as well as in scientific data. This method of information storage can be used for displaying radar data loops, with each frame stored in a hash that contains a "previous" and "next" frame. Additionally, it could be used for modeling states of pendulum or circular motion. It can be used for storing geographic information, such as time zones containing UTC offsets. There are probably many additional use cases.

As always, use caution with recursion. It is always unfortunate to find yourself killing code because it is stuck in an infinite loop. That said, be aware that JSON_SERIALIZE will not work in the examples above. Fortunately, given this use-case, there is no significant need to serialize the hashes' information into a string.

Comments (0) Number of views (97) Article rating: 5.0

Categories: IDL Data Point

Tags:

10

Apr

2014

Generating an Ellipsoid using IDL 8 Graphics

Author: David Starbuck

This blog post describes how you can use IDL to generate an ellipsoid such as the one shown below:

The MESH_OBJ, POLYGON, and PLOT3D routines were used to create this graphic, and the code that was used is shown at the bottom of the post (dj_ellipsoid_ng).

When using MESH_OBJ routine to create spherical object, you need to provide a two dimensional grid of radius values.The rows of this grid correspond to the longitude which goes from 0 to 360 degrees (theta). The columns of this grid correspond to the latitude which goes from -90 to 90 degrees (phi).

Since, MESH_OBJ requires the grid of radius values, and the value of the radius depends on the latitude and longitude, you must first generate latitude and longitude grids.  To generate the longitude grid (theta), I first generated a 51 element array going from 0 to 360 and converted to radians (to be used with the COS and SIN routines):

theta_1d = (findgen(51)*360/50)*!dtor

Then, I used a combination of the CONGRID and TRANSPOSE routines to replicate the values from theta_1d into a 51 by 51 element array.  The first TRANSPOSE call “theta_1d” makes the CONGRID routine accept it as a 2D array. 

theta = congrid(transpose(theta_1d),51,51)
theta = reform(transpose(theta))

I created the latitude grid (phi), using the same method: 

phi_1d = ((findgen(51)-25)*180/50)*!DTOR
phi = congrid(TRANSPOSE(phi_1d),51,51)

The next step is to determine the radius value for each longitude and latitude function.  To calculate the radius, I used the equation for an ellipsoid shown below:                

                                   

I converted this equation to spherical coordinates, and then solved it for “r”:

Using this result, I wrote the function “dj_get_rad”, which accepts the latitude and longitude grids and returns a grid of the radius values.  I then used MESH_OBJ with the radius values returned from this function to get the vertices and polygons of the ellipsoid.  

The final step is to create a visual of the data. To do this, I called PLOT3D with the NODATA keyword to generate the empty 3D dataspace. Then, I called POLYGON with the vertices argument set the vertices returned by MESH_OBJ and the CONNECTIVITY keyword set to the polygons returned it.

The "dj_ellispoid_ng" routine shows how this can be done. It requires 3 arguments which correspond to the "a", "b" and "c" parameters in the ellipsoid equation.Some sample images generated with different parameter values are shown below:

IDL> dj_ellipsoid_ng, 2,1.5,1 ;a=2,b=1.5,c=1



IDL> dj_ellipsoid_ng, 2,2,1


IDL> dj_ellipsoid_ng, 1,1,2

function dj_get_rad, theta, phi, a,b,c
  compile_opt idl2
   
    ;use the formula for the ellipsoid to
    ;determine the radius for various values
    ;of theta and phi.
    radius = sqrt(((a*b*c)^2)/( $
    b^2*c^2*(cos(theta)^2)*(cos(phi)^2)+ $
    a^2*c^2*(sin(theta)^2)*(cos(phi)^2)+ $
    a^2*b^2*(sin(phi)^2)))

    return, radius
end



pro dj_ellipsoid_ng, a, b, c, data=data
  compile_opt idl2
 
  ;create a grid of theta values
  ;these are the longitude of the of the
  ;ellipsoid and go from 0 to 360 degrees
  theta_1d = (findgen(51)*360/50)*!dtor
  theta = congrid(transpose(theta_1d),51,51)
  theta = reform(transpose(theta))
 
 
 
  ;create a grid of phi values. these
  ;are the latitude of the ellipsoid and go
  ;from -90 to 90 degrees
  phi_1d = ((findgen(51)-25)*180/50)*!dtor
  phi = congrid(transpose(phi_1d),51,51)
 
  ;use "get_rad" function to determine the
  ;radius values at each point of the theta
  ;and phi grid
  radius = dj_get_rad(theta,phi,a,b,c)
  ;radius = get_rad(theta,phi,a,b,c)
            
  ;create a mesh using the radius values
  ;this ouputs the vertices (vert) and polygons
  ;that will be used to generate the plot
  mesh_obj,4,vert,pol,radius
 
  ;output the data
  if (arg_present(data)) then begin
     data=vert
  endif

 
  ;determin the biggest value and create a scale from it
  m = max([a,b,c])
  scale = (findgen(10)-5)*m/5

  ;plot the scale and axis with no data. set clip=0, to
  ;prevent edges of ellipsoid from getting cut off.
  p_scale =plot3d(scale,scale,scale,/nodata,clip=0,$
                  aspect_z=1, aspect_ratio=1)
 
 
  ;use the polygon to plot the mesh. use the
  ;vertices and polygons output from the mesh_obj
  ;to fill out the data argument and connectivity keyword.
  ;use bright green as the color.
  p = polygon(vert,connectivity=pol,/data,$
              clip=0,fill_color=[0,255,0])
 
end

;main level program
;for some reason, if 'a; does
;not equal 'b', the ellipsiod
;comes out messed up
dj_ellipsoid_ng,1,1.1,1.2,data=out_vert
end



Comments (1) Number of views (225) Article rating: 4.5

3

Apr

2014

Using the Clipboard, Static Methods and ZLIB compression in IDL 8.3

Author: Jim Pendleton

When the Exelis Professional Services Group is contracted to author "large" applications for clients, we often have the luxury of building full installers that we use to place a variety of resource files and directories onto disk, along with the main functionality of an application or toolset.

In this environment, the ROUTINE_FILEPATH  function is a huge help for bootstrapping the application so it can find its resource files relative to its installation directory.

For smaller projects, however, it is often more convenient if all the resources, such as bitmap button images, can be bundled into a single file, in particular a compiled IDL SAVE file. This reduces the complexity of installation and testing.

Various routines exist for turning, say, bitmap images into IDL source code. I will show a version below that employs the new IDL 8.3 function for lossless, in-memory ZLIB compression of the image data. It also uses the new Clipboard static class so you can paste the generated code into a text editor with a single button click.

Finally, we'll investigate a utility for automatically generating an entire class of static methods which store a directory of images, greatly simplifying their accessibility.

The routine listed below takes as input an image in the form of a BYTARR along with a string that will be applied as the name of a new IDL function. It generates PRO code and places it into the system's clipboard as ASCII text. You can then paste the generated code directly into an editor window, which you will compile and execute.

PRO ImageToCode, image, functionName
 d = SIZE(image, /DIMENSIONS)
 z = ZLIB_COMPRESS(image)
 b64 = IDL_BASE64(z)
 l = LIST()
 l.Add, 'FUNCTION ' + functionName
 dims = STRJOIN(STRTRIM(D, 2), ',')
 l.Add, 'lines = LIST()'
 FOR i = 0L, STRLEN(b64) - 1, 66 DO $
    l.Add, "lines.ADD, '" + STRMID(b64, I, 66) + "'"
 l.Add, 's = IDL_BASE64(STRJOIN(lines.ToArray(/NO_COPY), ""))'
 l.Add, 'd = ZLIB_UNCOMPRESS(s)'
 l.Add, 'RETURN, REFORM(d, ' + dims + ')'
 l.Add, 'END'
 Clipboard.Set, l.ToArray(/NO_COPY)
END

In order to make the output code for the image data we first call the ZLIB_COMPRESS function to create an in-memory version of the image bitmap, using lossless compression. To simplify the code we will make an initial assumption that the input image is a 24-bit (RGB) image in a BYTARR.

Because we want to create IDL source code from this, we convert the binary data produced by ZLIB_COMPRESS into an ASCII format with the IDL_BASE64 function. This is a useful trick for encoding any type of binary data that needs to be exchanged in an ASCII format, for example via standard JSON objects passed to and from an ENVI Services Engine instance.

In order to make the output PRO code more legible, the output string from IDL_BASE64 is broken up over multiple lines, each with a maximum length of 80 characters. 

The new function code we create performs the inverse operations, converting the string back to binary via IDL_BASE64, then regenerating the BYTARR binary data via the ZLIB_UNCOMPRESS function.  Notice that the REFORM function call is required because the ZLIB compression doesn't encode the dimensionality of the original input data.

Try this:

IDL> f = filepath('colorbar24.png', subdir=['resource','bitmaps'])
IDL> imagetocode, read_image(f), 'cbimage'

Simply open an editor window and paste the contents of the system clipboard to it.  The imagetocode routine has populated the clipboard with the text of the routine. 

The output should look like this:

FUNCTION cbimage
lines = LIST()
lines.ADD, 'eJz6//8/w/9RPGLwuct3/udUTf2PLDZ32Y7/IIwsBlIDUossZuNX9H8gzQfpAamhFK'
lines.ADD, 'O7D4Yb+lf9r+lZ9b+ic9X/kraV/wtaVv7PaVz5P6Nuxf/UmhX/EytX/I8tW/4/snj5'
lines.ADD, '/9DC5f8D85b/981e9t8zc9l/17Rl/x2Tl/63TVz63zJu6aj5o+aPmj9qPknmj2L6Yw'
lines.ADD, 'AAAAD//wMA32owCw=='
s = IDL_BASE64(STRJOIN(lines.ToArray(/NO_COPY), ""))
d = ZLIB_UNCOMPRESS(s)
RETURN, REFORM(d, 4,24,24)
END

Of course the translation routine could have been designed to write its output to a file directly, but where would the fun be in that?

Save and compile the new file, cbimage.pro.

Display the output by executing the function in the context of the IMAGE function.

IDL> image(cbimage())

Now for some additional fun, the algorithm can be adapted easily to create an entire class of static methods.

For example, I may want to extend this functionality to create a single static class that contains all the bitmaps in the IDL distribution's resource\bitmaps directory. Using this mechanism, I would no longer need to include the files in that directory in my application distribution. I would simply include my object class, whether in source code or in the form of compiled code in a SAVE file.  I can simply reference each bitmap via the static class method name.

PRO ImagesToStaticClass, directory, className
files = FILE_SEARCH(FILEPATH('*', ROOT = directory))
l = LIST()
FOREACH file, files DO BEGIN
  CATCH, errorNumber
  IF (errorNumber NE 0) THEN BEGIN
    CATCH, /CANCEL
    CONTINUE
  ENDIF
  image = READ_IMAGE(file, r, g, b)

  CATCH, /CANCEL
  IF (N_ELEMENTS(image) LT 2) THEN CONTINUE
  s = SIZE(image, /STRUCTURE)
  IF (s.N_DIMENSIONS eq 2) THEN BEGIN
    newImage = BYTARR(3, s.Dimensions[0], s.Dimensions[1])
    newImage[0, *, *] = r[image]
    newImage[1, *, *] = g[image]
    newimage[2, *, *] = b[image]
    image = TEMPORARY(newimage)
  ENDIF

  image = TRANSPOSE(TEMPORARY(image), [1, 2, 0])
  d = SIZE(image, /DIMENSIONS)
  baseName = FILE_BASENAME(file)
  methodName = IDL_VALIDNAME(STRMID(baseName, 0, $
    STRPOS(baseName, '.', /REVERSE_SEARCH)))
  IF (methodName eq '') THEN CONTINUE
  z = IDL_BASE64(ZLIB_COMPRESS(image))
  l.Add, 'FUNCTION ' + className + '::' + methodName
  l.Add, 'COMPILE_OPT STATIC'
  dims = STRJOIN(STRTRIM(D, 2), ',')
  l.Add, 'lines = LIST()'
  FOR i = 0L, STRLEN(z) - 1, 66 DO $
    l.Add, "lines.ADD, '" + STRMID(Z, I, 66) + "'"
  l.Add, 's = IDL_BASE64(STRJOIN(lines.ToArray(/NO_COPY), ""))'
  l.Add, 'd = ZLIB_UNCOMPRESS(s)'
  l.Add, 'RETURN, REFORM(d, ' + dims + ')'
  l.Add, 'END'
ENDFOREACH
l.Add, 'PRO ' + className + '__DEFINE'
l.Add, '!null = {' + className + ', INHERITS IDL_Object}'
l.Add, 'END'
Clipboard.Set, l.ToArray(/NO_COPY)
END

Copy, save, and compile the source shown above.

Execute the routine giving it the name of a directory containing image files and the name of the object class to generate.  For example,

IDL> imagestostaticclass, FILEPATH('', SUBDIR=['resource','bitmaps']), 'IDLBitmaps'

Paste the routine's output to an editor file and save it as idlbitmaps__define.pro, then compile it.

To create an "open folder" bitmap widget button, for example, I can simply specify a call to the static method whose name corresponds to the original image file name, modulo a call to IDL_VALIDNAME.

IDL> tlb = WIDGET_BASE()
IDL> b = WIDGET_BUTTON(tlb, VALUE=IDLBitmaps.Open(), /BITMAP)
IDL> WIDGET_CONTROL, tlb, /REALIZE

Many of the bitmap files in IDL's resource directory are palletized, single plane images each with an associated color look-up table. For the sake of expediency and code simplicity at the expense of code size in ASCII characters, they're converted by the utility from single-plane images to RGB format before compressing them.

Notice that the utility skips any files which cannot be read successfully by the READ_IMAGE function. Other than that there is no robust error checking, an exercise left for the reader.

The routine returns the images dimensioned [x,y,3], the format required by WIDGET_BUTTON.

Comments (0) Number of views (2117) Article rating: No rating

Categories: IDL Data Point

Tags:

27

Mar

2014

Dynamic Keyword Validation using ROUTINE_INFO

Author: Brian Griglak

  In IDL, there are three variants of the _EXTRA keyword: _EXTRA, _REF_EXTRA, and _STRICT_EXTRA.  The _EXTRA and _REF_EXTRA keywords are used to define the signature of the routine, while you can use either _EXTRA or _STRICT_EXTRA when you invoke any routine.  As my colleague Jim Pendleton wrote about a few years ago, the _REF_EXTRA keyword is a potential improvement over plain old _EXTRA.  This is because it uses pass by reference semantics instead of the pass by value semantics that _EXTRA does, so you don’t make copies of the variables at each function call.  There is the downside that the values passed by _REF_EXTRA are mutable, so some care needs to be taken there, but it can save you a lot of memory and time allocating those copies.

  The downside of both _EXTRA and _REF_EXTRA is that they are generic grab bags that allow the caller to call your function with any superfluous keywords they want, with no way of knowing which keywords are actually used and which are extraneous.  Enter the _STRICT_EXTRA keyword, which is discussed in the help documentation of “Keyword Inheritance”.  Normally when you call a routine, you use _EXTRA keyword as the way to pass in the collection of keywords to the routine.  Any keywords in the _EXTRA bag that are part of the routine signature will be properly mapped, and whatever is left will be captured by _EXTRA/_REF_EXTRA if it’s present, or dropped on the floor otherwise.  If you were to call the same routine using _STRICT_EXTRA instead of _EXTRA, then it will throw an error if there are any keywords that do not map to the routine signature.  We can see this in action in this contrived example, which uses minimal procedures to illustrate the validation logic:

pro callByValue, _EXTRA=extra
  help, extra
end
pro callByReference, _REF_EXTRA=refExtra
  help, refExtra
end
pro callWithNoExtra, FOO=foo
  help, foo
end

pro callWrapper, _REF_EXTRA=extra
  callByValue, _EXTRA=extra
  callByReference, _EXTRA=extra
  callWithNoExtra, _EXTRA=extra
end
pro callStrictWrapper, _REF_EXTRA=extra
  callByValue, _STRICT_EXTRA=extra
  callByReference, _STRICT_EXTRA=extra
  callWithNoExtra, _STRICT_EXTRA=extra
end
pro extra_tests, _REF_EXTRA=extra
  callWrapper, PI=!pi
  callStrictWrapper, PI=!pi
end

 

The output from running extra_tests is:
** Structure <1307e5a0>, 1 tags, length=4, data length=4, refs=1:
   PI              FLOAT           3.14159
REFEXTRA        STRING    = Array[1]
FOO             UNDEFINED = <Undefined>
** Structure <1307e650>, 1 tags, length=4, data length=4, refs=1:
   PI              FLOAT           3.14159
REFEXTRA        STRING    = Array[1]
% Keyword PI not allowed in call to: CALLWITHNOEXTRA
% Execution halted at: CALLSTRICTWRAPPER   32 C:\Users\brian\IDLWorkspace83\Default\extra_tests.pro
%                      EXTRA_TESTS        43 C:\Users\brian\IDLWorkspace83\Default\extra_tests.pro
%                      $MAIN$

  The _EXTRA and _REF_EXTRA keywords are able to accept the PI keyword without incident.  When callWrapper invokes callWithNoExtra, the PI keyword is dropped on the floor, and FOO is left undefined.  But when callStrictWrapper invokes callWithNoExtra, the PI keyword does not line up with the routine signature and an error is thrown.  It is important to note that callByValue and callByReference work perfectly fine with _STRICT_EXTRA, as their _EXTRA and _REF_EXTRA keywords respectively will swallow up the PI keyword no problem.

  So _STRICT_EXTRA has its limitations, particularly in the case where you want to call more than one routine that doesn’t include a form of _EXTRA.  To accomplish this we need to query the IDL runtime to get information about the routine signatures using the powerful ROUTINE_INFO function.  When you use this function with its /PARAMETERS keyword, it will return a structure that lets you get the list of all the keywords in a given routine’s signature.  Let’s look at a simple example to see what ROUTINE_INFO returns:

pro myPro, PARAM1=p1, PARAM2=p2, PARAM3=p3
  print, 'in myPro'
  help, p1, p2, p3
end

function myFunc, PARAM2=p2, PARAM4=p4
  print, 'in myFunc'
  help, p2, p4
  return, 0
end

IDL> info1 = Routine_Info('myPro', /PARAMETERS)
IDL> info2 = Routine_Info('myFunc', /PARAMETERS, /FUNCTIONS)
IDL> info1
{
    NUM_ARGS: 0,
    NUM_KW_ARGS: 3,
    KW_ARGS: ["PARAM1" "PARAM2" "PARAM3"]
}
IDL> info2
{
    NUM_ARGS: 0,
    NUM_KW_ARGS: 2,
    KW_ARGS: ["PARAM2" "PARAM4"]
}

  The implied print output for a struct is convenient as it gives you the tags, but also expands arrays, which neither help nor print will do for a struct.  You’ll notice that I had to add the /FUNCTIONS keyword when I was requesting info about a function instead of a procedure.  So I can use the KW_ARGS member of the info struct to identify which members of the _REF_EXTRA bag to use for each routine invocation.  I can also use it to verify that there aren’t any superfluous keywords passed into the wrapper.

  Once you’ve verified that the keywords passed into the wrapper are valid, you then need to construct the subset of values that are to be passed into each wrapped routine.  The way to do this dynamically is to manually construct a struct to pass into the _EXTRA keyword.  We do this incrementally by adding only those keywords and values to a struct using CreateStruct to append new key/value pairs.  When using _REF_EXTRA, you get a string array that includes the keywords used to invoke your method.  To get the values for each keyword you have to use Scope_VarFetch with its /REF_EXTRA keyword to get a reference to that value in the local scope.  Here is an example wrapper for the myPro and myFunc routines defined above:

function myDynamicReferenceWrapper, _REF_EXTRA=refExtra
  compile_opt idl2
  ; first make sure _REF_EXTRA is defined, bail if not
  if (~ISA(refExtra)) then return, -1

  info1 = Routine_Info('myPro', /PARAMETERS)
  info2 = Routine_Info('myFunc', /PARAMETERS, /FUNCTION)

  ; check for invalid keywords in _REF_EXTRA
  foreach keyword, refExtra do begin
    if ((Total(keyword eq info1.KW_ARGS) eq 0) && $
        (Total(keyword eq info2.KW_ARGS) eq 0)) then begin
      Message, 'Invalid keyword ' + keyword
    endif
  endforeach

  ; call myPro with the appropriate keywords from _REF_EXTRA
  extra1 = {}
  foreach keyword, info1.KW_ARGS do begin
    w = where(keyword eq refExtra, found)
    if (found gt 0) then begin
      value = Scope_VarFetch(keyword, /REF_EXTRA)
      extra1 = Create_Struct(extra1, keyword, value)
    endif
  endforeach
  myPro, _EXTRA=extra1

  ; call myFunc with the appropriate keywords from _REF_EXTRA
  extra2 = {}
  foreach keyword, info2.KW_ARGS do begin
    w = where(keyword eq refExtra, found)
    if (found gt 0) then begin
      value = Scope_VarFetch(keyword, /REF_EXTRA)
      extra2 = Create_Struct(extra2, keyword, value)
    endif
  endforeach
  return, myFunc(_EXTRA=extra2)
end

  If you had concerns about the wrapped methods modifying your variables and want to use _EXTRA instead of _REF_EXTRA, then there are a couple tweaks to this wrapper:

function myDynamicValueWrapper, _EXTRA=extra
  compile_opt idl2
  ; first make sure _EXTRA is defined, bail if not
  if (~ISA(extra)) then return, -1

  info1 = Routine_Info('myPro', /PARAMETERS)
  info2 = Routine_Info('myFunc', /PARAMETERS, /FUNCTIONS)

  ; check for invalid keywords in _EXTRA
  myExtraKeywords = Tag_Names(extra)
  foreach keyword, myExtraKeywords do begin
    if ((Total(keyword eq info1.KW_ARGS) eq 0) && $
        (Total(keyword eq info2.KW_ARGS) eq 0)) then begin
      Message, 'Invalid keyword ' + keyword
    endif
  endforeach

  ; call myPro with the appropriate keywords from _EXTRA
  extra1 = {}
  foreach keyword, info1.KW_ARGS do begin
    w = where(keyword eq myExtraKeywords, found)
    if (found gt 0) then begin
      extra1 = Create_Struct(extra1, keyword, extra.(w[0]))
    endif
  endforeach
  myPro, _EXTRA=extra1

  ; call myFunc with the appropriate keywords from _EXTRA
  extra2 = {}
  foreach keyword, info2.KW_ARGS do begin
    w = where(keyword eq myExtraKeywords, found)
    if (found gt 0) then begin
      extra2 = Create_Struct(extra2, keyword, extra.(w[0]))
    endif
  endforeach
  return, myFunc(_EXTRA=extra2)
end

  Either of these wrappers could be made truly generic by adding string array parameter that was the set of routine names you want wrapped, instead of having myPro and myFunc hardcoded.

Comments (0) Number of views (201) Article rating: No rating

Categories: IDL Data Point

Tags:

20

Mar

2014

Dynamic Plots Using an Equation String

Author: Nancy Rynes

The PLOT function's new Equation argument adds flexibility to the creation of plots in IDL, allowing you to create dynamic, interactive output.

The Equation argument on the PLOT function allows you to specify either a string containing an equation with variable X, or the name of an IDL function that accepts X as an input argument. The result of the equation (or the function) should be a one-dimensional array of Y coordinates to be plotted.

  • If Equation is an expression, then the EXECUTE function is called once with the X array. Note that in certain circumstances (such as the IDL Virtual Machine), you may not be able to use the EXECUTE function.
  • If Equation is a function name, then CALL_FUNCTION is called once, with the X array as an input argument. The function should return a one-dimensional result array.

Once IDL creates the plot output, if you then interactively adjust the plot range, IDL will automatically recompute the equation to cover the new range.

We'll use the BESELJ function in IDL to show how to use a Function String in the Equation Argument:

 

; Plot J Bessel Functions

pj0 = PLOT('BESELJ(X, 0)', XRANGE=[0.0, 50],  $

  XTITLE='X', YTITLE='$J_n(x)$ or $Y_n(x)$', $

  TITLE='J Bessel Function')

pj1 = PLOT('BESELJ(X, 1)', 'r2', XRANGE=[0.0, 50], /OVERPLOT)

pj2 = PLOT('BESELJ(X, 2)', 'b2', XRANGE=[0.0, 50], /OVERPLOT)

; Annotate the plot.

xcoords = [1, 1.66, 3]

ycoords = [.8, .62,.52]

labels = '$\it' + ['J_0','J_1','J_2'] + '$'

  t = TEXT(xcoords, ycoords, labels, /DATA)

 

If you run the code above, it should generate a graphic like this:

Once IDL creates the plot, test out its dynamic capabilities:

  • Try clicking with the middle mouse button on the graphic and panning around.
  • You can also use the mouse wheel to zoom in or out, or hold down the <Shift> key and draw a zoom box.
  • Change the plot range programatically at the IDL command line:

pj0.xrange=[0.0, 150]

As you change the plot range, IDL recomputes the equation with new X values that span the new range.

Comments (0) Number of views (228) Article rating: No rating

Categories: IDL Data Point

Tags:

12345678910 Last

MOST POPULAR POSTS

AUTHORS

Authors

© 2014 Exelis Visual Information Solutions