遥感院数字摄影测量课程设计题目:基于DEM规则格网自动生成等高线。我在网上查找相关资料,发现并没有什么切实相关的内容,没有人对这一块内容进行一个完整的原理讲解与总结,所以我自己对照武汉大学出版社的《摄影测量学》中讲解的算法原理,结合老师上课所讲内容,理解并完成了这一算法的代码实现。
一、原理介绍
1.1实现流程图
1.2DEM无效值处理
我们拿到DEM数据之后,一般的组织形式都是:
第一行为「左下角格网坐标 旋转量 采样间隔 行列数」;第二行开始记录格网点的高程信息
通过记事本打开DEM数据,观察数据我们可以发现,其中存储的高程数据有大量的值为“-99999”的无效值,这将影响后续DEM状态矩阵计算。
所以我们要对这些无效值进行处理,处理方法是:最邻近元法。首先找到无效值的位置,然后先沿行,再沿列逐个搜索,找到的第一个有效值数据赋值给该无效值,完成无效值处理。
1.3计算状态矩阵
创建两个与DEM格网边大小相同的表示状态的矩阵Vk和Hk。
对整个DEM格网的横边、竖边进行状态判断,判断其是否与给定高度的等高线有相交,即是否有等高线穿过,若有相交则将状态矩阵中的对应值设置为1,否则为0.
判断相交的准则如下公式所示,其中k表示等高线高程序号,Zk表示待求等高线高程。其中i、j表示格网行列号,Zij表示DEM格网中对应序号点的高程,hij表示对应格网边的状态。
表示了DEM与等高线水平边(Hk)和竖直边(Vk)的相交状态。若Hk(i,j)=1,说明ij对应的格网与DEM的水平边有交点,既有等高线通过。示例如下图所示:
1.4起始点搜索
计算完状态矩阵后,对于给定的高程,我们就知道了等高线与DEM格网的相交状态了,下面将进行等高线跟踪查找,在此之前,需要先找到等高线搜索的起点。
等高线有两种,分别是开曲线与闭曲线,两者的区别在于起始点是否会再次被访问,故需要使用不同的方法查找。
(1)开曲线查找:
开曲线是起始点和终止点在DEM边缘的等高线,所以在边缘处查找即可,使用Vk计算起始于第一行和最后一行的等高线,使用HK计算起始于第一列和最后一列的等高线。将找到的格网作为等高线起点。
将状态矩阵中对应的“1”更改为“0”,以免再次查找。
(2)闭曲线查找:
闭曲线是在DEM内部格网中,首尾相连的等高线,所以在内部进行遍历查找,可以使用先搜索行再搜索列的方法遍历查找,第一个有等高线穿过的格网边计作闭曲线的起始边。
由于起点格网边会作为终点格网边被再次访问,所以在找到起始点后,不将其状态矩阵更改为“0”,再次作为终止点访问时,再设置为“0”,否则闭曲线无法闭合。
1.5等高线跟踪
找到起点后,就可以对该条等高线进行跟踪,首先将格网进行编号,因为从不同的等高线边进入格网,搜索离去边的代码是不同的。
记录上一步找到的起始边的编号,作为起始格网的进入边,并内插其等高线点坐标,然后再在格网中按照顺(逆)时针的顺序判断另外三条边是否为离去边,若对应状态矩阵为1,则说明是离去边,将该条边
对应的状态矩阵设置为0,内插等高线点坐标,保存该点的序号与XYZ值,将该离去边作为下一个格网的进入边,再次进行上述的“顺时针查找-内插坐标-记录等高线点-记录离去边(下一格网进入边)”的顺序进行查找。
比如当前等高线起点从4号边进入,则按照下式顺时针搜索等高线走向:
找到离去边后,内插该等高线点坐标值,Z为该等高线高程值
若从3号边出,即下一格网从1号边进入:
如此循环往复,开曲线查找直到找不到下一离去边/离去边为边缘线,闭曲线查找到起始点位置后找不到下一个离去边为止。
所有状态矩阵全部为0,该高程对应的等高线搜索完毕,更换下一高程,完成以上2、3、4步算法。直到所有高程的所有等高线全部搜索完毕。
1.6可视化
使用OSG(OpenSceneGraph)引擎进行可视化,设置参考坐标系的坐标、网格大小等,设置背景颜色、等高线线条粗细、颜色,选择OSG场景漫游方式。
将等高线模型加入到场景结点中,进行可视化即可。
二、程序框架设计
结构体设计:
struct PtLoc {
double X;
double Y;
double Z;
int in;
int col;
int row;
int HK;
};
struct CLine {
std::vector<PtLoc> pt;
int shape = 0;
};
自定义函数设计:
std::vector<std::vector<double>> GetNearestHeight();
std::vector<std::vector<double>> StateMatHk(int t);
std::vector<std::vector<double>> StateMatVk(int t);
void GetlinePt(std::vector<std::vector<double>>& Hk, std::vector<std::vector<double>>& Vk, double Z, int ni, int nj, CLine& line);
void getline(std::vector<std::vector<double>> Hk, std::vector<std::vector<double>> Vk, double Z, std::vector<CLine>& Line);
void savelines(std::string dir, std::vector<CLine> Line, int k);
void nlines(std::string dir, double dz);
GetNearestHeight()用于处理无效值
StateMatHk()与StateMatVk()用于计算状态矩阵
GetlinePt()用于给定起点与状态矩阵,跟踪等高线中的点
getline()用于查找起点,在函数中调用GetlinePt()函数,利用查找到的起点,去跟踪该条等高线中的余下点,并连接成线,同时完成剔除工作。
nlines()用于按照不同的高程值生成所有等高线。
savelines()用于保存不同高程的若干等高线到文件中。
三、成果展示
俯视角度:
侧视角度:
局部细节:
四、结果分析
整体效果良好、理想,能够通过等高线清晰了解地形地貌特征,坡度坡向等信息,且等高线绘制基本正确,边缘处都为开曲线,内部没有意外短接的闭曲线线条疏密合理,高程步距合适,且可以通过osg引擎实现模型的缩放平移,方便观察。
但是在俯视图中可以看出,右侧有一部分等高线出现了异常,可能是无效值处理的时候导致的问题。
五、反思与展望
在代码编写的过程中,对于状态矩阵组织、使用方面研究了很久,理解起来容易,实际编写过程中却不知道如何组织数据。
对于不同的进入边,应该使用不同的查找遍历逻辑,在这里由于自己逻辑混乱,编写错误很多次,不断的梳理修改后终于得出正确结果。
由于在之前的虚拟现实技术课程中学习过OSG与OpenGL引擎,故这里选择使用OSG引擎去进行等高线的三维可视化,这样可以方便的旋转缩放模型。
该代码中仍有一些部分可以优化:
1)可以设计等高线跟踪可视化代码,展示等高线跟踪的过程,更清晰的理解等高线跟踪算法原理与过程
2)输出的等高线是由直线连接等高线点得到的, 因此其一阶导数不连续,可以使用二次曲线拟合方法,将等高线平滑输出。
六、代码
还没有上传到GitHub上,大家可以参考GitHub - Charrrrrlie/PracticalCourse: 遥感院实习课代码集(数据结构,数字图像处理,遥感原理,解析摄影测量,近景摄影测量,数字摄影测量)中数字摄影测量部分