介绍 ENVIHydratable 接口
8740 评价本文:
暂无评分
介绍 ENVIHydratable 接口
匿名 2016年1月21日,星期四
之前我写过一篇博客,介绍了如何研究 ENVIRaster 虚函数的签名,并确定性地构建将栅格链传递给 ESE 所需的嵌套 JSON 对象。今天,我来告诉您,在 ENVI 5.3 SP1 中,我们为 ENVI API 添加了一些功能,让这个过程变得更加简单和自动化。
这是通过两个新设施实现的——ENVIHydratable 接口类和 ENVIHydrate() 工厂函数。在深入介绍它们之前,我先简单提一下它们的命名逻辑。虽然很自然地会想把它们叫做 ENVISerializable 和 ENVISerialize(),但"序列化"这个词意味着转换成文本/字节数组或从其转换回来。我们对这些功能的命名进行了讨论,最终决定使用 Dehydrate/Hydrate,因为它们暗示了我们所需的对称性和可逆性,同时又不像 serialize 那样暗示中间表示形式。如果您看一下这些新功能的工作方式,ENVIHydratable::Dehydrate() 方法返回一个 IDL Hash 对象,而 ENVIHydrate() 也期望一个 Hash 对象。做出这个决定是为了让我们有灵活性,不将 ENVI API 绑定到特定的传输流或存储格式。当前的 ESE 任务使用 JSON,但几年前 XML 可能是更合理的选择,几年后谁知道会有什么热门新技术出现。所以,与其现在就将 API 与 JSON 绑定,我们不如将其保持为 IDL 原始数据类型,这些数据类型很容易与 JSON 相互转换(通过 JSON_Serialize() 和 JSON_Parse()),但这并不是唯一的选择。如果有人想使用 XML,他们可以编写序列化/反序列化模块来实现。即使是使用 JSON,您仍然可以在序列化之前使用我上次博客中介绍的编码器之一,以确保浮点参数完全保持 IEEE 精度。
如前所述,ENVIHydratable 类是一个接口类——它定义了 Dehydrate() 方法的签名,并提供了通过 ISA() 函数进行多态识别的能力。这个类本身并不实现 Dehydrate() 方法,它只是抛出一个错误信息;任何继承它的类都有责任重写并提供特定于类型的实现,以返回一个合适的 Hash。在 ENVI 5.3 SP1 版本中,API 中可以被视为数据模型一部分的每个类都继承自 ENVIHydratable 并实现了 Dehydrate():ENVIRaster、ENVIMosaicRaster、ENVIRasterSeries、ENVIVector、ENVIROI、ENVIGCPSet、ENVITiePointSet、ENVISpectralLibrary、ENVITime、ENVICoordSys、ENVIPsuedoRasterSpatialRef、ENVIRPCRasterSpatialRef、ENVIStandardRasterSpatialRef、ENVIGridDefinition 和 ENVIRasterMetadata。由于所有虚拟栅格函数都返回 ENVIRaster 对象,因此此处记录的所有虚栅格函数也包括在内。
这些 Dehydrate Hash 看起来是什么样子的?每个 Hash 都有一个 "factory" 键,它是类/工厂函数的名称,不带 "ENVI" 前缀。读过我原始博客的读者会注意到,我当时只提到了 ENVI::OpenRaster() 作为普通栅格的工厂函数,但现在有了 ENVIURLRaster() 函数,它有效地取代了 ENVI::OpenRaster()。Hash 中的其余键是工厂函数中已定义值的关键字。这些键的值必须是 IDL 原始类型:标量数字或字符串、数字或字符串数组,或者由原始类型组成的 List 或 Hash 对象。当您有虚拟栅格链时,这就导致了 Hash 嵌套 Hash 的结构。
ENVIHydrate() 函数是最终的通用工厂函数。它接收一个 Hash,这个 Hash 必须包含一个 "factory" 键。该键必须关联一个标量字符串值,这个值就是在前面加上 "ENVI" 后要调用的工厂函数的名称。其他每个 Hash 键在调用工厂函数时都被视为关键字,而任何本身是 Hash 的值,在调用外部工厂函数之前,都会对其递归调用 ENVIHydrate()。这导致了对嵌套 Hash 结构的深度优先遍历,该顺序与对象在 Dehydrate 之前被创建的顺序相对应。
那么,让我们实际操作一下,看看它是如何工作的。这个例子有点刻意,但说明了 ENVIHydrateable 和 ENVIHydrate() 的强大功能。我首先将 qb_boulder_msi 加载到 ENVI 中,但随后决定我不喜欢它的一堆元数据。所以我创建了一个新的 ENVIStandardRasterSpatialRef,将其从 UTM 13N 带移到 18N 带;创建了一个 ENVIRasterMetadata 对象来反转波段的波长顺序;以及一个 ENVITime 对象将采集时间设置为现在。然后我关闭栅格,并用所有这些覆盖重新打开它。接着,我对栅格进行空间子集划分,取其左上象限,并计算其 NDVI(由于使用了新的波长选择波段 2 作为红色、波段 1 作为近红外,所以结果非常不同)。最后,我在最终的栅格上调用 Dehydrate(),这样我们可以看到它的"谱系"。这一切都是在一个无头的 ENVI 会话中完成的,随后该会话关闭,同时关闭所有栅格以证明我没有耍花样。然后我在 UI 模式下启动 ENVI,将脱水后的 Hash 传递给 ENVIHydrate(),并用它在视图中创建一个新图层。
e = ENVI(/HEADLESS)
file = FilePath('qb_boulder_msi', ROOT=e.Root, SUBDIR='data')
raster = e.OpenRaster(file) spatRef = raster.SpatialRef
newSpatRef = ENVIStandardRasterSpatialRef(COORD_SYS_CODE=32618, $ PIXEL_SIZE=spatRef.Pixel_Size, $ TIE_POINT_MAP=spatRef.Tie_Point_Map, $ TIE_POINT_PIXEL=spatRef.Tie_Point_Pixel)
raster.Close
metaOverride = ENVIRasterMetadata() metaOverride['wavelength'] = [ 830.0, 660.0, 560.0, 485.0 ]
timeOverride = ENVITime(UNIX_SECONDS=SysTime(1))
raster = e.OpenRaster(file, SPATIALREF_OVERRIDE=newSpatRef, $ METADATA_OVERRIDE=metaOverride, $ TIME_OVERRIDE=timeOverride)
subRaster = ENVISubsetRaster(raster, $ SUB_RECT=[0, 0, 511, 511])
ndvi = ENVISpectralIndexRaster(subRaster, INDEX='NDVI')
hydratedForm = ndvi.Dehydrate() print, hydratedForm, /IMPLIED
ndvi.Close subRaster.Close raster.Close e.Close
e = ENVI()
newRaster = ENVIHydrate(hydratedForm)
v = e.GetView()
l = v.CreateLayer(newRaster)
以下是脱水后的 Hash 的隐含打印输出(为了可读性,我在这里重新排列了一些键的顺序,但它使用的是 Hash 而不是 OrderedHash,所以顺序由哈希函数决定,而不是对象添加键的顺序):
{ "factory": "SpectralIndexRaster", "input_raster": { "factory": "SubsetRaster", "input_raster": { "factory": "URLRaster", "url": "C:\Program Files\Exelis\ENVI53\data\qb_boulder_msi", "spatialref_override": { "factory": "StandardRasterSpatialRef", "coord_sys_code": 32618, "pixel_size": [2.7999999999999998, 2.7999999999999998], "rotation": 0.00000000000000000, "tie_point_map": [480267.20000000001, 4428978.4000000004], "tie_point_pixel": [0.00000000000000000, 0.00000000000000000] }, "time_override": { "factory": "Time", "acquisition": "2016-01-21T17:07:45.987Z" }, "metadata_override": { "factory": "RasterMetadata", "WAVELENGTH": [830.00000000000000, 660.00000000000000, 560.00000000000000, 485.00000000000000] } }, "sub_rect": [0, 0, 511, 511] }, "index": "NDVI" }