简介:这份资源面向地理信息、城乡规划、环境研究及遥感分析方向的学习者与从业者,提供云南省普洱市30米分辨率的DEM数字高程数据,并附带区域行政边界矢量文件,可用于地形分析、制图渲染、洪水模拟与空间规划等场景。压缩包共12个文件,约211.48MB,以tif栅格高程数据为核心,配合shp、dbf、prj、shx等Shapefile组件描述区域边界与坐标系统,另有ovr、tfw、xml等辅助文件保障影像显示与元数据完整。目前已有527人学习下载。借助这套数据,读者可直接在QGIS或ArcGIS中完成地形起伏提取、坡度坡向计算与边界裁剪,省去自行搜集与配准的环节,是开展普洱市地理分析与制图练习的实用底图素材。
1. 普洱30m DEM到手之后:先搞清这套数据能干什么、不能干什么
拿到“云南省普洱市DEM数字高程数据30m(含区域范围shp文件).zip”这类数据包,很多人第一反应是解压、拖进ArcGIS,然后发现——咦,怎么只有一片灰不溜秋的栅格,说好的普洱市呢?其实这个包的核心就两样东西:一份30m分辨率的数字高程栅格(通常是tif或img格式),一份普洱市行政边界的shp面文件。前者告诉你每一格地面有多高,后者告诉你哪些格子属于普洱。30m这个精度,意味着每个像素代表地面30米×30米的区域,对市级尺度的地形分析、坡度坡向提取、流域划分、选址踏勘来说完全够用;但你要是想拿它做茶园梯田的施工放样,那就属于用错了工具。这篇笔记就按一线干活的顺序,把从数据检查、坐标对齐、按shp裁剪DEM,到坡度提取和常见翻车点,一步步讲清楚。适合手里已经拿到这套数据、准备做普洱本地地形分析的人。
2. 拆开压缩包先别急着拖进软件:数据自检与坐标系统一
2.1 包内文件到底哪个是主角
一个典型的DEM数据包解压后,通常包含以下文件。不要看到一堆文件就懵,按用途分就三类:高程栅格、边界矢量、附带的元数据。
| 文件类型 | 常见扩展名 | 作用 | 是否必须 |
|---|---|---|---|
| 高程栅格 | .tif / .img / .dem | 存储每个格网的高程值 | 必须 |
| 区域范围 | .shp + .shx + .dbf + .prj | 普洱市行政边界,用于裁剪和统计 | 必须 |
| 元数据 | .xml / .txt | 记录坐标系、精度、来源 | 建议保留 |
| 金字塔 | .rrd / .ovr | 加速显示,可重建 | 可删 |
重点看两个东西:栅格的坐标系和shp的坐标系。如果两者不一致,后面裁剪一定出问题。用ArcGIS的话,在Catalog里右键属性看Spatial Reference;用QGIS的话,右键图层属性看CRS。普洱市常见的坐标系有CGCS2000地理坐标系(经纬度,单位度)和CGCS2000 3度带投影坐标系(单位米)。30m分辨率的数据,如果原始是经纬度,实际地面分辨率会随纬度变化,在普洱一带大约相当于经度方向30m、纬度方向也接近30m,但做面积和距离计算时最好转成投影坐标。
2.2 用Python快速读取栅格和矢量基本信息
不想开笨重的桌面软件,可以用rasterio和geopandas几行代码把底细摸清。下面这段脚本我经常用来做数据入场检查。
import rasterio import geopandas as gpd # 读取DEM栅格 dem_path = "puer_dem_30m.tif" with rasterio.open(dem_path) as src: print("栅格尺寸:", src.width, "x", src.height) print("波段数:", src.count) print("坐标系:", src.crs) print("地理范围:", src.bounds) print("像元大小:", src.res) # 输出如 (0.000277, 0.000277) 表示度 print("无效值:", src.nodata) # 读取区域范围shp shp_path = "puer_boundary.shp" gdf = gpd.read_file(shp_path) print("要素数量:", len(gdf)) print("shp坐标系:", gdf.crs) print("边界范围:", gdf.total_bounds)逻辑说明:rasterio.open读取栅格元数据,重点看crs和res。如果res是0.000277这种小数,说明是地理坐标系,单位是度;如果是30左右,说明是投影坐标系,单位是米。geopandas读取shp后看crs是否与栅格一致。参数方面,nodata很关键,常见是-9999或-32768,裁剪和统计时要把它排除,否则最小值会变成-9999,坡度计算直接崩。
2.3 坐标系不一致时的统一策略
如果发现栅格是地理坐标系、shp是投影坐标系,或者反过来,不要硬裁。正确做法是统一到投影坐标系,因为30m分辨率做地形分析,用米为单位更直观。用QGIS的话,右键图层导出时选择目标CRS;用ArcGIS的话,用Project Raster工具转栅格,用Project工具转矢量。转完再确认一次两者的范围是否重叠。普洱市大致在东经99°到102°、北纬22°到24°之间,如果转出来范围跑到国外去了,说明投影参数选错了。
提示:转坐标系之前先备份原始文件。投影变换是有损的,尤其是栅格重采样,双线性插值和最近邻结果不同,高程数据建议用双线性或三次卷积。
3. 按shp裁剪DEM:ArcGIS、QGIS和Python三条路都走一遍
3.1 ArcGIS里用面图层裁剪栅格的标准操作
这是热搜里反复出现的问题:在ArcMap中依靠面图层裁剪DEM栅格tif文件。步骤不复杂,但参数选错的人不少。
第一步,打开ArcMap或ArcGIS Pro,加载DEM栅格和普洱市边界shp。第二步,打开ArcToolbox,找到Spatial Analyst Tools → Extraction → Extract by Mask。第三步,Input raster选DEM,Input raster or feature mask data选shp,Output raster指定输出路径。第四步,点环境设置,确认Output Coordinates与输入一致,Processing Extent选与shp相同或自动。第五步,运行。
关键参数在Extract by Mask里其实就一个:是否勾选“Use Input Features for Clipping Geometry”。勾上,输出范围严格按shp边界;不勾,输出范围是shp的外接矩形。做普洱市地形分析,通常要勾上,否则你会得到一块矩形区域,里面包含相邻州市的高程。
3.2 QGIS里用Clip raster by mask layer更顺手
QGIS的裁剪工具在Raster → Extraction → Clip Raster by Mask Layer。参数更直白:Mask layer选shp,Source CRS和Target CRS保持一致,勾选“Match the extent of the clipped raster to the extent of the mask layer”。输出格式选GeoTIFF。如果shp有多个面要素,比如普洱市下辖各区县,可以勾选“Keep resolution of input raster”,这样输出分辨率不变。
一个容易忽略的点:QGIS默认会用掩膜图层的范围裁剪,但如果shp的坐标系和栅格不一致,工具会报错或输出空白。所以第2章的自检步骤不能省。
3.3 Python批量裁剪:适合多个区县一次跑完
如果普洱市下辖的每个区县都要单独裁一份DEM,用arcpy或rasterio写循环最省事。下面用rasterio.mask实现。
import rasterio from rasterio.mask import mask import geopandas as gpd import os dem_path = "puer_dem_30m.tif" shp_path = "puer_counties.shp" out_dir = "output_counties" os.makedirs(out_dir, exist_ok=True) gdf = gpd.read_file(shp_path) with rasterio.open(dem_path) as src: for idx, row in gdf.iterrows(): geom = [row.geometry.__geo_interface__] try: out_image, out_transform = mask(src, geom, crop=True, nodata=src.nodata) out_meta = src.meta.copy() out_meta.update({ "height": out_image.shape[1], "width": out_image.shape[2], "transform": out_transform, "nodata": src.nodata }) name = row.get("NAME", f"county_{idx}") out_path = os.path.join(out_dir, f"{name}_dem.tif") with rasterio.open(out_path, "w", **out_meta) as dest: dest.write(out_image) print(f"已输出: {out_path}") except Exception as e: print(f"第{idx}个要素裁剪失败: {e}")逻辑说明:mask函数接收栅格数据集和几何对象列表,crop=True表示按几何边界裁剪而不是外接矩形,nodata继承源数据。out_meta更新宽高和仿射变换,保证输出栅格的地理位置正确。参数方面,如果shp字段名不是NAME,改成实际字段;如果某个区县与DEM范围无交集,会抛异常,用try包住避免中断。
3.4 裁剪结果验证:别只看图,要看数值
裁剪完不要只肉眼看形状对不对。用rasterio读一下统计值:最小值、最大值、均值。普洱市海拔范围大致在300米到3300米之间,如果裁剪结果最小值是-9999,说明nodata没处理好;如果最大值超过4000,可能混入了其他区域的数据。再检查像元数量,普洱市面积约4.5万平方公里,30m分辨率下大约5000万个像元,裁剪后数量应该在这个量级附近。
4. 从DEM到坡度坡向:提取地形因子的参数怎么设
4.1 坡度提取:ArcGIS和Python结果为什么不一样
坡度是DEM最常用的衍生因子。ArcGIS的Slope工具在Spatial Analyst → Surface → Slope,输入DEM,输出单位可选Degree或Percent。Python里用richdem或gdaldem也能算。但很多人发现两个软件算出来的坡度有差异,原因通常是算法不同:ArcGIS默认用Horn算法(三阶反距离平方权差分),GDAL的gdaldem slope默认用Zevenbergen & Thorne算法。两者在平坦地区差异小,在陡坡地区可能差1到2度。
如果只是做宏观地形分析,这点差异可以接受;如果要做工程边坡分级,建议统一用同一种算法,并在报告里注明。参数上,Z因子(Z factor)很关键。地理坐标系下,Z因子要设成纬度对应的换算系数,普洱一带大约取1.0左右(因为30m分辨率下经纬度与米的换算接近1:1);投影坐标系下,Z因子保持1。设错了,坡度会整体偏大或偏小。
4.2 坡向和山体阴影:参数少但容易出玄学
坡向(Aspect)输出的是0到360度的方向,0代表正北,90代表正东。ArcGIS的Aspect工具没有太多参数,但要注意:平坦区域(坡度接近0)的坡向会被赋值为-1,统计时要排除。山体阴影(Hillshade)需要设方位角和高度角,默认315度和45度,适合大多数场景。如果做普洱这种多山地区,可以调成270度和30度,让山脊和沟谷的立体感更强。
4.3 用gdaldem命令行批量生成坡度坡向
如果不想开图形界面,GDAL的命令行工具非常高效。下面三条命令分别生成坡度、坡向和山体阴影。
# 生成坡度,单位度,Z因子1 gdaldem slope puer_dem_30m.tif puer_slope.tif -p -z 1 -of GTiff # 生成坡向 gdaldem aspect puer_dem_30m.tif puer_aspect.tif -of GTiff # 生成山体阴影,方位角315,高度角45 gdaldem hillshade puer_dem_30m.tif puer_hillshade.tif -az 315 -alt 45 -of GTiff逻辑说明:-p表示输出百分比坡度,不加则输出度;-z是Z因子;-az和-alt控制光照方向。这些命令可以直接写进批处理脚本,对多个区县DEM循环执行。注意gdaldem要求输入栅格有正确的nodata值,否则边缘会出现异常坡度。
5. 避坑与排查:普洱DEM处理中最容易翻车的5件事
5.1 裁剪后栅格全黑或全白
现象:Extract by Mask跑完,加载输出栅格,显示全黑或全白,拉伸后也看不到地形。
原因:最常见的是nodata值设置冲突。源DEM的nodata是-9999,裁剪后输出栅格的nodata被自动改成0或其他值,导致显示时把0当成有效高程,整个栅格看起来就是一片平地。另一个原因是输出栅格的统计值没有重新计算,ArcGIS默认用旧统计值渲染。
解决:在ArcGIS里右键输出栅格 → Data → Export Raster时勾选“Use Renderer”或手动计算统计值(Catalog里右键 → Calculate Statistics)。用Python的话,在out_meta里显式写入nodata=src.nodata,并确保写入后重新打开检查。
5.2 shp边界和DEM对不上,裁剪出来是空
现象:运行裁剪工具后报错“ERROR 999999”或输出栅格没有任何像元。
原因:shp和DEM的坐标系不一致,或者shp的范围与DEM完全不重叠。有时候shp看起来在普洱,但坐标系被错误定义为WGS84 UTM 47N,而DEM是48N,差一个带号,位置就偏了几百公里。
解决:先用第2章的脚本打印两者的bounds,对比经纬度范围。如果shp的bounds明显偏离普洱,用Define Projection重新定义正确的坐标系,再用Project做转换。不要直接用Define Projection改投影,那只是改标签,不改坐标值。
5.3 坡度计算结果出现大量0或异常值
现象:坡度栅格中大片区域值为0,或者出现超过90度的值。
原因:DEM中存在nodata区域,但坡度工具没有正确识别nodata,把-9999当成高程参与计算,导致相邻像元高差巨大,坡度异常。另一个原因是Z因子设错,地理坐标系下没设Z因子,坡度值整体偏小。
解决:计算坡度前,用Set Null或Con工具把nodata区域掩膜掉。在ArcGIS的Slope工具环境设置里,把Mask设为DEM的nodata范围。Python里用numpy把nodata替换成np.nan再计算。
5.4 裁剪边界出现锯齿或黑边
现象:按shp裁剪后,边界不是平滑的行政界线,而是锯齿状,或者边界外有一圈黑边。
原因:栅格是格网结构,shp边界是矢量,裁剪时按像元中心点是否在面内判断,边界像元会被取舍,产生锯齿。黑边通常是输出时背景值没设为nodata。
解决:锯齿是正常现象,30m分辨率下无法避免。如果要求边界平滑,可以先用shp生成掩膜栅格,再做栅格乘法。黑边问题在输出时设置nodata为-9999或0,并在显示时设为透明。
5.5 文件太大,处理速度慢
现象:普洱市全域30m DEM大约几十MB到上百MB,但加上坡度、坡向、山体阴影后,文件夹迅速膨胀,ArcGIS操作卡顿。
原因:GeoTIFF默认不压缩,每个像元占4字节或8字节。5000万像元的浮点栅格就是200MB到400MB。
解决:输出时启用压缩。ArcGIS的Export Raster里选LZW或DEFLATE压缩;GDAL用-co COMPRESS=LZW。如果只做显示,可以建金字塔(.ovr文件)。另外,把不需要的中间文件及时清理,别让它们堆在工程目录里。
6. 进阶技巧:用DEM做普洱市地形起伏度分级和流域快速划分
6.1 地形起伏度:一个窗口统计的实用参数
地形起伏度是指单位面积内最高海拔与最低海拔之差,常用窗口有3×3、5×5、10×10像元。30m分辨率下,5×5窗口相当于150m×150m范围,适合分析普洱这种山地丘陵区。在ArcGIS里用Focal Statistics:邻域选Rectangle 5×5,统计类型选Range,输出就是起伏度栅格。然后按自然间断点分级,一般分成微起伏(<30m)、小起伏(30-70m)、中起伏(70-150m)、大起伏(>150m)。普洱市大部分区域属于小起伏和中起伏,局部澜沧江沿岸有大起伏。
6.2 用DEM和shp快速划分小流域
流域划分通常需要填洼、计算流向、流量累积、提取河网、生成流域边界。ArcGIS的Hydrology工具集可以一键完成,但参数多。一个简化做法:先用Fill填洼,再用Flow Direction计算流向,然后用Flow Accumulation计算汇流累积量,设定阈值(比如1000个像元)提取河网,最后用Watershed工具以河网节点为出口生成流域。普洱市属于澜沧江水系,划分结果可以和实际水系图对比验证。
6.3 我踩过的坑和固定习惯
早期做普洱DEM分析时,我图省事直接用地理坐标系算坡度,结果坡度值整体偏小,后来才发现Z因子没设。还有一次裁剪完没检查nodata,出图时整个区域一片灰,被同事笑了半天。现在我的固定习惯是:拿到数据先跑一遍第2章的自检脚本,裁剪后必看统计值,坡度计算前必确认Z因子和nodata。这套流程虽然多花十分钟,但省掉了后面反复返工的后悔药。希望帮到你。
本文还有配套的精品资源,点击获取