绘制等高线图的算法

您所在的位置:网站首页 等值线如何求 绘制等高线图的算法

绘制等高线图的算法

2024-02-25 15:40| 来源: 网络整理| 查看: 265

假设我们在一个矩形范围内,有一些离散的高度数据,形如(x,y,height)这样的数据集,然后我们要得到一张这样的等高线图:

我们描述一下生成等高线图的算法。

一、图形概述

实际地图上实用的单位是米,但我们显示时使用的单位是像素,这里面有一个转换的关系。而且,显示的图形有可能需要缩放。所以我们收集的数据,x和y统一使用相对值。我们把总长和总宽都定为1,实际的坐标相对于单位1来定。例如地图长8km,然后我们的坐标点x为1km,那么x就是1/8=0.125。

实际上,等高线图是分成一个一个网格的,我所使用的网格大小是10*10。每个网格里的线和颜色组成了整张的等高线图。

二、插值

我们的数据都是形如(x,y,height)这样的格式,如(0.85f, 0.05f, 0.49f)、(0.75f, 0.1f, 0.7f)、(0.5f, 0.65f, 0.04f)。

假如我们要显示的图形大小是800*600,切成网格后,就会有80*60=4800个网格。一般情况下,我们收集到的数据量远远小于这个数,所以我们就需要插值。

我们用比较简单的距离反比策略进行插值。我们知道,跟某个点的距离越近,受它的影响就越大。所以,跟点V(x1,y1)相距d的点P(x,y),它受V的影响可按以下公式计算:

P-V=V/d

其中d=(x-x1)^2+(y-y1)^2

而P的最终值,由平面上所有已知值的点确定。

代码如下:

double D = 0; double DV = 0; foreach (IntMeasureData imd in measure_data) { double d = 1.0 / ((imd.X - i) * (imd.X - i) + (imd.Y - j) * (imd.Y - j)); D += d; DV += imd.Z * d; } value = (float)(DV / D); 三、Marching squares算法

这就是生成等高线图的一种算法,我们来描述一下。

我们上面提到过,把800*600的图切成了80*60个网格,而在第二步里面,我们把这些网格都填满数据了。我们假设数据如下:

图中的数据就是高度。我们现在要画一条高度值为5的线。我们把5跟表格中的数据比较,比5大的为1,用实心黑点表示,比5小的为0,化为一个二值的表格,如下图所示:

然后如果我们把每个格子取出来,就会发现有以下的16种情况:

只要我们把上图的线画出来,就会拼出一张等高线图。

四、算法细节

问题1,我们可能需要的是有颜色的图,而不是只有线条的图。

我们只要把上图中格子里的线和实心黑点围成的区域填充颜色即可。

问题2,不同的高度值,如何绘图。

先画低阈值的,再画高阈值的,一层层往上叠即可。

问题3,有没有办法把所有格子的线条围成一个区域,然后对这个区域进行一次性颜色的填充。

如果有,就不会用这种分成一格一格的方法去做了。只能一格一格地填充颜色。

五、代码解析

代码是C#、WPF。

我们先定义测量数据的数据结构:

/// /// 测量数据 /// public struct MeasureData { /// /// 初始化测量数据 /// /// 坐标x /// 坐标y /// 高度 public MeasureData(float x, float y, float z) { X = x; Y = y; Z = z; } /// /// 坐标X /// public float X; /// /// 坐标Y /// public float Y; /// /// 高度 /// public float Z; } internal struct IntMeasureData { public IntMeasureData(MeasureData md, int x_num, int y_num) { X = (int)(md.X * x_num); if (X >= x_num) { X = x_num - 1; } Y = (int)(md.Y * y_num); if (Y >= y_num) { Y = y_num - 1; } Z = md.Z; } public int X; public int Y; public float Z; }

IntMeasureData是存放在网格里的数据。

然后我们做第一步:数据插值:

/// /// 数据插值 /// private void InitData() { x_num = (int)(w / grid_w); y_num = (int)(h / grid_h); data = new float[x_num, y_num]; IntMeasureData[] measure_data = new IntMeasureData[HeightDots.Length]; for (int i = HeightDots.Length - 1; i >= 0; i--) { measure_data[i] = new IntMeasureData(HeightDots[i], x_num, y_num); } min = float.MaxValue; max = float.MinValue; for (int i = 0; i < x_num; i++) { for (int j = 0; j < y_num; j++) { float value = 0; bool find = false; foreach (IntMeasureData imd in measure_data) { if (i == imd.X && j == imd.Y) { value = imd.Z; find = true; break; } } if (!find) { double D = 0; double DV = 0; foreach (IntMeasureData imd in measure_data) { double d = 1.0 / ((imd.X - i) * (imd.X - i) + (imd.Y - j) * (imd.Y - j)); D += d; DV += imd.Z * d; } value = (float)(DV / D); } data[i, j] = value; min = Math.Min(min, value); max = Math.Max(max, value); } } }

核心是Marching squares算法的实现:

/// /// 获取某个阈值下的图形数据 /// /// 阈值 /// 图形数据 private List CreateMapData(float threshold) { byte[,] binary_data = new byte[x_num, y_num]; for (int i = 0; i < x_num; i++) { for (int j = 0; j < y_num; j++) { binary_data[i, j] = (byte)((data[i, j] >= threshold) ? 1 : 0); } } List shapes = new List(); for (int i = 1; i < x_num; i++) { for (int j = 1; j < y_num; j++) { int num = (binary_data[i - 1, j - 1]


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3