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!



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 >

From Image to Insight: How GEOINT Automation Is Changing the Speed of Decision-Making

From Image to Insight: How GEOINT Automation Is Changing the Speed of Decision-Making

4/28/2025

When every second counts, the ability to process geospatial data rapidly and accurately isn’t just helpful, it’s critical. Geospatial Intelligence (GEOINT) has always played a pivotal role in defense, security, and disaster response. But in high-tempo operations, traditional workflows are no longer fast enough. Analysts are... Read More >

Thermal Infrared Echoes: Illuminating the Last Gasp of a Dying Star

Thermal Infrared Echoes: Illuminating the Last Gasp of a Dying Star

4/24/2025

This blog was written by Eli Dwek, Emeritus, NASA Goddard Space Flight Center, Greenbelt, MD and Research Fellow, Center for Astrophysics, Harvard & Smithsonian, Cambridge, MA. It is the fifth blog in a series showcasing our IDL® Fellows program which supports passionate retired IDL users who may need support to continue their work... Read More >

A New Era of Hyperspectral Imaging with ENVI® and Wyvern’s Open Data Program

A New Era of Hyperspectral Imaging with ENVI® and Wyvern’s Open Data Program

2/25/2025

This blog was written in collaboration with Adam O’Connor from Wyvern.   As hyperspectral imaging (HSI) continues to grow in importance, access to high-quality satellite data is key to unlocking new insights in environmental monitoring, agriculture, forestry, mining, security, energy infrastructure management, and more.... Read More >

1345678910Last
8671 Rate this article:
3.8

Extending ENVI Extensions

Anonym

One of my favorite parts of being able to extend ENVI with IDL is that I have the ability to add custom buttons into ENVI's toolbox. These buttons are called extensions and I have made many of them during my time with NV5 Geospatial. I have created buttons for our tradeshow demos, examples for customers, and even for the Precision Ag Toolkit. Extensions in ENVI can you anything that you want them to. Most of the time this is using ENVI's dynamic UI for creating simple widgets that allow a user to select all the inputs and outputs for a task, but you can also just call IDL directly and instantiate some other GUI based application if you want.

While extensions are very easy to make (I just copy/paste old code I had and tweak it), they can be cumbersome when you have many extensions to create all at once. In addition to this, if you have some addition that you want added to all your extensions, then you have to go through each one by hand to change them. This is where getting creative with IDL programming comes in really handy because IDL can be used to automate the generation of buttons in ENVI. For this example, I decided to finally take a crack at dynamically creating X number of buttons from ENVI tasks without needing to write a separate extension for every one.

The basic idea is that I wanted a dynamic extension that would allow me to pass in any task, create a button, run the right task when I press the button, and offer the ability to display any and all results from the task that are EVNIRasters, ENVIROIs, or ENVIVectors (or shapefiles). If you're unfamiliar how to add extensions to ENVI, then here is a link to the docs which can provide some background information

http://www.nv5geospatialsoftware.com/docs/ENVI__AddExtension.html

Following the example in the docs, the way that I decided to proceed was to use the UVALUE for the buttons to pass in information about which task I wanted to create a dynamic UI for. I decided to use an orderedhash for my data structure with: the key is the task name, the value is a two element string array that represents the folder that we want the task to appear in the name of the button that you will see in ENVI's toolbox. Here is an example of what the data structure looked like:

;create an orderedhash of buttons that we want to create
buttons = orderedhash() 
buttons['ROIMaskRaster'] = ['/Regions of Interest', 'ROI Mask Raster']

At this point it just came down to looping over the hash correctly so that the buttons would be made in the right space. I chose to use the foreach loop because it makes it easy to get the key and value from hashes or orderedhashes all at the same time. Here is what that code looks like:

foreach val, buttons, key do begin
  e.AddExtension, val[1], 'better_extensions', $
    PATH = '/Better Extensions' + val[0], $
    UVALUE = key 
endforeach

That short code block will dynamically go through every entry in my orderedhash and then create the buttons with the right placement and task to be executed when clicked on. Note that I created a subfolder called 'Better Extensions' that would contain all the fancy buttons that the extension would create. Once I had this code figured out, I just needed to look at the actual procedure which would run the extension (I called it "better_extensions")

You can see the complete code for better_extensions below, but there are a few key points to mention about the code:

  • To get the task name for the button that was clicked, when creating the extension I used the UVALUE keyword to store the name. Then, in the better_extensions procedure, I get the event as an argument and can get the uvalue for the button using widget_control.

  • To create the task UI all I needed to do was the following:

        ;create a dialogue for the rest of the task
        ok = e.UI.SelectTaskParameters(task)
    
  • After that, I just needed to go through each task parameter, check for INPUT or OUTPUT and, if it was output, check for ENVIRaster, ENVIVector, or ENVIROI. I then saved these and displayed them in ENVI's current view if they were found in the output.

The main goal here was to show that, with a little bit of IDL programming, you can take your ENVI analytics to the next level. The code for the extension can be found below. Cheers!

Also, keep an eye out for my next blog where I'm going to talk about giving an old extension (from the online extensions library) a much needed update!

; Add the extension to the toolbox. Called automatically on ENVI startup.
pro better_extensions_extensions_init

  ; Set compile options
  compile_opt idl2

  ; Get ENVI session
  e = envi(/CURRENT)
  
  ;create an orderedhash of buttons that we want to create
  buttons = orderedhash() 
  
  ;sample ROI tools
  buttons['ROIMaskRaster'] = $
    ['/Regions of Interest', 'ROI Mask Raster']
  bttons['ROIToClassification'] = $
    ['/Regsions of Interest', 'Classification Image from ROis']
  
  ;classification tools
  buttons['ISODataClassification'] = $
    ['/Classification/Unsupervised Classification', 'ISOData Classification']
  buttons['ClassificationAggregation'] = $
    ['/Classification/Post Classification', 'Classification Aggregation']
  buttons['ClassificationClumping'] = $
    ['/Classification/Post Classification', 'Clump Classes']
  buttons['ClassificationSieving'] = $
    ['/Classification/Post Classification', 'Sieve Classes']
  buttons['ClassificationSmoothing'] = $
    ['/Classification/Post Classification', 'Classification Smoothing']
  buttons['ClassificationToShapefile'] = $
    ['/Classification/Post Classification', 'Classification to vector']
  
  ;radiometric correction tools
  buttons['DarkSubtractionCorrection'] = $
    ['/Radiometric Correction', 'Dark Subtraction']
  buttons['ApplyGainOffset'] = $
    ['/Radiometric Correction', 'Apply Gain and Offset']

  ;filters
  buttons['BitErrorAdaptiveFilter'] = $
    ['/Filters', 'Bit Errors Filter']
  buttons['GaussianHighPassFilter'] = $
    ['/Filters', 'Gaussian High Pass Filter']
  buttons['GaussianLowPassFilter'] = $
    ['/Filters', 'Gaussian Low Pass Filter']
  buttons['HighPassFilter'] = $
    ['/Filters', 'High Pass Filter']
  buttons['LowPassFilter'] = $
    ['/Filters', 'Low Pass Filter']
  
  ;add the buttons
  foreach val, buttons, key do begin
    e.AddExtension, val[1], 'better_extensions', $
      PATH = '/Better Extensions' + val[0], $
      UVALUE = key 
  endforeach

end

; ENVI Extension code. Called when the toolbox item is chosen.
pro better_extensions, event

  ; Set compile options
  compile_opt idl2

  ;Get ENVI session
  e = envi(/CURRENT)
  if (e eq !NULL) then begin
    e = envi()
  endif

  CATCH, Error_status
  IF Error_status NE 0 then begin
    catch, /CANCEL
    help, /LAST_MESSAGE, output = err_txt
    p = dialog_message(err_txt)
    return
  ENDIF

  ;get the directory that our extension lives in
  WIDGET_CONTROL, event.id, GET_UVALUE = taskName
  
  ;create the task object
  task = ENVITask(taskname)
  
  ;add parameter to ask if we want to displayt he results
  displayParam = ENVITaskParameter(NAME='DISPLAY_RESULT', $
    DISPLAY_NAME = 'Display Result',$
    DEFAULT = !FALSE,$
    TYPE='bool', $
    DIRECTION='input', $
    REQUIRED = 1)
  ;replace old parameter
  task.AddParameter, displayParam

  ;create a dialogue for the rest of the task
  ok = e.UI.SelectTaskParameters(task)

  ;user selected OK
  if (ok eq 'OK') then begin
    ;get rid of dummy result
    display = task.DISPLAY_RESULT
    task.RemoveParameter, 'DISPLAY_RESULT'

    ;run the task
    task.execute

    ;check if we want to display things
    if display then begin
      ;things to display
      rasters = list()
      rois = list()
      shapefiles = list()
      
      ;check for output datatypes that are rasters, vectors, or rois
      foreach paramName, task.ParameterNames() do begin
        param = task.parameter(paramName)
        
        if (param.direction eq 'OUTPUT') then begin
          if (param.TYPE eq 'ENVIRASTER') then begin
            e.data.add, param.VALUE
            rasters.add, param.VALUE
          endif
          if (param.TYPE eq 'ENVIROI') then begin
            e.data.add, param.VALUE
            rois.add, param.VALUE
          endif
          if (param.TYPE eq 'ENVIVECTOR') then begin
            e.data.add, param.VALUE
            shapefiles.add, param.VALUE
          endif
        endif
      endforeach
      
      ;get envi's view
      View1 = e.GetView()
      
      ;disable refresh
      e.refresh, /DISABLE
      
      ;check for rasters and ROIs
      if (n_elements(rasters) gt 0) then begin
        
        ;display each raster in the view
        foreach raster, rasters do begin
          rasterLayer = View1.CreateLayer(raster)
        endforeach
        
        ;only display roi is there is a rasterlayer
        foreach roiarr, rois do begin
          ;handle arrays of rois
          foreach roi, roiarr do begin
            roilayer = rasterlayer.AddROI(roi)
          endforeach
        endforeach
      endif
      
      ;check for vectors
      ;only display vectors if ROI is present
      foreach vector, shapefiles do begin
        ; Create a vector layer
        vectorLayer = view.CreateLayer(vector)
      endforeach
      
      ;refresh display again
      e.refresh
    endif
  endif
end
Please login or register to post comments.