首页 / 资讯中心 / 文章详情

30m DEM数据读取、裁剪与地形分析:从Python实践到宿州案例

30m DEM数据读取、裁剪与地形分析:从Python实践到宿州案例 ★ FEATURED ARTICLE
简介安徽省宿州市30米分辨率的DEM数字高程数据包面向GIS分析、城市规划、地质灾害评估及环境研究人员覆盖宿州市全域并包含周边部分区域可直接用于地形分析、坡度坡向计算、汇水区模拟和区域对比研究。压缩包共12个文件约33.64MB核心为TIFF格式高程栅格另含TFW地理配准文件、Shapefile矢量范围及其属性、投影定义文件、空间索引与XML元数据便于在ArcGIS、QGIS等主流平台中直接打开和叠加分析。已有380人学习下载该套数据省去了自行下载原始DEM、裁切边界、转换坐标的步骤拿到手即可开展宿州地区的高程查询、等高线生成、视线分析等应用适合地理信息初学者快速上手也是专业人员做前期地形评估的便捷基础数据。1. 一份30m的宿州DEM压缩包先别急着解压它到底能做什么拿到“安徽省宿州市DEM数字高程数据30m含区域范围shp文件.zip”这类资源时第一反应别是双击解压先想清楚一个前提30m分辨率在DEM数据里属于中精度对宿州市所处的皖北平原地带来说海拔起伏整体不大每一格对应地面30m乘30m的范围做市县级的地形骨架、坡度分区、汇水路径判断完全够用但你要是拿它去做某段道路的施工放样那就超出了这份数字高程数据的精度边界。这份资源能解决的实际问题很具体没有实测高程时做地形分析底图或者拿区域边界shp配合做研究区裁剪和坡度反演适合规划评估、水利水文和农业条件分析方向的从业者。2. 拆开压缩包先看三样东西文件后缀、坐标系和shp四件套2.1 先看后缀是GeoTIFF还是IMG这决定工具链解压之前先用资源管理器看一眼压缩包内层文件的扩展名。市面上30m DEM数据常见的载体有三种GeoTIFF.tif、IMG.img和ASCII Grid.asc。从工具链兼容性看GeoTIFF最省心高程数值数组和地理坐标元数据封装在同一个文件里QGIS、ArcGIS和Python的rasterio直接就能读不需要额外找配套参数。IMG格式是旧平台遗留产物日常查看问题不大但用rasterio直接打开时经常因为缺少内嵌坐标系信息而出错得先用一次gdalinfo把参数逼出来。ASC是纯文本存储坐标信息完整但文件体积比GeoTIFF大好几倍读取速度也慢一般不推荐做主流工作流。我习惯性拿到压缩包先执行一条命令看文件清单unzip -l 宿州市DEM数据.zip这条命令不实际解压只是列表。重点看靠前的几条记录里有没有.tfw或.tif.aux.xml这些伴随文件。.tfw是栅格世界文件用于记录像元大小和左上角坐标.tif.aux.xml是GDAL生成的辅助元数据文件。有这两个文件说明数据发布方把地理配准信息外置了读取时软件会自动关联但如果.tif主文件在包内而.tfw被单独拷到别的目录就会出现有图层但位置对不上的情况。判断完后缀再动手是处理地理信息数据的第一步习惯。2.2 坐标系判定经纬度还是投影坐标直接决定叠不叠得起来宿州市地处皖北平原30m DEM的坐标系统逃不开两种形态一种是用经纬度存储的地理坐标系通常基于WGS84或CGCS2000单位是度剖面里分辨率一栏会显示类似0.00028这样的小数另一种是高斯-克吕格投影或UTM投影单位是米分辨率一栏直接显示30.0。判断方法很简单用rasterio打开后看profile的输出。投影选型的核心原则是尽量保持DEM原始坐标系不变叠加矢量数据时把矢量重投影到DEM的坐标系而不是反过来。原因在于栅格重投影必然触发重采样每一轮重采样都会重新计算像元高程值30m数据本身精度就有限反复重投影等于反复做平滑高精度细节越丢越多。矢量数据重投影只涉及顶点坐标换算不产生信息损失。这一点做水文分析时尤其重要流向计算对相邻像元的高程差极其敏感一旦高程被平滑掉洼地细节汇水路径就全歪了。2.3 shp边界文件不是单文件缺.prj会错位几十公里“含区域范围shp文件”这句提示看着省事实际是这套数据里隐藏坑最多的地方。规范的一份shapefile至少由.shp、.shx、.dbf三个基础文件组成缺一个软件就打不开更关键是还有一个.prj文件记录坐标系统。如果压缩包里只有.shp和.shx而缺少.prjQGIS或ArcGIS会默认按WGS84猜一个坐标系读入结果就是县级边界套到DEM上直接错位轻则歪几公里重则跑到相邻区域去。拿到资源后的第三个动作是核对边界文件所在目录下的文件件数。完整四件套是.shp、.shx、.dbf、.prj讲究一点的还有.cpg用来声明属性表的字符编码。缺.prj时的临时应对办法是手动指定坐标系但你得先确认这份shp本来应该用哪个坐标系常见做法是拿shp范围和DEM的范围对比如果两者bbox边界接近重合说明坐标系统一致直接给shp补一个和DEM一样的.prj就能对齐如果对不上就得逐个试候选坐标系这在后面避坑章节再展开。3. 用Python把DEM读出来、画成图、按边界裁出来三个可复现步骤3.1 先读元数据别急着把整块栅格载入内存处理DEM的第一个动作不是加载全部数据而是读取元数据。用rasterio打开文件后先打印profile确认四件事数据类型是16位整数还是32位浮点、有没有nodata值、宽度和高度是多少、坐标系是什么。这四件套决定后面所有处理能不能跑通。import rasterio with rasterio.open(宿州市dem.tif) as src: profile src.profile bounds src.bounds crs src.crs nodata src.nodata print(分辨率/类型信息:, profile) print(数据范围:, bounds) print(坐标系:, crs) print(无效值标记:, nodata)逻辑说明src.profile返回一个字典包含driver、width、height、count、dtype、crs、transform和nodata这些关键键值对。dtype里如果是int16或uint16说明高程以整数存储注意后续坡度计算要转成浮点如果nodata是-9999这类值就要在载入时处理无效区域。transform实际上是仿射变换参数从中能读出像元大小30m数据正常显示为(30.0, 30.0)。参数说明src.bounds输出的是左下角和右下角的坐标元组拿它跟shp的范围往复核对就能完成坐标系统一致性初判。这一步跑完你会看到类似{driver: GTiff, dtype: float32, nodata: -9999.0, width: 5000, height: 4000, crs: CRS.from_epsg(4490)}的输出。看到CRS.from_epsg(4490)就说明这份数据用的是CGCS2000地理坐标系如果显示EPSG:32650则是WGS84 UTM 50N投影带。不同来源数据习惯不同以实际输出为准。3.2 把高程渲染成地形图顺便验证数据有没有坏块读完元数据后做第一次可视化验证这一步能快速暴露数据里面的坑数据空洞、异常高值、全黑全白渲染等问题肉眼一眼就能看出来。import numpy as np import matplotlib.pyplot as plt import rasterio with rasterio.open(宿州市dem.tif) as src: elev src.read(1) # 第一个波段就是高程 nodata src.nodata # 将无效值掩盖掉避免渲染出黑边和伪高墙 elev_masked np.ma.masked_equal(elev, nodata) fig, ax plt.subplots(figsize(10, 8)) im ax.imshow(elev_masked, cmapterrain, vminnp.nanpercentile(elev_masked, 2), vmaxnp.nanpercentile(elev_masked, 98)) plt.colorbar(im, axax, labelElevation (m)) plt.title(宿州DEM 30m 地形渲染) plt.show()逻辑说明elev src.read(1)读入的二维数组形状等于profile里的(height, width)数组里存的就是每个像元的高程值。np.ma.masked_equal()把等于nodata的像元打上掩膜这样matplotlib在渲染时不会把这个异常值当作真实高程。vmin和vmax取2%和98%分位数是为了对抗直方图两端的极端值如果数据里混杂着个别几百米的异常像元不做这一步整张图的花色会被拉伸到失真空。参数说明cmapterrain是matplotlib自带的梯度配色蓝色表示低海拔、棕绿色表示高海拔vmin/vmax原意是直方图拉伸范围这里手动指定分位数比默认的min-max拉伸稳健。看到图像上如果有明显的孤立噪点或整块黑色区域基本可以判断该区域存在NoData空洞后面分析前要做插值或单独处理。3.3 用shp边界做裁剪让数据严格对齐研究区拿到宿州市的shp边界最常见的用途就是把全域DEM裁剪成实际需要的研究区范围。这一步用rasterio.mask实现最简单几行代码就能完成。import geopandas as gpd import rasterio from rasterio.mask import mask # 读取边界文件 boundary gpd.read_file(宿州市边界.shp) with rasterio.open(宿州市dem.tif) as src: # 把边界转换到DEM的坐标系 boundary boundary.to_crs(src.crs) # 裁剪栅格 out_image, out_transform mask( src, shapesboundary.geometry, cropTrue, # 裁掉边界矩形以外的区域 nodatasrc.nodata # 保留原NoData值填充裁剪后无效区 ) out_meta src.meta.copy() # 更新元数据中的尺寸和仿射变换参数 out_meta.update({ height: out_image.shape[1], width: out_image.shape[2], transform: out_transform }) with rasterio.open(宿州市dem_cropped.tif, w, **out_meta) as dst: dst.write(out_image)逻辑说明gpd.read_file读入矢量边界后用to_crs(src.crs)把矢量重投影到栅格坐标系这一步避免了前面强调的坐标系不一致问题。mask(..., cropTrue)会把输出范围压缩到边界要素的外接矩形内数据量会大幅减小。最后的out_meta.update()必须同步修改高度、宽度和仿射变换否则写出的文件地理参考是错的。参数说明nodatasrc.nodata这里容易被忽略。裁剪后边界多边形外侧区域会填充原先的NoData值如果这一步不指定rasterio默认用0填充生成的裁剪图会用一堆0m高程去污染后续坡度计算。做完裁剪后再渲染一次确认边界贴合再开始正式的地形分析。4. 避坑与常见问题处理DEM数据时最容易翻车的五个场景4.1 坐标系错位几十公里叠上边界发现跑到隔壁市现象把shp边界和DEM同时拖进GIS软件边界线和栅格边缘相差很远量测一下差了几十公里。 原因shp缺少.prj坐标系文件软件按默认WGS84猜测读入与DEM实际使用的坐标系不一致。 解决先比较shp和DEM的包围盒范围。跑一下前文写的gpd.read_file(boundary).crs发现crs为空就手动补齐常见做法是先尝试与DEM相同的EPSG代码并重新加载看是否重合如果范围差在百公里级多半是坐标系类型不同要切换到UTM投影带再试。用代码解决比在软件里手动点选更可控把试错过程记录下来。4.2 渲染全是黑白色台阶高程范围明显不像宿州地形现象地形图渲染出来不是自然的青绿渐变而是强烈的黑白台阶颜色条上的极值和宿州平原高度严重不符。 原因数据类型没有正确识别或者NoData值没做掩膜处理。常见的是原始DEM用int16存储高程但读入时没有把nodata-9999排除掉-9999这个值参与了高度量化把整个色彩拉伸区间都挤爆了。 解决回到3.1节打印profile确认dtype和nodata。如果读入的数组max值显示几千万先np.ma.masked_equal(elev, nodata)再做直方图统计。还有一类情况是原始文件头部存在空值坏块需要对照渲染图逐块排查。4.3 填洼操作把真实洼地也填平了水文路径全乱现象做完水文分析提取出来的河网在平原区变成一条直线或者原本该汇聚到低洼耕地的水全部流到了沟渠上。 原因水文分析前置步骤要求填洼但宿州皖北平原区域农田沟渠、人工水塘繁多这些是真实地形中的微洼地。把fill_sinks当作无脑必做步骤对待结果把真实微地形也抹平了。 解决填洼前先算一次流向和汇流累积量对比填洼前后洼地区域的分布差异。常见做法是只填深度小于阈值的洼地而不是全部填平或者填洼后检查被填充区域数量如果超过总面积一定比例就要小心。做水文分析时30m网格在平原区的微地形可信度本身就有限确定的阈值骤减。4.4 从30m重采样成10m以为是精度提升现象用重采样工具把DEM从30m改成10m出图细腻了但坡度分区结果和原始数据差异巨大。 原因重采样不产生新信息只有插值。10m的网格里90%的新像元值都是由周围30m像元推算出来的属于数学猜测不是实测。平原区这一效果不明显但到了海拔变化稍大的区域插值造成的台阶感特别明显。 解决区分“分辨率”和“精度”数据源是30m后期不管重采样成多少米信息量都不会超过原始值。如果后续要更细致的坡度分析应该去找更高精度的数据源而不是拿重采样糊弄。4.5 读取路径带中文导致文件打不开现象压缩包解压后在自己电脑上rasterio读不了文件报错dataset is not a valid或No such file但文件明明在。 原因部分旧版本的GDAL以及没有配置好环境变量的rasterio组合对Windows系统含中文字符的路径支持不好。 解决把工作目录改成纯英文路径或者把文件复制到D:/dem_work/data/这类路径再操作。路径不带中文这个习惯要养成尤其在配合gdalwarp、gdal_contour等命令行工具的时候乱码路径会引发各种玄学问题。5. 把这份30m数据用出更大价值坡度坡向、等高线与水文分析一条龙当基础读图和非裁剪都跑通之后30m DEM能做的地形衍生分析比大部分人预期的要多。这里讲一条完整链条坡度坡向计算、等高线提取、填洼与河网提取每个环节都有固定参数要控制。坡度坡向计算适合最先做。30m数据在宿州这种起伏平缓的区域坡度角跨度一般在2到8度之间但平原区坡度直方图会有大量接近0的值这点在分析土地利用条件时要特别注意切割程度的参数设置不能按山地标准来。常用工具是gdaldem命令行一条指令即可完成gdaldem slope 宿州市dem_cropped.tif 宿州市坡度.tif -p -s 111120 gdaldem aspect 宿州市dem_cropped.tif 宿州市坡向.tif逻辑说明slope后面的-p参数表示以百分数输出坡度-s是水平距离缩放因子处理经纬度坐标系的数据时必须加这个参数。-s 111120的含义是输入数据以度为单位时将每度水平距离换算成约111120米没有这个参数时计算出来的坡度值会严重失真。参数说明如果数据是投影坐标系不需要加-s输出坡度在平原区基本上都集中在个位数不要看到一片浅色就怀疑出错了。等高线提取适合做地形专题图底图。30m网格提取等高线常见做法是设置间隔20米但宿州平原区海拔低20米间隔能画出的等高线数量有限改5米或10米才能看到曲面变化。gdal_contour -a elev -interval 10 宿州市dem_cropped.tif 宿州市等高线.shp逻辑说明-a elev指定属性字段名记录每条等高线对应的高程值-interval 10表示等高距10米。拿生成的等高线和坡度图互相印证能快速判断出哪里有陡坡哪里是缓坡。参数说明等高线提取出来后发现锯齿感很重可以考虑先用gdal_translate做一次轻量平滑但要注意过度平滑后线会和原始高程脱钩。最后是水文分析一条龙填洼、流向、汇流累积和河网阈值。燃map工具链在QGIS里可以处理参数按以下顺序走分析步骤常用工具关键参数说明填洼SAGA Fill Sinks最小洼地深度不设或设0.5宿州平原人工沟渠多最小深度设过大会抹平真实洼地流向D8算法方向类型MFDMFD多流向分配比D8在平缓区更合理但计算慢汇流累积Flow Accumulation数据类型浮点输出为每个像元的上游汇水面积河网提取阈值法/斯特拉勒法阈值按累积量百分位取常见做法是取累积量前1%作为河网起点水文链条第1条跑完后提取河网和原始的shp边界叠加能很直观看出排水方向是否合理。如果发现主河道走向和shp边界不太相符先回查填洼参数和DEM的确认坐标系。这套流程做完下一步就是把坡度、坡向和高程三个栅格叠成一张分析底图。这份30m DEM虽然在微观尺度上有它的局限但在市县级尺度的地形分析任务里属于性价比不错的选择。最后说一个我自己的习惯以前处理某模拟项目X的DEM数据时我跳过元数据检查直接做填洼计算结果整个研究区的河网全歪了事后排查两天发现是NoData值参与计算把边缘区域污染了。从那以后每次处理DEM数据我都强制依次走四步检查读profile确认dtype和nodata、确认坐标系是否与shp对齐、渲染一遍看有没有坏块、再做任何衍生分析。这四个步骤做完再动手能避开绝大多数回不了头的错误路径希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站