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. Android零基础入门第88节:Fragment显示和隐藏、绑定和解绑

    在上一期我们学习了FragmentManager和FragmentTransaction的作用,并用案例学习了Fragment的添加.移除和替换,本期一起来学习Fragment显示和隐藏.绑定和解绑. ...

  2. SQL介绍及MySql的安装

    数据库及SQL概念 数据库是按照数据结构存储和组织数据的仓库 结构化查询语言(Structured Query Language)简称SQL MySql:DBMS MySql安装 安装MySql服务端 ...

  3. Linux --- 程序后台运行的几种方法

    有时候我们运行一个程序,耗时比较长,所以在快下班的时候或是网络不稳定的时候就比较抓狂. 今天分享几个我在工作中用到的把程序放在后台运行的方法. nohup $ nohup --h Usage: noh ...

  4. MySQL InnoDB缓冲池(Buffer Pool)

    InnoDB缓冲池并不仅仅缓存索引,它还会缓存行的数据.自适应哈希索引.插入缓冲(Insert Buffer).锁,以及其他内部数据结构. InnoDB还使用缓冲池来帮助延迟写入,这样就能合并多个写入 ...

  5. WCF研究-后篇

    最后就对之前的资料进行整理以及在其他博客园的朋友那看到的资料稍微分享一下,这样有助于学习和使用WCF的朋友更好的学习和理解WCF 后期要是看到合适的资料也会再次编辑这个后篇,让我共同进步! 后篇 1. ...

  6. python字典的内建函数

    In [70]: test=dict(x=1,y=2,z=3) In [71]: test Out[71]: {'x': 1, 'y': 2, 'z': 3} In [72]: a=['a','b', ...

  7. CodeSmith使用SQLite Provider找不到请求的 .Net Framework 数据提供程序

    关于CodeSmith5使用SQLite Provider时的报错:System.BadImageFormatException: 未能加载文件或程序集“System.Data.SQLite,.... ...

  8. 高并发 Nginx+Lua OpenResty系列(5)——Lua开发库Redis

    Redis客户端 lua-resty-redis是为基于cosocket API的ngx_lua提供的Lua redis客户端,通过它可以完成Redis的操作.默认安装OpenResty时已经自带了该 ...

  9. C# 设计模式,工厂方法

    C#工厂方法 using System; using System.Collections.Generic; using System.Linq; using System.Text; using S ...

  10. 浅入深出Vue:数据渲染

    今天来正式开始 vue的学习,首当其冲的当然是数据的渲染.毕竟数据就是拿来看的,看看如果使用 vue来展示数据. 为什么渲染 俗话说 "人靠衣装马靠鞍", 那咱们的代码就是得靠 U ...