三维可视化稀疏环境监测数据
11712 给这篇文章评分:
暂无评分
三维可视化稀疏环境监测数据
Jim Pendleton 2016年4月28日,星期四
可视化在数据分析中的作用,是以更直观的方式增强我们感知数据关系的能力。
稀疏且空间不规则的数据,难以形成视觉上连贯的模式,这对我们来说是一个挑战。

想象一个池塘,在不同深度和不同位置实地采集了某种量(如甲烷气体或藻类浓度)的样本,这是我们NV5 Geospatial定制解决方案组几年前处理过的一个问题。
我们希望构建一个工具,以各种三维形式可视化这些浓度,例如插值到池塘侧面的平面切片,就像这样。

本篇博客重点介绍了几种可用于将不规则数据投影到平面以及其他更复杂表面(如上图所示)的技术。
图像是数据的一种形式,我们通常拥有二维的规则像素网格。这使得可视化变得简单明了。ENVI 擅长处理这类数据。
当我们添加第三空间维度和不规则采样时,可视化就变得极具挑战性。对于这类复杂数据,我们确实需要 IDL 而非 ENVI 来进行有效可视化。
在继续之前,我必须指出,下面展示的内容充其量是使用非常通用的模型在采样点之间插值数据的首次估算。当然,适当的物理模型将产生更逼真的最终显示效果。本篇博客的重点在于可视化技术而非建模。
为了进行演示,我们需要模拟一些数据。我们将为“池塘”生成一个轮廓,并用具有模拟实验值的采样点对其进行随机播撒。
完整示例可在可下载的程序ribbonimage.pro中找到。
第一步是生成代表池塘的多边形。为了增加挑战性,我们让池塘成为一个复杂形状,即两条相互偏移的正弦曲线。
A = Findgen(361)
Theta = A*!dtor
Y1 = Sin(Theta)
Y2 = Y1 + .5
X = A/180.
XX = [X, X[360], Reverse(X), X[0]]
YY = [Y1, Y2[0], Reverse(Y2), Y1[0]]
X 和 Y 坐标位于“数据空间”。我们将以几种不同的方式使用这些坐标。稍后,我们将使用这些坐标构建池塘的“墙壁”。首先,我们显示池塘的轮廓。对于像这样的简单显示,我喜欢使用直接图形。
ImageSize = 512
Window, /Free, XSize = ImageSize, YSize = ImageSize
Plot, XX, YY, YRange = [Min(YY), Max(YY)], XStyle = 5, $
YStyle = 5, XMargin = [0, 0], YMargin = [0, 0]

接下来填充池塘的像素并返回填充像素的坐标。掩码的坐标将以像素为单位,而非数据空间。
PolyFill, XX, YY
Im = TVRD()
Mask = Array_Indices(Im, Where(Im))
MaskX = Mask[0, *]
MaskY = Mask[1, *]

在所有填充的像素中,随机选择 250 个作为我们的采样点。
NPoints = 250
NMask = N_elements(Mask)
Points = RandomU(Seed, NPoints) * NMask
PointsX = MaskX[Points]
PointsY = MaskY[Points]
将采样点位置转换为数据坐标,并为池塘中的数据创建随机的 Z 位置。
XY = Convert_Coord(PointsX, PointsY, /Device, /To_Data)
PointsX = Reform(XY[0, *])
PointsY = Reform(XY[1, *])
ZMin = -.3
ZMax = 0.
PointsZ = ZMin + RandomU(Seed, NPoints)*(ZMax - ZMin)
将随机 Z 位置缩放到 0 到 255 的范围。在此示例中,每个样本的“值”仅仅是采样位置的深度,按此范围缩放。
Values = BytScl(PointsZ)
绘制采样点,投影到二维空间,显示其分布。
TVLCT, R, G, B, /Get
LoadCT, 15, /Silent
TVLCT, Red, Green, Blue, /Get
Device, Get_Decomposed = WasDecomposed
Device, Decomposed = 0
For I = 0L, N_elements(PointsX) - 1 Do Begin
PlotS, PointsX[I], PointsY[I], Color = Values[I], PSym = 5
EndFor
Device, Decomposed = WasDecomposed
TVLCT, R, G, B
图中符号颜色代表深度值。

接下来,我们将切换到对象图形,在第三维度中完成繁重的工作。创建一个IDLgrModel来容纳图形以及我们将应用于采样点和池塘“墙壁”的调色板。
oM = IDLgrModel()
oPalette = IDLgrPalette()
oPalette.LoadCT, 15
为通用的“采样圆柱体”创建坐标。
CylRadius = .01
CylHeight = .02
XCyl = Cos(Theta)*CylRadius
YCyl = Sin(Theta)*CylRadius
Mesh_Obj, 5, CylV, CylConn, $
Transpose([[XCyl], [YCyl], [Replicate(-CylHeight/2, 361)]]), $
P1 = 4, P2 = [0, 0, CylHeight], /Close
在每个随机采样点的位置创建一个圆柱体对象,并根据其深度进行着色。
For I = 0, NPoints - 1 Do Begin
oM2 = IDLgrModel()
oM.Add, oM2
oCyl = IDLgrPolygon( $
CylV, $
Poly = CylConn, $
Color = Values[I], Palette = oPalette)
oM2.Add, oCyl
oM2.Translate, PointsX[I], PointsY[I], PointsZ[I]
EndFor
可以单独渲染采样点。您可以使用诸如XOBJVIEW之类的工具来渲染主容器模型,即变量“oM”。

您可以看到我们在显示此类稀疏数据时遇到的主要问题。是的,我们可以分辨出各个点,但点之间的空白使得我们很难看清数据的模式,即使借助动画的优势也是如此。
我们可以在数据点之间或周围的空间中创建一个连接点的网格模型。空间不必是直线网格。我们可以计算每个网格点处的插值数据(最好使用物理模型)。然后,我们让图形系统根据网格点之间的距离进行额外的插值。
为此示例创建网格,使用MESH_OBJ将池塘轮廓沿 Z 轴向上拉伸。我们添加的步数越多,轮廓上的点越多,显示效果就越平滑。
NZSteps = 50
Mesh_Obj, 5, V1, P1, Transpose([[XX], [YY], [Replicate(ZMin, 361*2+2)]]), $
P1 = NZSteps, P2 = [0, 0, (ZMax - ZMin)]
使用 MESH_OBJ 生成的顶点坐标创建一个新的IDLgrPolygon。
NVertices = N_elements(V1)/3
oP1 = IDLgrPolygon(V1, Poly = P1)
oM.Add, oP1
通过将池塘墙壁的 IDLgrPolygon 的 STYLE 属性临时设置为 1,我们可以看到生成的网格。网格的交点坐标正是我们希望插值“测量”值的位置。

对于池塘墙壁中的每个顶点,根据其与最近(N = 3)采样点的距离计算其插值,并执行一种反距离加权,使最近的采样位置比其他位置具有更大的影响。该位置顶点的颜色根据加权平均值计算。此处正是您插入自己物理模型的地方。
N = 3
VertexColors = BytArr(NVertices)
For I = 0L, NVertices - 1 Do Begin
Delta = Sqrt((V1[0, I] - PointsX)^2 + $
(V1[1, I] - PointsY)^2 + (V1[2, I] - PointsZ)^2)
S = Sort(Delta)
DeltaS = Delta[S[0:N - 1]]
XNear = PointsX[S[0:N - 1]]
YNear = PointsY[S[0:N - 1]]
ZNear = PointsZ[S[0:N - 1]]
VNear = Values[S[0:N - 1]]
Sum = Total(1.0/DeltaS^2)
W = (1./DeltaS^2)/Sum
VertexColors[I] = (Total(VNear*W) > 0) < 255B
EndFor
将计算出的顶点颜色分配给池塘墙壁多边形,然后渲染图形模型。
oP1.SetProperty, Vert_Colors = VertexColors, Palette = oPalette, $
Style = 2
oM.Rotate, [1, 0, 0], -60
XObjView, oM

此解决方案可推广到任何类型的采样表面。例如,我们也可以查看水平切片,并加入一些透明度以增加趣味性。

或者我们甚至可以显示一个穿过数据的倾斜切片平面。

这些就留给读者作为练习了。