X

NV5 Geospatial Blog

Each month, NV5 Geospatial posts new blog content across a variety of categories. Browse our latest posts below to learn about important geospatial information or use the search bar to find a specific topic or author. Stay informed of the latest blog posts, events, and technologies by joining our email list!



NV5 at ESA’s Living Planet Symposium 2025

NV5 at ESA’s Living Planet Symposium 2025

9/16/2025

We recently presented three cutting-edge research posters at the ESA Living Planet Symposium 2025 in Vienna, showcasing how NV5 technology and the ENVI® Ecosystem support innovation across ocean monitoring, mineral exploration, and disaster management. Explore each topic below and access the full posters to learn... Read More >

Monitor, Measure & Mitigate: Integrated Solutions for Geohazard Risk

Monitor, Measure & Mitigate: Integrated Solutions for Geohazard Risk

9/8/2025

Geohazards such as slope instability, erosion, settlement, or seepage pose ongoing risks to critical infrastructure. Roads, railways, pipelines, and utility corridors are especially vulnerable to these natural and human-influenced processes, which can evolve silently until sudden failure occurs. Traditional ground surveys provide only periodic... Read More >

Geo Sessions 2025: Geospatial Vision Beyond the Map

Geo Sessions 2025: Geospatial Vision Beyond the Map

8/5/2025

Lidar, SAR, and Spectral: Geospatial Innovation on the Horizon Last year, Geo Sessions brought together over 5,300 registrants from 159 countries, with attendees representing education, government agencies, consulting, and top geospatial companies like Esri, NOAA, Airbus, Planet, and USGS. At this year's Geo Sessions, NV5 is... Read More >

Not All Supernovae Are Created Equal: Rethinking the Universe’s Measuring Tools

Not All Supernovae Are Created Equal: Rethinking the Universe’s Measuring Tools

6/3/2025

Rethinking the Reliability of Type 1a Supernovae   How do astronomers measure the universe? It all starts with distance. From gauging the size of a galaxy to calculating how fast the universe is expanding, measuring cosmic distances is essential to understanding everything in the sky. For nearby stars, astronomers use... Read More >

Using LLMs To Research Remote Sensing Software: Helpful, but Incomplete

Using LLMs To Research Remote Sensing Software: Helpful, but Incomplete

5/26/2025

Whether you’re new to remote sensing or a seasoned expert, there is no doubt that large language models (LLMs) like OpenAI’s ChatGPT or Google’s Gemini can be incredibly useful in many aspects of research. From exploring the electromagnetic spectrum to creating object detection models using the latest deep learning... Read More >

1345678910Last
«September 2025»
SunMonTueWedThuFriSat
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011
15504 Rate this article:
No rating

Dynamic Keyword Validation using ROUTINE_INFO

Anonym

  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.

Please login or register to post comments.