简介:本资源面向地理信息科学、地球物理及遥感方向的科研人员与高年级本科生,提供一套基于GMT(Generic Mapping Tools)实现栅格数据本地化裁剪与山体阴影增强的完整实践方案。针对GIS中常见需求——使用Shapefile矢量边界精确裁剪遥感影像或DEM栅格,并叠加光照模拟地形立体效果,资源封装了从shp读取、坐标匹配、grdclip裁剪到grdimage山体阴影渲染的全流程GMT脚本与配套数据。压缩包共11个文件(289KB),含shp/shx/dbf/prj等标准矢量组件、gmt配置脚本、bat批处理命令及png示例图,结构紧凑,可直接运行调试。已有82人学习下载,读者可即刻获得可复用的裁剪模板、光照参数调优经验及shp与栅格协同处理的关键排错提示,显著降低GMT地理可视化入门门槛。
1. 项目概述:当本地矢量遇上全球栅格
在地理信息处理(GIS)和地球科学制图领域,我们常常会遇到一个非常具体的需求:手头有一份特定区域的矢量边界文件(比如某个县的行政边界、一个研究区的范围,或者一条河流的流域),同时还有一份覆盖范围更大的栅格数据(比如全球地形数据、遥感影像或气候模型输出)。我们的目标很明确,就是把这个大范围的栅格数据,精准地“裁剪”到我们关心的那个小区域里,并在此基础上,制作出具有立体感、能直观反映地形起伏的山体阴影图。这听起来像是专业GIS软件(如ArcGIS)的典型工作,但今天我们要聊的,是如何用一款在科研和制图领域备受推崇的命令行工具——GMT(Generic Mapping Tools)——来高效、精准地完成这项任务。
你可能会问,为什么不用更“傻瓜式”的桌面软件?原因在于效率、可重复性和批量处理能力。当你需要处理成百上千个区域,或者你的数据处理流程需要嵌入到一个自动化的脚本中时,命令行工具的优势就凸显出来了。GMT正是这样一款利器,它以强大的数据处理和高质量制图能力著称,尤其擅长处理全球尺度的网格数据。本次项目“GMT使用本地shp文件裁剪栅格文件并使用山体阴影”,核心就是解决“如何利用本地的Shapefile矢量文件作为蒙版,去裁剪一个栅格文件,并基于裁剪后的地形数据生成美观的山体阴影图”这一问题。
整个过程可以拆解为几个关键环节:首先是环境与数据的准备,确保你的GMT安装正确,并且手头有合适的.shp矢量文件和.grd(或其它格式)栅格文件;其次是用GMT读取并处理这些数据;核心步骤是执行裁剪操作;最后是基于裁剪结果计算并可视化山体阴影。虽然项目标题提及了.rar压缩包,这通常意味着里面包含了示例数据和可能的工作脚本,但我们的重点在于理解其背后的原理和命令流程,这样你就能举一反三,处理自己的数据了。接下来,我将以一个具体的场景为例,假设我们手头有中国云南省的县级行政区划Shapefile,以及一份SRTM全球地形数据,目标是制作某个县的地形山体阴影图。
2. 前期准备:理解你的数据与工具链
在动手写任何命令之前,花点时间弄清楚你手里的“原料”和“厨具”至关重要。这一步没做好,后面很可能步步维艰。
2.1 核心工具:GMT的安装与模块认知
GMT不是一个单一的软件,而是一个庞大的工具集。我们不需要一次性掌握所有命令,但必须了解本项目会用到的几个核心模块:
gmt:这是GMT 6版本后的主命令,所有功能都通过它来调用,格式如gmt module [options]。grdcut/grdclip:栅格裁剪的核心命令。虽然grdcut常用于按矩形范围裁剪,但结合其他工具也能实现基于矢量的复杂裁剪。grdmask:这是实现矢量裁剪栅格的关键命令。它的本质是创建一个与栅格文件同范围、同分辨率的“掩膜(mask)网格”,在矢量区域内的值设为1(或指定值),区域外的值设为0(或NaN)。然后利用这个掩膜网格与原栅格进行计算,从而实现裁剪。grdgradient:用于计算山体阴影(hillshade)或坡度(gradient)。它需要输入地形栅格,通过模拟光照效果,输出一个表示光照强度的新栅格,这是生成立体地形图的灵魂。grdimage:用于将栅格数据(无论是原始地形还是山体阴影)渲染成图像。pscoast,psxy:用于绘制海岸线、边界、矢量要素等。在成果图中叠加行政区划边界会使得地图更专业。grdinfo:查看栅格文件信息的利器,比如范围、网格间距、数据格式等,在操作前务必先用它摸清数据底细。
确保你的GMT已正确安装,并且版本在6.0以上。可以在终端输入gmt --version来验证。如果还没安装,请根据你的操作系统(Linux/macOS/Windows WSL)去GMT官网查找安装指南。
2.2 数据准备:栅格与矢量的“握手”
数据是项目的基石。我们需要两类数据:
A. 栅格文件(待裁剪对象)常见格式有NetCDF (.grd, .nc),GeoTIFF (.tif),ESRI .asc等。GMT对NetCDF格式支持最原生。例如,我们可以使用SRTM(航天飞机雷达地形测绘任务)的90米或30米分辨率数字高程模型(DEM)。假设我们已经下载了覆盖中国区域的SRTM数据,并拼接成了一个名为srtm_china.grd的文件。
提示:在操作前,务必使用
gmt grdinfo srtm_china.grd查看其经纬度范围(x_min, x_max, y_min, y_max)、网格间距和数据类型。记下这个范围,后续设置绘图区域时会用到。
B. 矢量文件(裁剪模具)这就是我们的本地Shapefile(.shp)。一个完整的Shapefile通常由多个文件组成(.shp, .shx, .dbf, .prj等),你需要确保这些文件都在同一目录下,并且GMT能够读取。GMT通过psxy或grdmask命令的-S选项来识别矢量数据。 例如,我们有一个云南省某县边界的Shapefile,文件基名为county_boundary(即目录下存在county_boundary.shp,county_boundary.shx等文件)。
关键兼容性检查:
- 投影一致性:这是最容易出错的地方。栅格数据和矢量数据必须使用相同的地理坐标系或投影坐标系。你可以通过
.prj文件或使用GDAL的ogrinfo/gdalinfo命令来查看数据的投影信息。如果两者投影不同,必须先进行投影转换,确保它们“说同一种语言”。GMT虽然能在绘图时进行动态投影转换,但在进行像grdmask这类网格操作时,强烈建议先统一投影。 - 范围包含关系:你的矢量区域必须完全或部分位于栅格数据的覆盖范围内。如果你想裁剪的区域完全在栅格范围之外,那结果自然是空的。
3. 核心操作解析:从矢量掩膜到栅格裁剪
理解了工具和数据,我们就可以进入核心的裁剪流程了。这个过程不是简单的一步到位,而是通过创建一个中间产品——掩膜网格——来实现的。
3.1 第一步:创建矢量掩膜网格
如前所述,grdmask是我们的核心武器。它的作用是:在一个指定的网格范围内(这个范围通常要略大于你的矢量区域,并且包含你的栅格数据范围),生成一个新的网格文件。这个新网格在矢量多边形内部的节点上赋值为1,外部的节点上赋值为0(或者No Data值,如NaN)。
假设我们的栅格数据srtm_china.grd的范围是100E/110E/20N/30N,网格间距是0.000833度(约90米)。我们想用county_boundary.shp来创建掩膜。
一个基本的命令如下:
gmt grdmask county_boundary.shp -Gsrtm_china.grd -R100/110/20/30 -I0.000833 -N0/1/1 -r -V -Mcounty_mask.grd让我们拆解这个命令:
county_boundary.shp:输入的矢量文件。-Gsrtm_china.grd:这是一个关键参数。它告诉grdmask参考srtm_china.grd的网格注册方式(像素点注册还是网格线注册)和数据类型。这能确保生成的掩膜网格与原地形网格在空间上完全对齐,这是后续正确计算的前提。你也可以用-R -I直接指定范围和间隔,但用-G引用原文件更不容易出错。-R100/110/20/30:指定生成掩膜的区域范围(经度最小值/最大值/纬度最小值/最大值)。这里我们用了原栅格的范围,确保掩膜覆盖整个可能区域。-I0.000833:指定输出掩膜网格的间距(分辨率)。这里设置为和原地形数据一致。-N0/1/1:这是掩膜值的设置。格式为outside/inside/boundary。0/1/1表示多边形外部值为0,内部值为1,边界上也设为1。你也可以用-NNaN/1/1,这样外部就是NaN(非数字),在后续计算中会被自动忽略。-r:注册方式。确保与原栅格一致(通常是网格线注册-r或像素注册-rp)。使用-G引用原文件时,这个参数有时可以省略,因为会继承原文件的属性。-V:显示详细处理信息,方便调试。Mcounty_mask.grd:输出的掩膜网格文件名。
执行完这一步,你会得到一个county_mask.grd文件。你可以用gmt grdimage county_mask.grd -JX10c -B -C快速查看一下,它应该是一个二值图,你的目标区域是白色(值1),其他区域是黑色(值0)。
3.2 第二步:应用掩膜,裁剪栅格
有了掩膜网格,裁剪就变成了一个简单的网格间乘法或条件赋值操作。我们使用grdmath命令。
gmt grdmath srtm_china.grd county_mask.grd MUL = county_dem_clipped.grd这个命令非常直观:grdmath是GMT的网格计算器。srtm_china.grd MUL county_mask.grd表示将地形网格与掩膜网格逐像元相乘。在掩膜为1的区域,地形值乘以1保持不变;在掩膜为0的区域,地形值乘以0就变成了0。这样,我们就得到了一个仅在目标区域有地形值、其他区域为0的新网格county_dem_clipped.grd。
如果你在创建掩膜时使用了-NNaN/1/1,那么外部是NaN。NaN与任何数进行算术运算结果都是NaN。所以乘法后,区域外依然是NaN,区域内保留原值。NaN在GMT中被视为“无数据”,在绘图和后续处理中会被自动忽略,这通常是更干净的做法。命令是一样的。
为什么是乘法?这是一种高效且数学上清晰的掩膜方法。除了乘法,你也可以用grdclip或grdmath的条件语句来实现,但乘法是最直接和常见的。
3.3 第三步:计算山体阴影
裁剪得到了纯净的县域地形数据county_dem_clipped.grd,现在可以为它制作“光影效果”了。这用到grdgradient命令。
gmt grdgradient county_dem_clipped.grd -Gcounty_hillshade.grd -A315 -Nt0.8 -Vcounty_dem_clipped.grd:输入的地形网格。-Gcounty_hillshade.grd:输出的山体阴影网格。-A315:光照方向(方位角)。315度表示光线从西北方向照射(这是制图中非常经典的角度,能产生良好的立体感)。你可以调整这个值来改变阴影方向。-Nt0.8:-N指定标准化方式,t表示使用-A指定的方位角进行地形斜率计算,0.8是一个增强因子(exaggeration factor)。值1.0表示原始坡度,小于1会减弱阴影对比,大于1会增强。0.8是一个比较稳健的默认值,能使地形起伏看起来更自然,避免过强的“浮雕感”。-V:显示进度。
生成的county_hillshade.grd是一个灰度网格,值通常在 -1 到 1 之间,表示每个像元受到光照的强度(亮到暗)。
4. 成果可视化:绘制专业地形图
有了裁剪后的地形和计算好的山体阴影,我们就可以绘制一张专业的地形图了。通常,我们会将山体阴影作为底图(提供明暗纹理),再给地形高度叠加上颜色(提供高程信息)。
4.1 创建色标文件(CPT)
首先,我们需要一个颜色映射表(CPT)来将高程值映射为颜色。我们可以基于裁剪后地形数据的范围来生成。
# 首先获取裁剪后地形的高程范围 gmt grdinfo county_dem_clipped.grd -T100 # 假设输出建议的色标分段是 -T100/5000/100,即从100米到5000米,每100米一段。 # 然后基于这个范围创建一个色标。这里使用GMT内置的‘dem2’色标,它非常适合地形。 gmt makecpt -Cdem2 -T100/5000/100 -Z > topography.cpt-Cdem2:使用内置的dem2配色方案。-T100/5000/100:指定颜色映射的数据范围(最小值/最大值/间隔)。这里的值需要根据你实际的grdinfo输出进行调整。-Z:创建连续变化的色标。> topography.cpt:将生成的色标保存到文件。
4.2 绘制地图
现在,使用grdimage来组合绘制。通常的顺序是:先绘制山体阴影(作为灰度底图),再在上面叠加彩色地形(使用半透明效果,让阴影透上来)。
gmt begin county_topographic_map pdf # 1. 设置绘图区域和投影。这里使用墨卡托投影,范围由裁剪后的地形决定。 gmt grdinfo county_dem_clipped.grd -I- # 获取数据的精确范围,假设是 R102.5/103.5/24.8/25.5 gmt basemap -R102.5/103.5/24.8/25.5 -JM10c -Baf -BWSen+t"云南省XX县地形图" # 2. 首先绘制山体阴影(灰度图)。使用 -I 选项将山体阴影网格作为强度(光照)图层。 gmt grdimage county_hillshade.grd -I -Q # 3. 在上面叠加彩色地形。使用 -C 指定色标,-t 设置透明度(例如50%,即0.5)。 gmt grdimage county_dem_clipped.grd -Ctopography.cpt -t50 # 4. 叠加行政区划边界,使其更清晰。 gmt psxy county_boundary.shp -W1p,black -O -K # 5. 添加比例尺和图例 gmt basemap -Tm103.2/24.9+w2c+o0c/0.5c # 比例尺 gmt colorbar -Ctopography.cpt -Bxa1000f500 -By+l"Elevation (m)" -DJMR+w5c/0.3c+h -O gmt end命令解析:
gmt begin ... end:这是GMT 6的现代模式,用于管理一个绘图会话。-JM10c:使用墨卡托投影,地图宽度为10厘米。-Baf:自动绘制带有刻度的边框。-I在grdimage中:表示接下来的网格(county_hillshade.grd)将作为强度图层(即山体阴影)使用,它会调制后面绘制的彩色地形的亮度。-Q:禁用插值,对于山体阴影这种表示纹理的网格,禁用插值可以保持其清晰度。-t50:设置50%的透明度,这样彩色地形不会完全遮盖住底下的山体阴影纹理,两者融合效果更好。-W1p,black:用1点粗的黑色线绘制矢量边界。
执行上述脚本后,你将得到一个名为county_topographic_map.pdf的高质量矢量图,它清晰地展示了该县的地形起伏,兼具科学性与美观性。
5. 实战中的陷阱与精进技巧
按照上述流程,你大概率能成功出图。但在实际项目中,总会遇到一些“坑”。下面分享几个我踩过之后总结出来的经验。
5.1 矢量数据自身的问题
问题1:Shapefile多边形不闭合或自相交这是一个常见问题,尤其来自某些不太规范的来源。grdmask在处理有几何错误的多边形时可能会失败或产生奇怪的结果。
- 排查:使用
ogrinfo -al county_boundary.shp | grep -i "ring"或QGIS等软件检查几何有效性。 - 解决:在GMT外部修复。推荐使用
GDAL/OGR的ogr2ogr命令:
这个命令会尝试修复几何错误。然后用修复后的文件进行后续操作。ogr2ogr -f "ESRI Shapefile" county_boundary_fixed.shp county_boundary.shp -nlt POLYGON -makevalid
问题2:矢量与栅格范围不匹配,掩膜结果为全NaN或全0
- 排查:首先分别用
gmt grdinfo和ogrinfo -so查看两者的范围。确保矢量至少有一部分落在栅格范围内。 - 解决:如果矢量范围远大于栅格,考虑先裁剪矢量。如果只是略有偏差,可以适当扩大
grdmask的-R范围,确保覆盖矢量区域。但最根本的还是要保证数据源的空间参考一致。
5.2 栅格数据处理中的精度与性能
问题:裁剪后边缘有锯齿或数据异常
- 原因:这可能源于两个网格在边界处像元不对齐,或者原始栅格数据本身在边界就有异常值(如SRTM数据边缘的填充值)。
- 解决:
- 对齐:确保
grdmask的-R、-I、-r参数与原始地形网格完全一致。使用-G引用原文件是最稳妥的方法。 - 处理NoData:在裁剪前,先处理原始地形中的无效值。例如,SRTM的海洋区域可能是-32768。你可以先用
grdclip将其设为NaN:
然后用处理过的gmt grdclip srtm_china.grd -Sb-32767/NaN -Sa-32769/NaN -G srtm_china_nan.grdsrtm_china_nan.grd进行后续操作。
- 对齐:确保
性能优化:处理大范围高分辨率数据全球高分辨率地形数据(如30米SRTM)体积庞大。直接对整个大文件进行grdmask和grdmath操作可能非常慢且消耗内存。
- 技巧:先粗略确定矢量范围,然后用
grdcut将大栅格切出一个稍大的子区域,再对这个子区域进行精细的矢量裁剪。
这能极大提升处理速度。# 先用 ogrinfo 获取矢量范围,假设是 102/104/24/26 gmt grdcut srtm_china.grd -R102/104/24/26 -G srtm_subregion.grd # 然后基于 srtm_subregion.grd 和矢量文件进行上述掩膜、裁剪流程
5.3 山体阴影效果的艺术性调整
默认参数生成的山体阴影可能太“平”或太“刺眼”。
- 光照方向(-A):315度是标准,但尝试45度(东北光)或135度(东南光)可能会突出不同的地形特征。
- 增强因子(-Nt):
-Nt1.2会增加对比度,让山脉看起来更陡峭;-Nt0.5会减弱对比度,效果更柔和。对于丘陵地区,可能需要调高;对于高山地区,默认值可能就很好。 - 多方向光照合成:这是制作出版级地形图的技巧。通过组合两个或多个不同方向的光照,可以消除单一光源造成的死角阴影,让地形细节更丰富。
然后将gmt grdgradient county_dem_clipped.grd -Ghill_nw.grd -A315 -Nt1 gmt grdgradient county_dem_clipped.grd -Ghill_ne.grd -A45 -Nt0.6 gmt grdmath hill_nw.grd hill_ne.grd ADD 2 DIV = hill_combined.grdhill_combined.grd用作强度图层。这需要一些实验来找到最佳权重组合。
5.4 自动化与批处理
如果你需要对多个县(多个Shapefile)执行相同的操作,手动重复是不可接受的。编写一个Shell脚本(Bash)或Python脚本是必然选择。
一个简单的Bash脚本框架如下:
#!/bin/bash # 假设所有县的shp文件都在 county_shps/ 目录下,命名为 county_01.shp, county_02.shp ... BASE_DEM="srtm_china.grd" for SHP in county_shps/*.shp; do COUNTY_NAME=$(basename "$SHP" .shp) echo "Processing $COUNTY_NAME..." # 1. 创建掩膜 gmt grdmask "$SHP" -G"$BASE_DEM" -NNaN/1/1 -r -M"mask_${COUNTY_NAME}.grd" # 2. 裁剪DEM gmt grdmath "$BASE_DEM" "mask_${COUNTY_NAME}.grd" MUL = "dem_${COUNTY_NAME}.grd" # 3. 计算山体阴影 gmt grdgradient "dem_${COUNTY_NAME}.grd" -G"hillshade_${COUNTY_NAME}.grd" -A315 -Nt0.8 # 4. 绘制地图 (这里需要为每个县定制 -R 范围,可以从裁剪后的DEM获取) RANGE=$(gmt grdinfo "dem_${COUNTY_NAME}.grd" -I-) gmt begin "map_${COUNTY_NAME}" pdf gmt basemap -R$RANGE -JM10c -Baf -BWSen+t"${COUNTY_NAME}地形图" gmt grdimage "hillshade_${COUNTY_NAME}.grd" -I -Q gmt grdimage "dem_${COUNTY_NAME}.grd" -Ctopography.cpt -t50 gmt psxy "$SHP" -W0.5p,black gmt colorbar -Ctopography.cpt -DJMR+w5c/0.3c+h -Bxa1000f500 -By+l"Elevation (m)" gmt end # 5. 清理中间文件(可选) rm "mask_${COUNTY_NAME}.grd" done echo "All counties processed!"这个脚本实现了自动化批量处理,大大提升了工作效率。关键在于利用循环和变量,将单次流程封装起来。
本文还有配套的精品资源,点击获取