1. 问题

之前我在《使用GDAL实现DEM的地貌晕渲图(一)》这篇文章里面讲述了DEM晕渲图的生成原理与实现,大体上来讲是通过计算DEM格网点的法向量与日照方向的的夹角,来确定该格网点的晕渲强度值。但其实关于这一点我不是很理解,这样做随着坡面与光源方向的夹角不同,确实产生了不同色调明暗效果;但晕渲图同时又有“阴坡面越陡越暗,阳坡面越陡越亮”的特性的,而阴阳坡面的划分又是跟坡度和坡向相关,之前的生成方法能体现出这种特性吗?

经过查阅资料,却在ArcGIS的帮助文档《山体阴影工具的工作原理》(在线版本可查看这篇文章《ArcGIS教程:山体阴影工作原理》)中查阅到了晕渲图的另外一种生成算法。利用直接利用坡度和坡向的关系,算出每个点的山体阴影值:





并且,在该文档中,还附带了一个具体的计算示例:





具体的算法过程说的很清楚了,可惜的就是不太明白具体的原理是什么,在帮助中指向了一本1998的英文文献[Burrough, P. A. and McDonell, R. A., 1998. Principles of Geographical Information Systems (Oxford University Press, New York), 190 pp]也实在是没法深入查阅深究。而在查阅中文论文的时候,关于这一段的描述也是互相抄袭,摘录如下:



这一段的论述反正我是没看明白的,也就不多做论述了,希望看懂这个算法的大神能指点我一下。

2. 实现

虽然更深入的原理没弄明白,不过作为应用者却足够能够实现其算法了。我这里通过GDAL实现了晕渲图的生成:

#include <iostream>
#include <algorithm>
#include <gdal_priv.h>
#include <osg/Vec3d>
#include <fstream> using namespace std;
using namespace osg; // a b c
// d e f
// g h i
double CalHillshade(float *tmpBuf, double Zenith_rad, double Azimuth_rad, double dx, double dy, double z_factor)
{
double dzdx = ((tmpBuf[2] + 2 * tmpBuf[5] + tmpBuf[8]) - (tmpBuf[0] + 2 * tmpBuf[3] + tmpBuf[6])) / (8 * dx);
double dzdy = ((tmpBuf[6] + 2 * tmpBuf[7] + tmpBuf[8]) - (tmpBuf[0] + 2 * tmpBuf[1] + tmpBuf[2])) / (8 * dy); double Slope_rad = atan(z_factor * sqrt(dzdx*dzdx + dzdy*dzdy));
double Aspect_rad = 0;
if (abs(dzdx) > 1e-9)
{
Aspect_rad = atan2(dzdy, -dzdx);
if (Aspect_rad < 0)
{
Aspect_rad = 2 * PI + Aspect_rad;
}
}
else
{
if (dzdy > 0)
{
Aspect_rad = PI / 2;
}
else if (dzdy < 0)
{
Aspect_rad = 2 * PI - PI / 2;
}
else
{
Aspect_rad = Aspect_rad;
}
} double Hillshade = 255.0 * ((cos(Zenith_rad) * cos(Slope_rad)) + (sin(Zenith_rad) * sin(Slope_rad) * cos(Azimuth_rad - Aspect_rad)));
return Hillshade;
} int main()
{
GDALAllRegister(); //GDAL所有操作都需要先注册格式
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO"); //支持中文路径 const char* demPath = "D:/CloudSpace/我的技术文章/素材/DEM的渲染/dst.tif";
//const char* demPath = "D:/Data/imgDemo/K51E001022/k51e001022dem/w001001.adf"; GDALDataset* img = (GDALDataset *)GDALOpen(demPath, GA_ReadOnly);
if (!img)
{
cout << "Can't Open Image!" << endl;
return 1;
} int imgWidth = img->GetRasterXSize(); //图像宽度
int imgHeight = img->GetRasterYSize(); //图像高度
int bandNum = img->GetRasterCount(); //波段数
int depth = GDALGetDataTypeSize(img->GetRasterBand(1)->GetRasterDataType()) / 8; //图像深度 GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName("GTIFF"); //图像驱动
char** ppszOptions = NULL;
ppszOptions = CSLSetNameValue(ppszOptions, "BIGTIFF", "IF_NEEDED"); //配置图像信息
const char* dstPath = "D:\\dst.tif";
int bufWidth = imgWidth;
int bufHeight = imgHeight;
int dstBand = 1;
int dstDepth = 1;
GDALDataset* dst = pDriver->Create(dstPath, bufWidth, bufHeight, dstBand, GDT_Byte, ppszOptions);
if (!dst)
{
printf("Can't Write Image!");
return false;
} dst->SetProjection(img->GetProjectionRef());
double padfTransform[6] = { 0 };
if (CE_None == img->GetGeoTransform(padfTransform))
{
dst->SetGeoTransform(padfTransform);
} //申请buf
depth = 4;
size_t imgBufNum = (size_t)bufWidth * bufHeight * bandNum;
float *imgBuf = new float[imgBufNum];
//读取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
GDT_Float32, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth); if (bandNum != 1)
{
return 1;
} //
double startX = padfTransform[0]; //左上角点坐标X
double dx = padfTransform[1]; //X方向的分辨率
double startY = padfTransform[3]; //左上角点坐标Y
double dy = padfTransform[5]; //Y方向的分辨率 //申请buf
size_t dstBufNum = (size_t)bufWidth * bufHeight * dstBand * dstDepth;
GByte *dstBuf = new GByte[dstBufNum];
memset(dstBuf, 0, dstBufNum*sizeof(GByte)); //设置方向:平行光
double solarAltitude = 45.0;
double solarAzimuth = 315.0; //
double Zenith_rad = osg::DegreesToRadians(90 - solarAltitude);
double Azimuth_math = 360.0 - solarAzimuth + 90;
if (Azimuth_math >= 360.0)
{
Azimuth_math = Azimuth_math - 360.0;
}
double Azimuth_rad = osg::DegreesToRadians(Azimuth_math); //a b c
//d e f
//g h i
double z_factor = 1;
for (int yi = 1; yi < bufHeight-1; yi++)
{
for (int xi = 1; xi < bufWidth-1; xi++)
{
size_t e = (size_t)bufWidth * yi + xi;
size_t f = e + 1;
size_t d = e - 1; size_t b = e - bufWidth;
size_t c = b + 1;
size_t a = b - 1; size_t h = e + bufWidth;
size_t i = h + 1;
size_t g = h - 1; float tmpBuf[9] = { imgBuf[a], imgBuf[b], imgBuf[c], imgBuf[d], imgBuf[e], imgBuf[f], imgBuf[g],imgBuf[h], imgBuf[i] };
double Hillshade = CalHillshade(tmpBuf, Zenith_rad, Azimuth_rad, dx, -dy, z_factor); dstBuf[e] = (GByte)(std::min(std::max(Hillshade, 0.0),255.0));
}
} //写入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, dstBuf, bufWidth, bufHeight,
GDT_Byte, dstBand, nullptr, dstBand*dstDepth, bufWidth*dstBand*dstDepth, dstDepth); //释放
delete[] imgBuf;
imgBuf = nullptr; //释放
delete[] dstBuf;
dstBuf = nullptr; //
GDALClose(dst);
dst = nullptr; GDALClose(img);
img = nullptr; return 0;
}

最终得到的晕渲结果和ArcMap的晕渲结果比较,几乎是一模一样的:



后续会正式在这个基础之上实现彩色的晕渲图。

3. 参考

[1]. ArcGIS帮助:山体阴影工具的工作原理。

[2]. 基于视觉表象的彩色晕渲地图色彩设计.郭礼珍等.2004

使用GDAL实现DEM的地貌晕渲图(二)的更多相关文章

  1. 使用GDAL实现DEM的地貌晕渲图(三)

    目录 1. 原理 1) ArcMap生成彩色晕渲图 2) 彩色色带赋值 3) 颜色叠加 2. 实现 3. 结语 4. 参考 1. 原理 之前在<使用GDAL实现DEM的地貌晕渲图(一)>和 ...

  2. 使用GDAL实现DEM的地貌晕渲图(一)

    目录 1. 原理 1) 点法向量 2) 日照方向 (1) 太阳高度角和太阳方位角 (2) 计算过程 3) 晕渲强度 2. 实现 3. 参考 @ 1. 原理 以前一直以为对DEM的渲染就是简单的根据DE ...

  3. 全球数字高程数据(DEM)详解,还有地形晕渲、等高线等干货

    1 基本概念 DEM是数字高程模型的英文简称(Digital Elevation Model),是研究分析地形.流域.地物识别的重要原始资料.由于DEM 数据能够反映一定分辨率的局部地形特征,因此通过 ...

  4. 到手的DEM不会用?教你6个常用强大功能

    一.概述 DEM是数字高程模型(Digital Elevation Model)的简称,接触GIS,规划,设计类的多多少少会接触到DEM,可是这个直接查看黑溜溜一片DEM到底可以用来做什么呢? 二.背 ...

  5. SuperMap空间数据处理与制图操作短视频汇总

    转自:http://blog.csdn.net/supermapsupport/article/details/70227669 空间数据处理与制图是GIS系统建设最基础的部分,这里利用超图桌面软件- ...

  6. ArcGIS 栅格数据教程

    ArcGIS 栅格数据教程 全部8个教程,带详细操作步骤和原始数据. 技术咨询:谢老师,135_4855_4328,xiexiaokui#139.com ArcGIS 10.5 此教程中的练习将使用样 ...

  7. 山顶点提取(ArcPy实现)

    一.背景 山顶点指哪些在特定邻域分析范围内,该点都比周围点高的区域.山顶点是地形的重要特征点,它的分布与密度反映了地貌的发育特征,同时也制约着地貌发育.因此,如何基于DEM数据正确有效的提取山顶点,在 ...

  8. 地形鞍部的提取(ArcPy实现)

    1.背景 相邻两山头之间呈马鞍形的低凹部分称为鞍部.鞍部点是重要的地形控制点,它和山顶点.山谷点及山脊线.山谷线等构成地形特征点线,对地形具有很强的控制作用.因此,因此,对这些地形特征点.线的分析研究 ...

  9. ArcGIS空间分析工具

    1. 3D分析 1.1. 3D Features toolset 工具 工具 描述 3D Features toolset (3D 要素工具集) Add Z Information 添加 Z 信息 添 ...

随机推荐

  1. AlwaysOn数据同步暂停及回退技术

    随着AlwaysOn技术的流行,关于AlwayOn的问题也越来越多,某企业搭建有三副本的AlwaysOn一套,现想修改主节点上某张表的某个数据,看看会出现什么后果,如果结果正常,就同步到其他节点上:如 ...

  2. 获取UWP配置文件中的版本信息

    原文:获取UWP配置文件中的版本信息 在一般的软件中,我们都会显示当前软件的版本信息.以前作者都是在发版的时候修改一下UWP的配置文件中的版本信息和软件中的版本信息.但是每次这样很麻烦,有时间忘记修改 ...

  3. ML:多变量线性回归(Linear Regression with Multiple Variables)

    引入额外标记 xj(i) 第i个训练样本的第j个特征 x(i) 第i个训练样本对应的列向量(column vector) m 训练样本的数量 n 样本特征的数量 假设函数(hypothesis fun ...

  4. 15 款 jQuery 社交分享插件

    过去几年中社交媒体越来越流行了,能够分享音乐.视频.图像甚至是其他的 docs 文档到互联网上去,这样子还能够提高页面的点击量.通常,一些社交媒体插件都能允许你的用户分享你网站上的内容到其他的社交平台 ...

  5. 使用VS将 XML/Json 转为Model Class文件

    环境: VS2015 Win10   XML例子: <OTA_GetRerStatusRQ EchoToken=" B3BB9248255BD851AC94" UserNam ...

  6. LLVM和GCC的区别(LLVM提供了模块化的编译模块,非常有利于重用,以前的编译器都没有做到这一点)

    最近在Mac OS X Mountain Lion下用Xcode进行开发,发现在编译选项里有如下所示的这两种编译器:一个是Apple LLVM compiler 4.2,另外一个是LLVM GCC 4 ...

  7. C++界面库(十几种,很全)

    刚开始用C++做界面的时候,根本不知道怎么用简陋的MFC控件做出比较美观的界面,后来就开始逐渐接触到BCG  Xtreme ToolkitPro v15.0.1,Skin++,等界面库,以及一些网友自 ...

  8. C++ 使用回调函数的方式 和 作用。 持续更新

    先看两个demo: 一.在类test1中调用函数print() ,把print()的函数指针传递给test1的函数指针参数 test1.h: #include <stdio.h> #inc ...

  9. spring+rabbitmq+stomp搭建websocket消息推送(非spring boot方式)

    前言: 两年前做过spring+activemq+stomp的ws推送,那个做起来很简单,但现在公司用的mq中间件是rabbitmq,因此需要通过rabbitmq去做ws通信.仔细搜了搜百度/谷歌,网 ...

  10. inno setup 配合istools

     istool,可以认为是inno setup的IDE吧   用用就知道了,istool用起来方便些,当然,脚本写好了,用不用istool就无所谓了