扩展 ENVI 扩展功能
原文链接:https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/extending-envi-extensions
9750 为本文评分:
3.8
扩展 ENVI 扩展功能
匿名作者 2017年1月19日,星期四
能够使用 IDL 扩展 ENVI 功能时,我最喜欢的部分之一就是可以向 ENVI 工具箱中添加自定义按钮。这些按钮被称为扩展,我在 NV5 Geospatial 工作期间创建了许多扩展。我为展会展台演示、客户示例,甚至为精准农业工具包都创建过按钮。ENVI 中的扩展可以实现你想要的任何功能。大多数时候,这是使用 ENVI 的动态用户界面来创建简单的小部件,允许用户为任务选择所有输入和输出,但你也可以根据需要直接调用 IDL 并实例化其他基于 GUI 的应用程序。
虽然扩展很容易制作(我通常只是复制/粘贴旧代码并进行调整),但当需要同时创建多个扩展时,这个过程可能会很繁琐。此外,如果你希望为所有扩展添加某些额外功能,就必须手动逐一修改。这正是发挥 IDL 编程创造力的时候,因为 IDL 可以用于自动化生成 ENVI 中的按钮。在这个示例中,我决定尝试从 ENVI 任务中动态创建 X 个按钮,而无需为每个按钮单独编写扩展。
基本思路是,我希望有一个动态扩展,允许我传入任何任务,创建一个按钮,在按下按钮时运行正确的任务,并能够显示任务中所有类型为 ENVIRaster、ENVIROI 或 ENVIVector(或 shapefile)的结果。如果你不熟悉如何向 ENVI 添加扩展,可以参考以下文档链接以获取一些背景信息:
http://www.nv5geospatialsoftware.com/docs/ENVI__AddExtension.html
根据文档中的示例,我决定使用按钮的 UVALUE 来传递关于我想为哪个任务创建动态用户界面的信息。我选择使用有序哈希(orderedhash)作为数据结构:键是任务名称,值是一个包含两个元素的字符串数组,分别代表我们希望任务出现在 ENVI 工具箱中的文件夹路径和按钮名称。以下是一个数据结构示例:
; 创建一个我们想要生成的按钮的有序哈希
buttons = orderedhash()
buttons['ROIMaskRaster'] = ['/Regions of Interest', 'ROI Mask Raster']
此时,关键在于正确遍历哈希,以便在正确的位置创建按钮。我选择使用 foreach 循环,因为它可以方便地同时从哈希或有序哈希中获取键和值。相关代码如下所示:
foreach val, buttons, key do begin
e.AddExtension, val[1], 'better_extensions', $
PATH = '/Better Extensions' + val[0], $
UVALUE = key
endforeach
这段简短的代码块将动态遍历有序哈希中的每个条目,然后创建具有正确位置和点击时执行任务的按钮。请注意,我创建了一个名为“Better Extensions”的子文件夹,用于包含该扩展将创建的所有高级按钮。弄清楚这段代码后,我只需要查看实际运行扩展的过程(我称之为“better_extensions”)。
你可以在下方看到 better_extensions 的完整代码,但有几个关键点需要说明:
- 要获取被点击按钮的任务名称,我在创建扩展时使用了 UVALUE 关键字来存储名称。然后,在 better_extensions 过程中,我将事件作为参数获取,并使用
widget_control获取按钮的 uvalue。 - 要创建任务用户界面,我只需要执行以下操作:
; 为任务的其余部分创建对话框 ok = e.UI.SelectTaskParameters(task) - 之后,我只需要遍历每个任务参数,检查 INPUT 或 OUTPUT,如果是输出,再检查是否为 ENVIRaster、ENVIVector 或 ENVIROI。然后保存这些结果,并在 ENVI 的当前视图中显示它们。
这里的主要目标是展示,通过一点 IDL 编程,你可以将 ENVI 分析提升到一个新的水平。扩展的代码如下所示。干杯!
此外,请关注我的下一篇博客,我将讨论如何为一个旧扩展(来自在线扩展库)进行急需的更新!
; 将扩展添加到工具箱。在 ENVI 启动时自动调用。
pro better_extensions_extensions_init
; 设置编译选项
compile_opt idl2
; 获取 ENVI 会话
e = envi(/CURRENT)
; 创建一个我们想要生成的按钮的有序哈希
buttons = orderedhash()
; 示例 ROI 工具
buttons['ROIMaskRaster'] = $
['/Regions of Interest', 'ROI Mask Raster']
bttons['ROIToClassification'] = $
['/Regsions of Interest', 'Classification Image from ROis']
; 分类工具
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']
; 辐射校正工具
buttons['DarkSubtractionCorrection'] = $
['/Radiometric Correction', 'Dark Subtraction']
buttons['ApplyGainOffset'] = $
['/Radiometric Correction', 'Apply Gain and Offset']
; 滤镜
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']
; 添加按钮
foreach val, buttons, key do begin
e.AddExtension, val[1], 'better_extensions', $
PATH = '/Better Extensions' + val[0], $
UVALUE = key
endforeach
end
; ENVI 扩展代码。在工具箱项被选中时调用。
pro better_extensions, event
; 设置编译选项
compile_opt idl2
; 获取 ENVI 会话
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
; 获取我们扩展所在的目录
WIDGET_CONTROL, event.id, GET_UVALUE = taskName
; 创建任务对象
task = ENVITask(taskname)
; 添加参数以询问是否要显示结果
displayParam = ENVITaskParameter(NAME='DISPLAY_RESULT', $
DISPLAY_NAME = 'Display Result',$
DEFAULT = !FALSE,$
TYPE='bool', $
DIRECTION='input', $
REQUIRED = 1)
; 替换旧参数
task.AddParameter, displayParam
; 为任务的其余部分创建对话框
ok = e.UI.SelectTaskParameters(task)
; 用户选择了 OK
if (ok eq 'OK') then begin
; 移除虚拟结果参数
display = task.DISPLAY_RESULT
task.RemoveParameter, 'DISPLAY_RESULT'
; 运行任务
task.execute
; 检查是否要显示结果
if display then begin
; 要显示的内容
rasters = list()
rois = list()
shapefiles = list()
; 检查输出数据类型是否为栅格、矢量或 ROI
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
; 获取 ENVI 的视图
View1 = e.GetView()
; 禁用刷新
e.refresh, /DISABLE
; 检查栅格和 ROI
if (n_elements(rasters) gt 0) then begin
; 在视图中显示每个栅格
foreach raster, rasters do begin
rasterLayer = View1.CreateLayer(raster)
endforeach
; 仅在有栅格图层时显示 ROI
foreach roiarr, rois do begin
; 处理 ROI 数组
foreach roi, roiarr do begin
roilayer = rasterlayer.AddROI(roi)
endforeach
endforeach
endif
; 检查矢量
; 仅在有 ROI 时显示矢量
foreach vector, shapefiles do begin
; 创建矢量图层
vectorLayer = view.CreateLayer(vector)
endforeach
; 再次刷新显示
e.refresh
endif
endif
end