news 2026/9/11 22:02:17

福州市30m DEM数据处理实战:从解包裁剪到坡度等高线生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
福州市30m DEM数据处理实战:从解包裁剪到坡度等高线生成

简介:福建省福州市30米分辨率的DEM数字高程数据包,面向GIS从业者、城乡规划人员与地理信息专业学生,满足地形起伏分析、坡度坡向计算、洪水淹没模拟、交通选线等应用需求,30米分辨率在宏观尺度上兼顾了精度与数据量。压缩包共12个文件,核心是福州市DEM.tif,以30米×30米栅格表达地表海拔,覆盖福州市全境并包含周边部分区域;同时附有福州市范围.shp矢量边界,配套prj坐标参考文件、dbf属性表、sbn/sbx空间索引以及xml元数据,用户在ArcGIS、QGIS等软件中加载后即可直接使用,无需自行配准、定义投影或裁剪范围。整个压缩包约35.83MB,体积紧凑,便于分发和本地存储。目前已有1300人学习下载,可用于城乡规划、灾害评估和GIS教学实训;包内DEM影像还能生成等高线、制作三维地形场景、提取坡度坡向因子,配合shp边界可快速统计不同行政区内的高程分布特征,适合科研项目、工程实践与课堂教学数据支撑。

1. 福建省福州市DEM数字高程数据zip的解包与数据组织

“福建省福州市DEM数字高程数据30m(含区域范围shp文件).zip”这个压缩包,名字已经把内容拆得很清楚:一份标称30米分辨率的DEM栅格,外加一份福州市区域范围矢量边界。凡是做地形分析、洪涝淹没模拟、道路选线、甚至光伏选址的人,手里大概率都存过同款格式的数据包。真正拿到手以后,问题通常不在“有没有数据”,而在“数据能不能被直接用”:DEM坐标基准是什么?shp和栅格是否叠加在同一位置?nodata填充值是多少?如果这些不确定,后面所有坡度、面积统计都是白算。

下面按“先看元数据,再裁剪对齐,然后做应用,最后校验交付”的顺序展开。用到的是GDAL、QGIS和Python,依赖小、脚本可带走。无论你是第一次打开这个zip,还是给甲方做数据预处理,都可以照下面的参数先跑一遍,再去调整细节。

2. DEM数字高程数据30m的分辨率、数据源与坐标系判断

2.1 30m像元能看见什么,看不见什么

DEM的30m指的是每个像元覆盖地面上约30m×30m范围内的高程平均值。福州市区丘陵居多,闽江沿岸和鼓山一带高差大。30m网格能说清整个山体的走势,能算出坡度是5度还是15度,但看不清一条10m宽的冲沟,也压不住单栋建筑的高程影响。相比之下,热词里常被问到的ALOS 12.5m DEM就能多分辨出一些细小地形。12.5m像元面积是30m像元的约六分之一,理论上细节多,数据量也接近6倍,处理时间不只线性上涨。

在实际项目里选哪个分辨率,通常不是“越高越好”,而是和底图分辨率匹配。如果你只是叠加2020道路shp做宏观选线,30m足够;如果要细化到一个小型地块的汇水边界,最好换12.5m或机载LiDAR。30m的优势是覆盖范围规整、生产标准成熟,很多区域范围shp和流域数据都是基于30m DEM切出来的,交接成本低。

2.2 用gdalinfo和ogrinfo查清坐标系和分辨率

拿到数据包里的dem文件,第一件事不是拿去算,而是用命令行看元数据。以GDAL 3.x环境为例:

gdalinfo dem_30m.tif

输出里重点看三行:Size规定栅格宽高像素数;Origin规定左上角坐标;Pixel Size规定每个像元在地图单位下的宽高;Coordinate System is之后的文字是空间参考。如果Pixel Size是0.00027777778,说明栅格是地理坐标系,这个值约等于1秒,也就是赤道附近30m。如果Pixel Size是30,说明已经是投影坐标,单位是米。

再看shp矢量边界:

ogrinfo -so fuzhou_boundary.shp fuzhou_boundary

-so是“summary only”的意思,只输出图层概要,而不是逐要素打印。重点看Extent(空间范围)和Layer SRS(空间参考)。如果shp的范围和dem栅格范围对不上,或者一个在经纬度一个在投影米,后续步骤必须统一。这里有个容易忽略的细节:ogrinfo的输出里shp的Extent单位必须与SRS一致,不要只看到数字就以为是经纬度。

2.3 没有readme时怎么推断DEM来源

压缩包如果只给一个tif和shp,没有readme,我会用三个指标判断来源。第一,看nodata:常见SRTM衍生数据经常用-32768或-9999。第二,看覆盖范围:如果tif的经纬度范围正好覆盖福州市辖区,通常说明生产方用市级行政边界做了一次裁剪。第三,看边缘:30m栅格在海岸带经常出现负值,这是因为部分地区的高程基准和海洋深度数据拼接时没有完全对齐。

常见公共DEM数据源对比:

数据源标称分辨率常见坐标系主要弱项
SRTM 90m90mWGS84地理坐标细节不够
SRTM 1弧秒30mWGS84地理坐标部分版本有空洞
ALOS AW3D3030mWGS84/UTM下载分块多
ALOS 12.5m12.5mUTM数据量大
ASTER GDEM30mWGS84影像伪影多

这张表不负责判断你手上这份是哪种,但它告诉你:同样是30m,不同生产机构的精度和空洞分布并不一样。如果之后看到坡度结果里有长条状的低值,多半是原始数据源本身的问题,不是你的处理流程不对。

3. 用GDAL把福州市DEM与shp裁剪到同一坐标系和范围

3.1 解压后先核对shp的四个伴生文件

shp文件不是单文件。解压后能看到fuzhou_boundary.shp、fuzhou_boundary.shx、fuzhou_boundary.dbf、fuzhou_boundary.prj等。.shx是索引,.dbf是属性表,.prj是坐标系定义。如果压缩包里只有.shp没有.prj,后面在QGIS里会默认用WGS84估算,一旦带入真实工程就会偏位。收到数据先跑一句:

unzip -l "福建省福州市DEM数字高程数据30m(含区域范围shp文件).zip"

列出zip内容清单,核对关键文件。如果发现缺.prj,或者文件名为乱码,可以在QGIS里手动指定坐标系,但更稳妥的办法是让提供方补一份,因为没有坐标系的空间边界不具备可交换性。shp配套表可以参考:

扩展名作用缺失影响
.shp几何要素主文件不可用
.shx几何索引无法在GIS中正常绘制
.dbf属性表要素无法带属性
.prj坐标系定义无法准确落图
.cpg属性编码中文属性乱码

3.2 裁剪dem数据:gdalwarp按shp精确裁剪

最常见的需求是“把福州之外的高程都去掉”,这是对dem文件的常规裁剪操作。用gdalwarp按shp边界裁剪:

gdalwarp -cutline fuzhou_boundary.shp -crop_to_cutline \ -dstnodata -9999 -co COMPRESS=DEFLATE -co TILED=YES \ dem_30m.tif fuzhou_dem_clip.tif

参数解释:-cutline指定裁剪矢量,shp可直接使用;-crop_to_cutline把输出栅格范围收紧到矢量边界的实际范围,而不是只做掩膜;-dstnodata -9999把边界外的像素填充为-9999,防止后续统计把0值当真实高程;-co COMPRESS=DEFLATE启用压缩,栅格体积通常会小一半,-co TILED=YES把内部存储改为瓦片,后续读取局部区域更快。

如果只想按矩形范围快速切一块,不加shp也行:

gdal_translate -projwin 118.8 26.4 120.2 25.2 \ -a_srs EPSG:4326 dem_30m.tif west_fuzhou.tif

-projwin的参数顺序是左上角x、左上角y、右下角x、右下角y。新手容易在25.2和26.4之间颠倒上下来回改,gdal_translate不会报错,只会输出一张空栅格。注意gdalwarp是按边界形状精确裁剪,gdal_translate只能切外接矩形,两者用途不同。

提示:裁剪后用QGIS加载fuzhou_dem_clip.tif,如果边界外显示黑色,并不是数据坏了,而是背景被填了-9999。把渲染的nodata选项勾上即可。

3.3 shp转txt:导出福州边界坐标用于交接和校验

“shp转txt”的本质是把矢量几何转成可读文本,便于交给不装GIS的开发或写报告。我一般直接转CSV并把坐标系改成WGS84经纬度:

ogr2ogr -f CSV fuzhou_boundary_wgs84.csv fuzhou_boundary.shp \ -t_srs EPSG:4326 -lco GEOMETRY=AS_XY

-t_srs EPSG:4326把输出坐标转成经纬度;-lco GEOMETRY=AS_XY在CSV中额外生成X、Y两列;图层里的MultiPolygon会用重复的点表达,需要在外层处理。如果GDAL版本较老,GEOMETRY=AS_XY可能不生效,用Python读更可靠。

from osgeo import ogr ds = ogr.Open("fuzhou_boundary.shp") layer = ds.GetLayer(0) with open("fuzhou_boundary.txt", "w", encoding="utf-8") as fp: for feature in layer: geom = feature.geometry() if geom is not None: fp.write(geom.ExportToWkt() + "\n")

这段代码把每个要素的几何导出成WKT字符串,一行一个多边形。WKT比CSV更适合表达复杂边界,因为它保留了环的顺序和子多边形边界。实际交接时如果对方只要坐标点,再在WKT基础上做字符串拆分即可。

3.4 统一坐标系:shp与DEM投影不一致时优先做哪个

如果gdalinfo显示dem是WGS84,shp是CGCS2000高斯投影,gdalwarp裁剪时会在内部临时转换,但结果容易卡在重投影环节。更可控的做法是先统一到一种坐标,再裁剪。判断基准很简单:看shp的.prj,如果单位是米,而栅格的Pixel Size是度,就先把shp转成地理坐标再裁剪。gdalwarp也支持-t_srs,但建议把裁剪和重投影分两步执行,哪个环节出错一目了然。转换shp用一条命令即可:

ogr2ogr -t_srs EPSG:4326 fuzhou_boundary_wgs84.shp fuzhou_boundary.shp

这样生成的shp文件可以用QGIS打开,也能作为后续gdalwarp的cutline。福州市常用投影带是120度中央经线的东西带,直接选WGS84经纬度能避免带号出错的麻烦。

4. 把福州市30m DEM转成坡度、等高线和区域统计结果

4.1 坡度坡向与山体阴影:三个gdaldem参数表

DEM最常见的二次产品是坡度、坡向和山体阴影。GDAL的gdaldem命令一次只能算一种,优点是不用装桌面软件。对福州市这种起伏较大的区域,30m栅格算坡度得到的角度能反映沟谷密度和断裂走向。常用参数:

gdaldem slope dem_30m.tif fuzhou_slope.tif -p -s 111120 gdaldem aspect dem_30m.tif fuzhou_aspect.tif -compass gdaldem hillshade dem_30m.tif fuzhou_hillshade.tif -az 315 -alt 45

第一条命令里-p要求输出坡度百分比,如果去掉-p则输出0到90的度数值,不少人的坡度直方图和自己预期对不上,往往就是百分比和度数的差别。-s 111120是垂直放大倍数,只在地理坐标系下生效,含义是“1度约等于111120米”;投影坐标系下应该删除这个参数。第二条-compass把坡向输出为北向0度顺时针到360度,如果不加,0度指东,和常用的方位表示不一致。第三条的-az 315设置光源来自西北,-alt 45设置太阳高度角45度,适合观察福州西北向山体形态。

参数调整可以参考:

输出命令必调参数常见误用
坡度gdaldem slope-p / -s投影坐标系下仍留-s
坡向gdaldem aspect-compass0度方向错误
山体阴影gdaldem hillshade-az -alt高度角过高没有阴影

4.2 DSM和DEM的关系:什么时候需要从DSM生成DEM

很多城市级高程包其实是DSM,表面高程包含了建筑和树冠。DSM直接算坡度,屋顶和道路连接处会形成断层。热词“dsm生成dem”描述的正是这种前处理:把DSM中的地物盖层去掉,还原裸露地表高程。概念上,DEM是地形表面,DSM是地物表面,两者之差是冠层高度。处理时常用移动窗口低通滤波,或者用LiDAR点云做地面分类。对30m格网来说,直接在DSM上做低通滤波会把真正的山脊线一起抹平,所以我会先测一次滤波前后的坡度差异;如果差异集中在城区,再决定是否做DSM转DEM。

4.3 用gdal_contour生成shp文件:等高线直接交付CAD

等高线是dem文件的矢量产品,也是“生成shp文件”最常见的场景。命令:

gdal_contour -a elev -i 20 fuzhou_dem_clip.tif fuzhou_contour_20m.shp

-a elev表示在输出shp属性表里加一个叫elev的字段记录高程值;-i 20表示每20米画一条等高线。福州市区闽江两岸高差小,20米一条会显得稀疏,到了鼓山一带又过密,所以绘制大区域时常用分段间距:先按30米生成,再在关键区域单独加密到5米。gdal_contour输出的shp线条数量在城区可能达到几万条,建议生成后用ogr2ogr -simplify做线条简化再交付。

4.4 用shp对DEM做分区统计:各区县平均高程

这一步回答“福州市某一片区平均高程是多少”这类问题。用rasterio的mask函数先按shp边界裁剪,再对像元值做统计。

import geopandas as gpd import rasterio from rasterio.mask import mask import numpy as np # 如果你的shp是区县边界,循环计算每个区域 city = gpd.read_file("fuzhou_counties.shp") with rasterio.open("fuzhou_dem_clip.tif") as src: for _, row in city.iterrows(): geom = row.geometry out_img, out_transform = mask(src, [geom], crop=True) data = out_img[0] valid = data[data != src.nodata] print(row["name"], valid.min(), valid.max(), valid.mean())

注意mask()的第二个参数需要几何对象列表,传入DataFrame整列会有隐藏坑。crop=True保证输出范围对齐shp边界。src.nodata取的是文件真实填充值,如果文件里写的是-32768而代码里写-9999,统计结果会混入无效像元。需要土方量时,再对valid乘以30×30即可。

5. 用无头脚本校验DEM与shp的完整性和坐标一致性

5.1 用Python脚本检查栅格与shp是否套合

收到zip后,在交付别人之前,我会跑一段快速校验脚本,确认三件事:shp和DEM是否覆盖同一范围,分辨率是否标注错误,nodata是否被当作真实高程。脚本只依赖osgeo:

from osgeo import gdal, ogr dem = gdal.Open("fuzhou_dem_clip.tif") gt = dem.GetGeoTransform() cols, rows = dem.RasterXSize, dem.RasterYSize width = cols * abs(gt[1]) height = rows * abs(gt[5]) shp = ogr.Open("fuzhou_boundary.shp") extent = shp.GetLayer(0).GetExtent() extent_width = extent[1] - extent[0] extent_height = extent[3] - extent[2] x_ok = abs(width - extent_width) / extent_width < 0.01 y_ok = abs(height - extent_height) / extent_height < 0.01 print("栅格范围与shp匹配:", x_ok and y_ok)

这段代码从GetGeoTransform()取出像元宽高,乘以栅格行列数得到DEM的总跨度;再与shp的Extent()比较。相对误差小于1%就认为匹配,避免直接在经纬度和米之间比较。如果裁剪前有大量余量,宽高会比shp范围大出几百倍,打印结果立刻能发现问题。

5.2 大范围任务时用渔网分割shp降内存

福州一个市的范围,单块30m栅格通常只有几百万像元,直接处理没问题。但如果把整个福建省的DEM一起灌进来,或者要做逐格网统计,内存会先爆。热词“渔网分割shp”就是这个场景的解法:在QGIS的Vector creation – Create grid工具里生成矩形渔网,再用Clip把shp按格网切片,每片单独算坡度,最后用gdalbuildvrt拼回一个VRT。批量循环里一次只打开一个切片文件,峰值内存可以降到原来的几十分之一。

5.3 输出文件的最终验证

数据交接的最后一公里经常是“文件打开后显示黑色”,原因多半是nodata没有做透明处理。确认方法:

gdalinfo -stats fuzhou_dem_clip.tif

-stats会计算最大值最小值。如果最小值和nodata一致,说明栅格沿边界仍有留空区,在QGIS中使用“Singleband pseudocolor”渲染时选择“Min/Max”并把nodata设为透明即可。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 22:01:48

绿联DXP2800 GT:万兆NAS如何实现新手零门槛家用落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 22:01:00

Rust并发安全:深入理解Send与Sync trait及线程安全实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 21:59:50

净利润包括哪些项目?净利润怎么分析?

月底出报表那几天&#xff0c;我基本不敢约朋友吃饭。从ERP导收入成本&#xff0c;从报销系统导费用&#xff0c;从CRM导销售数据&#xff0c;三个系统口径还不一样&#xff0c;手工拼到一张表里&#xff0c;净利润数字经常对不上。上个月为了核对一个子公司的净利润&#xff0…

作者头像 李华
网站建设 2026/9/11 21:58:30

MFC自绘图表完全指南:GDI坐标映射与曲线/柱状/饼图实现

简介&#xff1a;一份基于MFC类库编写的图表绘制源码工程&#xff0c;面向熟悉C基础语法、希望进阶Windows GUI开发的学习者&#xff0c;也可作为高校《Visual C程序设计》课程设计或软件工程师快速实现数据可视化的参考。它围绕CDC设备上下文与GDI绘图机制&#xff0c;示范了曲…

作者头像 李华
网站建设 2026/9/11 21:58:15

用 Sway 实现智能合约版 FizzBuzz:从 ABI 设计到链上调用

用 Sway 实现智能合约版 FizzBuzz&#xff1a;从 ABI 设计到链上调用 【免费下载链接】sway &#x1f334; Empowering everyone to build reliable and efficient smart contracts. 项目地址: https://gitcode.com/GitHub_Trending/sw/sway Sway 语言官方书籍的 FizzBu…

作者头像 李华
网站建设 2026/9/11 21:57:39

从RAR到Piano Roll:MIDI钢琴数据集清洗与标准化实战

简介&#xff1a;面向米哈游音乐二创爱好者和音乐信息检索、生成方向研究者的精选钢琴二创数据集&#xff0c;数据源自《原神》《崩坏&#xff1a;星穹铁道》等米哈游旗下游戏的标志性旋律再创作。整理方在原始网络乐谱基础上&#xff0c;补充了游戏内地区名与结构信息作为关键…

作者头像 李华