简介:澄迈县30米分辨率DEM数字高程模型数据包,覆盖海南省澄迈县并附带市级范围Shapefile边界,适合GIS初学者、城乡规划、地质与环境专业学生进行地形分析、坡度坡向计算、水文模拟等练习。数据包共12个文件,核心为TIFF高程栅格(含ovr与aux辅助文件),可直接读取海拔信息;澄迈县范围shp及其dbf、prj、shp.xml配套文件则提供边界属性、坐标投影与元数据,另有tfw地理配准文件和sbn/sbx空间索引,整体约4.89MB,压缩包结构简洁,可导入ArcGIS、QGIS等主流GIS平台。目前已有264人学习下载,作为理解DEM数据结构的学习范例,也可用于生成等高线、制作三维地形、提取流域特征等基础研究。需注意该数据集标注为学习练习用途,若用于商业项目应事先确认授权许可。
1. 拿到的是数据包,不是现成答案
海南省澄迈县 DEM 数字高程数据 30m(含市级范围 shp 文件).zip,光看名字容易误判成“下载完解压就能用”。实际打开后你会发现里面往往是两部分:一是覆盖澄迈县及周边范围的 30 米分辨率 DEM 栅格,二是用来限定工作区的市级行政区划 SHP 文件,zip 只是外层打包。这个组合的常见用途是:用 SHP 做掩膜去裁剪 DEM、把高程转成坡度坡向、或者统一坐标系后做地形分析。对 GIS 从业者来说,坑不在下载,而在解压后的投影不一致、边界有缝隙、栅格出现 NoData 黑边。本文按“先搞清数据形态、再从命令行和可视化两个路径处理、最后做质检”的顺序,直接把可复现的命令和参数写出来。
2. 从 zip 到可用数据:文件结构、坐标参照和常见误判
2.1 先别急着解压:zip 内部结构决定了后续操作路径
拿到这个 zip,我一般会先看一眼压缩包内部布局,而不是双击直接全部解压。原因是这类数据包经常出现“嵌套文件夹”或“重复文件名”问题,如果里面同时存在dem.tif和dem.tfw,解压顺序错了会导致栅格配准信息丢失。
常见做法是用命令行工具列出 zip 内容,比图形界面更可控。在 Windows 上可以用 PowerShell 的Expand-Archive,但它不支持只查看内容,所以我通常用tar或unzip来预览结构。Windows 10 1803 之后的系统自带了tar.exe,可以直接用:
tar -tf "海南省澄迈县DEM数字高程数据30m(含市级范围shp文件).zip"-t表示列出内容,-f指定文件名。这一步能让你看到 zip 内部是否有顶层文件夹,以及 SHP 文件的三个伴生文件(.shp、.shx、.dbf)是否齐全。缺少.shx时 SHP 仍可能在多数软件中打开,但缺少.dbf时属性表会变成空壳,这点在后续按行政区裁切时非常关键。
解压时建议保留目录结构,避免把栅格和矢量混在同一个扁平目录里:
tar -xf "海南省澄迈县DEM数字高程数据30m(含市级范围shp文件).zip" -C ./chengmai_dem-C指定解压目标目录。注意:如果 zip 内本身有一个顶层文件夹,解压后会变成./chengmai_dem/xxx/...,后续脚本里引用路径时要多注意一层。
2.2 这是不是“真正的 30 米 DEM”?先看像素分辨率再谈精度
很多用户拿到“30m DEM”就直接用,但这里的 30m 通常有两种含义:一是分辨率约等于 30 米,二是数据源来自 ASTER GDEM 或 ALOS AW3D30 这类全球产品。ASTER GDEM 的标称分辨率是 30 米(1 弧秒),但实际地面分辨率并非严格 30 米,尤其在地形起伏大的区域会出现条带噪声。ALOS 12.5 米 DEM 数据本身比 30 米更细,但如果被重采样到 30 米,精度不一定优于原生 30 米产品。
打开 DEM 后第一步不是看颜色,而是用gdalinfo确认以下四个值:
Size:像元数量,不能直接反映地面分辨率Origin:左上角坐标Pixel Size:像元尺寸,单位通常为度或米EPSG:坐标系代码
在命令行里执行:
gdalinfo chengmai_dem/dem.tif如果输出里Pixel Size显示为(0.00027777778, -0.00027777778),这表示约 1 弧秒,接近 30 米,但单位是度。如果你的 SHP 是投影坐标系(比如 EPSG:32649,WGS 84 / UTM zone 49N),而 DEM 是地理坐标系(EPSG:4326),必须做统一处理。
一个容易踩的坑:用 ArcMap 直接加载时,软件会自动做动态投影,看起来两者重合得很好,但一旦做Extract by Mask或Zonal Statistics,就会因为空间参考不完全一致而报错或产生偏移。我一般会在处理前先写个小函数检查一致性:
from osgeo import gdal, ogr dem = gdal.Open("chengmai_dem/dem.tif") shp = ogr.Open("chengmai_data/city_boundary.shp") dem_srs = dem.GetProjection() lyr = shp.GetLayer() shp_srs = lyr.GetSpatialRef().ExportToWkt() print("DEM SRS matches SHP SRS:", dem_srs == shp_srs)这段代码如果输出False,不要试着强行叠加,而是先把 SHP 投影到 DEM 的坐标系,或者反过来重采样 DEM 到 SHP 的坐标系。决策依据是:如果你后续要提取等高线或计算坡度,建议以 DEM 的坐标系为准,因为重采样栅格会改变高程值,而矢量投影不会改变几何精度。
2.3 一个 zip 里两种格式,是顺手打包还是有意组织?
压缩包名里特意写“含市级范围 shp 文件”,说明数据提供方的意图是让用户借 SHP 来限定分析范围。这里有个细节:打包者可能用的是海南省级或海口市级的行政边界,但文件名写澄迈县,内部 SHP 的范围却覆盖了周边市县。这不算错误,但你必须先看 SHP 的要素范围,再决定是原样使用还是重新裁剪你真正需要的区域。
查看 SHP 范围的最快方式是用 OGR 信息:
ogrinfo -so -al chengmai_data/city_boundary.shp-so表示只输出摘要,-al表示列出所有图层。输出中的Extent字段就是要素的实际边界,如果这个范围远大于澄迈县,你要么是用它做掩膜去裁剪 DEM 到局部区域,要么是把 SHP 里不需要的行政区要素删掉导出新文件。考虑到澄迈县本身的面积并不大,30 米分辨率下完全可以直接保留全幅 DEM,再用 SHP 做掩膜提取,避免反复裁剪造成边缘锯齿。
3. 用 SHP 裁剪 DEM:从 QGIS 界面到 GDAL 命令行
3.1 SHP 不是拿来“画范围”的,它是裁切模板
在处理 DEM 和 SHP 的组合时,最常见的需求是用行政区边界把 DEM 裁成研究区。有人在 ArcMap 里用Extract by Mask工具做这件事,但参数选项较少;也有人直接在 QGIS 里用Clip Raster by Mask Layer,这两个工具底层逻辑一致:SHP 的几何被转换成栅格掩膜,然后参与裁剪。我需要提醒的是,掩膜分辨率默认与 DEM 一致,但如果 SHP 边界很小且 DEM 像元很大,掩膜边缘会出现台阶状锯齿。解决办法是先对 SHP 做一点点缓冲(通常半个像元,即 15 米),再做裁剪。
在 QGIS 中的操作路径是:栅格→提取→按掩膜层裁剪栅格。参数设置如下:
- 输入图层:
dem.tif - 掩膜图层:
city_boundary.shp - 裁剪模式:
掩膜层(区别于扩展和像素) - 输出裁剪栅格:
chengmai_dem_clip.tif - 勾选
为输出栅格创建 VRT(可选,调试时用)
裁剪完成后,打开属性查看 NoData 值,QGIS 默认通常保留 DEM 原有的 NoData。如果裁剪后图像边缘出现黑色块,多半是 NoData 没有正确继承,需要在后续处理里手动指定。
3.2 更可控的 GDAL 命令行裁剪:一次跑完,参数全握在手里
QGIS 适合交互式操作,但在批量处理或需要复现的场景中,我倾向用gdalwarp。它裁剪栅格的同时可以做重投影、重采样和 NoData 设置,一个命令干完 ArcGIS 里两三个工具的活。对一个 30 米分辨率的县域 DEM 来说,以下命令足够用:
gdalwarp -cutline chengmai_data/city_boundary.shp -crop_to_cutline -dstnodata -9999 -of GTiff chengmai_dem/dem.tif chengmai_dem_clip.tif参数拆开看:
-cutline:指定矢量裁剪边界,支持 SHP、GeoJSON 等格式-crop_to_cutline:让输出范围恰好等于裁剪区的边界框,不加这个参数输出范围仍是原 DEM 的范围,只是多出来的部分变成 NoData-dstnodata -9999:设置输出栅格的 NoData 值,避免默认的 0 被误认为真实高程-of GTiff:输出格式为 GeoTIFF,保证地理信息不丢失
如果 SHP 和 DEM 坐标系不一致,gdalwarp会自动做重投影,但我在实际项目里不推荐依赖这种隐式行为。先手动统一坐标系是一种美德,因为你无法控制源数据里的参数是否精确。统一坐标系时可以这样做:
ogr2ogr -t_srs EPSG:32649 chengmai_data/city_boundary_utm49.shp chengmai_data/city_boundary.shp gdalwarp -t_srs EPSG:32649 -cutline chengmai_data/city_boundary_utm49.shp -crop_to_cutline -dstnodata -9999 -of GTiff chengmai_dem/dem.tif chengmai_dem_clip_utm49.tifogr2ogr用-t_srs转换坐标参考,输出新 SHP。gdalwarp中的-t_srs会把 DEM 重采样到目标坐标系,此时输出像素尺寸单位变成米,可以直接量算坡度坡向。
3.3 裁完检查像素尺寸和边界对齐:不做这一步等于白裁
裁剪成功的栅格并不代表能用。我在处理海南区域数据时发现一个常见问题:裁剪后的 DEM 边缘会有一圈高程值异常偏低的像元,这通常是 SHP 边界穿过了低质量的 SRTM 空洞,裁切后把空洞区的 0 值或 32767 值当作真实高程带进来了。因此裁剪后的下一步是检查最小值最大值:
gdalinfo -stats chengmai_dem_clip_utm49.tif查看Minimum和Maximum值,如果最小值是 -9999 或 0,而你确定研究区最高点比这高得多,说明 NoData 与有效高程值混合了。解决办法是重新执行裁剪并显式声明-srcnodata和-dstnodata:
gdalwarp -srcnodata -9999 -dstnodata -9999 -cutline chengmai_data/city_boundary_utm49.shp -crop_to_cutline -of GTiff chengmai_dem/dem.tif chengmai_dem_clip_clean.tif-srcnodata告诉 GDAL 输入里哪些值是无数据,-dstnodata则定义输出里的无数据值。两者分开设置比混用好理解,输入来自原始 DEM,输出是重采样后的结果,重采样过程会因为插值产生新的像元值组合,不显式指定就会出现边缘渐晕效应。
4. SHP 转 TXT、批量压缩和 zip 加密:数据交付的 3 个高频后续动作
4.1 从 DEM 或 SHP 里提取坐标到 TXT,写脚本比手动导出靠谱
很多人搜“shp转txt”,实际要的是把 SHP 里的点要素坐标导出成文本,或者把 DEM 采样成 XYZ 格式。前者在导入测量仪器或做简单可视化时常用,后者则多见于建模软件输入。把 SHP 的点坐标导出成 TXT 有现成工具,但直接用 GDAL 脚本更灵活:
from osgeo import ogr ds = ogr.Open("chengmai_data/points.shp") lyr = ds.GetLayer() with open("points_xy.txt", "w") as f: for feat in lyr: geom = feat.GetGeometryRef() if geom is not None: x, y, _ = geom.GetPoint() if geom.GetGeometryName() == "POINT" else (0, 0, 0) f.write(f"{x},{y}\n")如果你的 SHP 是面状或线状,GetPoint()会返回错误,这时需要用geom.GetGeometryRef(0).GetPoint()绕一层。这个逻辑很容易被忽略,因为实际传给你的 SHP 里可能既有面又有线,先打印geom.GetGeometryName()是安全的做法。
另一个更直接的工具是ogr2ogr,但很多人不知道它能写出 CSV/TXT 格式:
ogr2ogr -f "CSV" points_xy.csv chengmai_data/points.shp -lco GEOMETRY=AS_XYZ-lco GEOMETRY=AS_XYZ会把几何坐标转成 X、Y、Z 三列输出,-lco是 layer creation option 的缩写。若所有要素都是二维点,则 Z 列恒为 0。CSV 用文本编辑器打开另存为 TXT 就是纯文本格式,不需要额外转码。
4.2 单个 SHP 如何批量压缩:写循环比逐个右键压缩省一半时间
项目里如果有很多县域 SHP,逐个右键压缩费时且容易漏。用 zip 命令配合 shell 循环可以批量打包,还可以把每个 SHP 连同投影文件(.prj)和索引文件(.shx)一起打包,方便交付:
for shp in *.shp; do base="${shp%.shp}" zip "${base}.zip" "${base}.shp" "${base}.shx" "${base}.dbf" "${base}.prj" done"${shp%.shp}"是 shell 参数扩展,去掉扩展名后作为压缩包名。.dbf里存的是属性表,.prj里存的是投影信息,很多人只压缩.shp文件导致对方打不开属性。若原始数据没有.prj,后续在 GIS 软件里会被强制指定坐标系,这是数据交接时最常见的纠纷之一。
如果要对整个目录统一压缩且排除临时文件,可以用:
zip -r dem_shp_package.zip chengmai_data/ -x "*.tmp" -x "*~"-r递归压缩子目录,-x指定排除规则。这个命令适合交付前打包,但注意 zip 格式对中文文件名支持一般,文件名内部如果用中文,解压到部分 Linux 环境可能乱码。这种情况下我建议先重命名成拼音或英文再压缩。
4.3 zip 加密是交付保护,不是安全手段
相关热搜词里出现过“zip压缩包密码破解工具”和“zip密码移除”,这里必须把话讲清楚:zip 的加密本质是防君子不防小人,标准 ZipCrypto 加密很容易被已知明文攻击破解,AES-256 加密要安全得多。如果你要给客户或同事发一个带敏感边界数据的 zip,用 7-Zip 或 WinRAR 的 AES 加密比传统 zip 密码靠谱:
7z a -tzip -p"your_password" -mem=AES256 chengmai_dem_encrypted.zip chengmai_dem/-mem=AES256指定加密算法,-p设置密码。注意:普通zip命令行不支持 AES,Windows 自带的Compress-Archive也不支持。用7z才能输出 AES 加密的 zip。如果收到的是加密 zip 并且密码丢失,合法路径只有两条:一是问对方要密码,二是用字典攻击碰运气,效率取决于密码强度。网上流传的所谓“zip密码移除”对 AES 加密几乎无效,别指望。
4.4 遇到 “failed to copy spatial iop zip” 或 “invalid zip archive” 怎么办
这两个报错在 GIS 圈子里很常见。failed to copy spatial iop zip通常出现在 GDAL 安装不完整或环境变量冲突时,它和你的 DEM zip 无关,而是 GDAL 在初始化时找不到内部数据包。解决办法是重新安装对应版本的 GDAL,或者在 Windows 上使用 OSGeo4W 环境并确保GDAL_DATA指向正确的gdal-data目录。
invalid zip archive: could not find eocd则说明 zip 文件损坏或下载不完整。EOCD 是 End of Central Directory 记录,位于 zip 文件末尾,如果下载工具断了导致文件被截断,就会出现这个错。这时候不要再试解压,直接重新下载,或改用支持断点续传的下载工具。如果你需要从命令行验证 zip 完整性:
zip -T chengmai_dem.zip-T会逐个测试压缩包内的文件 CRC 校验值,输出OK才代表完整。这个命令对判断“文件能打开但解压报错”非常有效,值得在数据处理流水线里加一步。
5. DEM 和 SHP 坐标系不一致时,到底该信谁?一个校准流程跑通
5.1 优先以 DEM 原始坐标系为基准,还是以 SHP 投影为基准?
讨论坐标系一致性时,必须先明确 DEM 的用途。如果目标是做坡度坡向或水文分析,栅格像元的几何属性必须真实反映地面距离,这种情况下建议把数据投影到 UTM 或高斯-克吕格投影,得到单位为米的像元尺寸。如果只是做简单的可视化展示,地理坐标系也未尝不可。但澄迈县位于低纬度地区,经度 1 度对应的距离在 110 公里上下,纬度方向约 111 公里,如果直接用经纬度单位计算坡度,得出的数值是错的。
所以我的做法是:第一步,用ogrinfo查看 SHP 的坐标系;第二步,用gdalinfo查看 DEM 的坐标系;第三步,看两者 EPSG 是否一致。不一致时优先将 DEM 重投影到 CGCS2000 / 3-degree Gauss-Kruger zone 23(海南常用投影),大多数收集来的 SHP 也在这个坐标系里。命令如下:
gdalwarp -t_srs EPSG:4548 -r bilinear -of GTiff dem.tif dem_cgcs2000_3deg_23.tifEPSG:4548 是 CGCS2000 / 3-degree Gauss-Kruger zone 23,对应海南区域。-r bilinear是重采样方法,高程数据用双线性插值即可,不要用最邻近法(会丢失细节)。如果 DEM 里有少量空洞,可以考虑先填洞再用cubic重采样,但这是后话。
5.2 校准后的三层质检:边界、直方图和剖面线
重投影完成后,用gdalinfo -stats再看一遍 Min/Max 和均值,确认高程范围符合澄迈县实际地形。澄迈县北临琼州海峡,南部有丘陵山地,海拔从接近海平面到数百米。如果最小值是负几十甚至负几百,且位置不在海岸线附近,就要怀疑 NoData 混入或像元值有系统偏差。
第二层质检是把 DEM 加载到 QGIS,叠加 SHP 边界,检查边界线是否与地形起伏吻合。如果 SHP 边界穿过山谷但 DEM 边缘出现明显的矩形裁切痕迹,说明-crop_to_cutline没生效或输出范围不对。
第三层更定量:随机抽取 5 到 10 个已知高程点(比如道路交叉点、山顶),用gdallocationinfo查询 DEM 上的高程值,再与参考值比较:
echo "110.2046 19.7501" | gdallocationinfo -valonly dem_cgcs2000_3deg_23.tif-valonly只输出像元值,输入坐标格式为 “经度 纬度”,如果 DEM 已是投影坐标系,这里就要输入对应投影下的坐标。误差在 ±10 米内正常,30 米 DEM 的垂直精度本来就谈不上厘米级。
5.3 高程转等高线:野外踏勘前生成辅助文件
有了裁剪好的 DEM,生成等高线在野外验证时很实用。GDAL 有个配套工具gdal_contour,30 米分辨率 DEM 生成 10 米间隔的等高线会有大量锯齿,20 米或 25 米间隔更合理:
gdal_contour -a elev -i 25.0 dem_cgcs2000_3deg_23.tif contour_25m.shp-a elev给等高线属性字段取名为elev,-i 25.0设定等高距。生成后的 SHP 可以直接与原始 SHP 叠加检查高程分布合理性。若等高线在某个区域密集到完全粘连,说明该区域坡度很大,DEM 在此处的噪声会被放大,后续做坡度分级时需要考虑平滑处理。
6. 把 DEM 转成 3D Tiles:轻量预览和共享的实用技巧
处理完 DEM 裁剪、坐标统一和数据交付后,另一个高频需求是把 DEM 转成 Cesium 能直接加载的 3D Tiles,也就是相关热搜词里的“shp转3dtiles”和“dem转3dtiles”。用 Cesium 预览 DEM 比在桌面 GIS 里分享更轻,对方不需要安装任何软件,浏览器打开就能看地形和边界。
从 DEM 生成 3D Tiles 的常用工具是 Cesium ion 的在线上传,但它要注册,所以更可控的做法是用开源工具py3dtiles:
py3dtiles convert dem_cgcs2000_3deg_23.tif -o tiles_outputpy3dtiles convert会读取 DEM 栅格,按地形起伏生成瓦片金字塔,输出目录里包含tileset.json和分块文件。这里有一个重要参数:原始 DEM 如果像元尺寸是 30 米,直接转换会生成大量瓦片,建议先重采样到 60 米或 90 米再转换,预览效果差别不大,但文件体积和加载速度差距明显。重采样命令:
gdalwarp -tr 60 60 -r bilinear dem_cgcs2000_3deg_23.tif dem_60m.tif-tr 60 60是指定输出像元大小为 60 米。如果原始 DEM 已经是投影坐标系,-tr的单位是米;如果还是经纬度,-tr单位是度,需要自己换算。这一步顺带解决了网络加载性能问题。
如果你需要把 SHP 边界也叠加到 3D Tiles 场景里,可以用shp转 GeoJSON 再转 3D Tiles,或者直接在 Cesium 里加载 GeoJSON 图层,不需要做 3D Tiles。常见错误是用shp2tiles之类的工具把矢量压成 3D Tiles 时忽略了属性字段,结果场景里能看到边界但无法高亮或查询属性,分享价值大打折扣。
最后给一个验证 3D Tiles 是否成功的命令:用 Python 的json模块检查 tileset 结构:
python -c "import json; d=json.load(open('tiles_output/tileset.json')); print(d['root']['boundingVolume'].keys()); print(len(d['root']['children']))"如果输出里boundingVolume包含box或region,且 children 数量大于 0,说明瓦片生成基本正确。若报 KeyError,说明 tileset JSON 结构异常,通常是因为输入 DEM 范围太小导致瓦片金字塔层数不够,回到gdalinfo检查Size,如果宽高像素数少于 256,要么扩大范围,要么降低-tr值。
本文还有配套的精品资源,点击获取