简介:全国DEM数据TIF数据.zip提供覆盖全国范围的数字高程模型栅格数据,面向地理信息、遥感及相关地学研究人员与工程技术人员,可用于地形特征提取、流域划分、土地利用适宜性评价以及三维地形可视化等多种分析场景。压缩包内共有七个文件,核心数据为TIF格式的高程栅格,同时附带地理配准文件、投影参数定义、金字塔文件、属性表以及文本格式的补充说明,可辅助在ArcGIS、ENVI等软件中实现坐标加载、快速显示和基础查询。数据覆盖完整且带有空间参考信息,使用时需确认投影坐标系和分辨率是否匹配任务要求,必要时可进行填洼、裁剪和误差校正等预处理。整体压缩包约三十一兆字节,数据量适中,便于存储和交换。目前已有10778人学习浏览,对于需要全国尺度高程底图的区域分析、科研教学与项目开发而言,是一份实用且易用的基础数据。
1. 全国DEM数据TIF数据.zip:拆开之前先想清楚你要拿它干嘛
拿到一个“全国DEM数据TIF数据.zip”,第一反应通常是解压之后直接拖进GIS软件,然后等着图层出现。实际做过的都知道,这个操作翻车概率极高:要么打开一片黑,要么坐标跑到海里,要么坡度算出来几十度起步。问题不在数据本身,而在大多数公开DEM产品是按分幅组织的原始采样格网,并不是为“桌面直接出图”准备的,你需要先把它当成原材料,走一遍检查、拼接、重投影、裁剪的预处理链。
这个zip能解决的是全国尺度的地形底图问题:坡度、坡向、山体阴影、水文分析、选址通视、淹没模拟,凡是依赖高程栅格的生产任务都从它开始。适合两类人,一类是用GIS做分析但被数据组织方式搞烦的从业者,另一类是写自动化脚本处理栅格的开发工程师。后者如果直接拿一个分幅TIF就开跑,后面会被坐标系和NODATA折磨到怀疑人生。
先说结论:这份数据包本身不是拿来直接出图的,它是“半成品原料”。能不能用得顺,取决于你愿不愿意在动手前花十分钟搞清楚它的分幅规则、高程基准和无效值设置。这三件事搞明白,后面每一步都有后悔药可吃;搞不明白,每一步都是黑匣子。
2. 先读懂分幅TIF:分辨率、高程基准与坐标参考,决定后面每一步
全国尺度的DEM数据通常不会拼成单个大文件分发,而是按经纬度格网或标准图幅切成大量TIF分幅,每个分幅覆盖一个矩形区域。这带来一个直接后果:你看到的是一个几百个文件组成的zip,不是一个“图层”。在动手拼接之前,必须先回答三个问题:一个像元在地面上到底占多大面积、高程值相对于哪个基准面、每个分幅的坐标参考是什么。
这三个问题决定了后面每一步的操作参数。分辨率影响重采样设置,基准面影响高程比较,坐标参考影响投影转换。很多人拿到数据直接做坡度分析,算出来的结果离谱到没法看,根因就是没搞懂这三个属性之间的关系。
2.1 “30米分辨率”不是你想的那种均匀网格
所谓30米分辨率,在大多数公开DEM产品里指的是赤道附近的像元尺寸。实现方式通常是在经纬度方向上按固定角秒切分,比如1角秒约等于30米。这样在地球高纬度地区,南北方向还是30米,但东西方向的实际地面距离会明显小于30米,因为经线在向两极收敛。换句话说,同一份DEM,在低纬和高纬地区的地面采样密度并不一致。
这个特性直接影响到两件事。第一,如果你把经纬度坐标的DEM直接当成平面数据算面积、算坡度,高纬度地区会整体变形;第二,如果同一个项目要拼接不同纬度的分幅,你没有必要把每个分幅都重采样到同一个像元尺寸,重投影时会统一处理。另外,高程值本身还要区分基准:有些产品的高程是相对于椭球面的椭球高,有些是相对于大地水准面的正高,两者差几米到几十米不等。做淹没分析或者土方计算时,这几米可能就是致命的误差来源。判断依据一般藏在zip内的说明文件里,文件名里带版本号或者源数据标识的,要优先按随附元数据来解读。
2.2 用gdalinfo在30秒内识别分幅TIF的关键信息
不管后续用桌面GIS还是脚本处理,第一步都建议用GDAL自带的gdalinfo把分幅TIF的元数据打出来看看。它对GeoTIFF的读取是行业最稳的,不会像某些桌面软件那样自动做“看不见的修正”。
gdalinfo dem_30m_n28_e105.tif这是命令行下最朴素也最有用的动作。输出信息量很大,但真正要读的字段就下面几个,我做了个快速对照表。
| 字段 | 含义 | 判断要点 |
|---|---|---|
| Size is 1201, 1201 | 栅格宽度和高度(像元数) | 确认分幅行列数是否符合预期 |
| Origin = (104.9999999, 29.0001389) | 左上角坐标 | 和文件名里的经纬度对得上才对 |
| Pixel Size = (0.0002777778, -0.0002777778) | 每像元对应的经纬度跨度 | 转成米时要除以纬度余弦 |
| Coordinate System is | 坐标参考描述 | 缺这个说明坐标信息丢失,要警惕 |
| NoData Value = -9999 | 无效值标记 | 没有这行说明后面渲染几乎必黑屏 |
| Compression / Interleave | 压缩方式和波段存储 | 有LZW压缩说明分幅设计合理 |
| BIGTIFF | 是否启用大文件格式 | 没看到但你处理全国数据,预算好4GB上限 |
这些字段在拼接之前就值得逐一过一遍。尤其是NoData Value,如果没有输出,后面所有分幅拼到一起后,无效区域会被当成真实高程参与计算,渲染和坡度结果都会出现大面积异常。遇到缺NoData的TIF,用gdal_translate补一个再进流程。
2.3 压缩、分块与BIGTIFF:加载性能的三个隐藏因素
TIF后缀只说明容器格式,不说明内部组织方式。同样是TIF,有的加载飞快,有的拖动一下转半分钟,差别就在压缩、分块和文件大小上限三个设置上。
LZW或DEFLATE压缩的TIF体积更小,读取时GDAL会解压,CPU开销略高,但磁盘IO压力大幅降低。分幅DEM一般用LZW就够,DEFLATE的压缩率更高但解压更慢。TILED=YES意味着数据按小块(如256×256)组织存储,而不是按行扫描,这对切片服务和局部窗口浏览非常友好,全国范围的大栅格更应该开。至于BIGTIFF,是因为经典TIFF格式单个文件超过4GB就无法写入,全国DEM拼接后动辄几十GB,如果你直接gdal_translate出去而不开BIGTIFF=YES,文件会写到一半报错,或者被迫拆成多个文件。预处理阶段就把这三个参数想清楚,后面能省掉大量返工。
3. 把全国分幅DEM TIF整理成可用的底图:解压检查、虚拟拼接与重投影
知道了分幅TIF的底细,接下来就是预处理。我一般会遵循一套固定顺序:先解压做完整度检查,再建立虚拟拼接,然后一次性做重投影和重采样,最后才根据分析目标去裁剪和派生地形因子。
这个顺序不是随便定的。直接对几百个分幅跑gdal_merge.py,不仅慢,而且内存经常撑不住;直接在每个分幅上分别做投影转换又会产生海量中间文件。正确做法是先用gdalbuildvrt“假装拼好”,再用gdalwarp一次性物化,这样只产生一份大TIF,效率最高,也最容易排查问题。
3.1 解压后先做三件事:检查文件命名、配套文件与NODATA
解压不是双击完事。打开目录后马上确认三件事:分幅文件的命名规律、有没有配套的坐标文件、总数据量是否和zip标注一致。这些信息决定你后面能不能用通配符批量处理。
ls -lh dem/ du -sh dem/ls看一眼命名,du看一眼总量。命名规律很关键,常见的如dem_30m_n28_e105.tif,其中n28代表北纬28度,e105代表东经105度,这种命名可以直接推算出分幅覆盖范围,也可以用来判断数据是否存在空洞。配套文件方面,如果每个TIF旁边有.tfw、.prj或者同名xml,说明坐标信息齐全;如果只有一个裸TIF,说明坐标信息可能内嵌在GeoTIFF头里,也可能已经丢失,需要用gdalinfo逐一确认。
我见过最坑的情况是:zip里几百个文件,解压后有一半分幅的.tif文件缺失,但没有任何报错提示,直到拼接完成后才发现北部区域少了一大块。所以进入下一步之前,最好用文件名正则把该有的分幅清单列出来,和实际文件做一次差集。这步不复杂,但能省下后面排查位置错漏的大把时间。
3.2 用gdalbuildvrt做虚拟拼接,避免一次合并全国数据
分幅数量多的时候,直接合并不仅慢,而且一旦参数不对就要重新跑一遍。虚拟拼接(VRT)是更好的中间状态:它不复制任何像元数据,只是记录“哪些TIF、按什么规则叠在一起”的XML描述。加载时GDAL会按需去读原始文件,速度取决于原始文件的读取效率。
gdalbuildvrt -o national_dem.vrt dem_*.tif这条命令把dem目录下所有TIF按空间位置拼进一个VRT。-o指定输出文件名,dem_*.tif是shell通配符,会展开成所有分幅。如果分幅之间有重叠,VRT默认按顺序取第一个文件的数据,所以d_e排序要留意——建议命名规律能保证按行列顺序排列。遇到个别分幅坐标缺失时,VRT会失败并提示找不到坐标信息,这就是前面检查配套文件的价值所在。
VRT还有一个好处是方便做临时验证:你可以直接加载VRT进QGIS看全局渲染效果,不需要等物化完成。发现问题时删掉VRT重做即可,根本不伤原始分幅。
3.3 坐标与投影重采样:gdalwarp的五个必设参数
VRT本身不解决坐标系问题。全国DEM原始数据通常是WGS84经纬度坐标,而地形分析需要平面坐标下的距离和面积才有意义。这一步用gdalwarp把整个VRT重投影到目标投影,同时完成像元重采样,直接输出一份正式TIF。
gdalwarp -t_srs EPSG:32650 -tr 30 30 -r bilinear -dstnodata -9999 \ -co COMPRESS=DEFLATE -co TILED=YES -co BIGTIFF=IF_SAFER \ national_dem.vrt national_dem_utm.tif五个关键参数拆开来看。-t_srs指定目标坐标系,EPSG:32650是UTM 50N投影,适用于东经117度到123度之间的区域;你在其他区域要换成对应的分带。-tr 30 30表示重采样后的像元尺寸为水平方向和垂直方向各30米,重投影后像元不再以度为单位。-r bilinear选择双线性插值,地形数据用bilinear比nearest平滑,比cubic快,是兼顾效率和精度的选择。-dstnodata -9999把无值区域统一写成-9999,防止不同分幅各自为政。最后的-co参数控制输出格式,DEFLATE压缩、TILED分块、BIGTIFF按需开启,这三件套在大范围栅格里基本属于标配。
有一点值得反复强调:分析用的投影别选Web墨卡托(EPSG:3857)。墨卡托在高纬度会把面积拉大好几倍,坡度、面积、通视分析全部失真。做省级或更小尺度分析时,用高斯-克吕格或UTM分带投影都行,具体分带要看项目区经度范围。这个选择比任何花哨算法都重要。
3.4 什么时候该把VRT“物化”成正式TIF
有人会在VRT上直接做后续的裁剪和坡度计算,这当然可以,但性能很差。VRT每次读取都要临时去解析原始文件的位置和格式,反复分析时等于把IO压力放大好几倍。
我一般会在重投影完成后就把结果物化成正式TIF,也就是上面那条gdalwarp命令输出的national_dem_utm.tif。之后的所有裁剪、坡度、填洼操作都基于这份文件。这样做的另一个好处是:这份TIF已经是统一坐标、统一分辨率、统一NODATA的“干净底图”,分发给别人或做缓存都用它,不会因为原始分幅存在差异而产生理解偏差。
4. 全国DEM栅格生产流程:正式拼接、按行政区裁剪与地形因子计算
底图准备好之后,进入生产阶段。这个阶段的任务很明确:把全国底图变成真正用于分析的成果——拼接好的全域栅格、裁剪到工作区的DEM、以及坡度、山体阴影、填洼后的水文修正栅格。
每一步都有对应的GDAL工具或轻量Python库,参数不复杂,但值得逐个确认,因为地形成果不像普通遥感影像“看得见就算对”,它需要数值尺度和空间范围都正确。
4.1 用gdal_merge.py做正式拼接:BIGTIFF与Float32
VRT是虚拟拼接,物化后的正式拼接用gdal_merge.py最省事。它的逻辑是把多个输入栅格按空间位置合并到一块画布上,重叠区默认取第一个文件的值。如果你的分幅之间严格无重叠,这个工具几乎零成本。
gdal_merge.py -o dem_merge.tif -ot Float32 -a_nodata -9999 \ -co BIGTIFF=YES -co TILED=YES -co COMPRESS=DEFLATE \ national_dem_utm.tif-ot Float32把输出数据类型强制为32位浮点,确保高程精度不丢失,尤其是后续要做浮点运算的坡度或填洼。-a_nodata统一无效值。这里注意:如果输入文件已经做过gdalwarp的统一NODATA设置,-a_nodata可以省略,但写上更保险。输出的-co和前面一样用三件套,全国范围的数据量非常大,BIGTIFF=YES是必不可少的,否则超过4GB直接报错。
4.2 用gdalwarp按行政区裁剪:cutline的坐标一致性
把全国底图裁剪到项目区,属于高频需求。最稳妥的做法不是“按范围框裁”,而是“按矢量边界裁”,避免把边界外的高程带进来。
gdalwarp -cutline county.shp -crop_to_cutline -of GTiff \ dem_merge.tif county_dem.tif-cutline指定行政区矢量文件,-crop_to_cutline让输出栅格的范围完全贴合矢量边界,而不是保留矩形外框。这两个参数组合起来就等于“无锯齿裁剪”。一个常见的坑是:矢量边界坐标系与栅格不一致时,GDAL会做动态转换,但转换精度取决于矢量本身的精度。建议在裁剪前先确认两者的坐标描述,若一个WGS84一个国测局坐标,先统一再裁,不要指望gdalwarp自动收拾残局。
边界处的高程值有时会出现异常条纹,尤其是沿切割线的像元。可以在裁剪后加一道腐蚀处理,把边界外一圈NODATA写入。做法不复杂:用gdalwarp的-dstnodata再跑一次,但这一步我通常留到坡度计算之后再做,避免过早丢边。
4.3 地形因子计算:slope、hillshade与参数选择
有了裁剪后的DEM,坡度、坡向、山体阴影这类派生栅格就是一条命令的事。gdaldem是专门干这个的工具,参数不复杂,但容易因忽视比例因子而翻车。
gdaldem slope county_dem.tif county_slope.tif -s 1 -alg ZevenbergenThorne gdaldem hillshade county_dem.tif county_hillshade.tif -z 2 -az 315 -alt 45gdaldem slope里,-s 1表示高程单位与水平单位都是米,如果高程是英尺,这里要填0.3048。-alg ZevenbergenThorne是可选算法,适合相对平滑的自然地形,默认的Horn算法在陡峭区域更稳健,但没有绝对优劣。坡度输出以度为单位,范围0到90。gdaldem hillshade的-z 2是垂直夸张系数,山地地形用1或2都行;-az 315是光源方位角,315度对应西北光;-alt 45是光源高度角。这套参数出来的渲染效果比较接近传统纸质地貌图的立体感。
坡度计算前一定要确认输入栅格已经是平面投影坐标,否则水平单位是度,高程单位是米,算出来的坡度值每个都像“悬崖”。这个坑在后面章节会展开讲,但这里先记住:投影先行,坡度后算。
4.4 水文分析前的填洼:GDAL不提供,用pysheds补上
水文分析是DEM最重要的应用方向之一,但它有一个前置动作是GDAL本身没提供的:填洼。真实地表有很多凹陷区域,如果不处理,水流会在这些地方积聚而无法继续向下游流动,流向网络和汇流累积量都会失真。所以做水文分析前,必须先把DEM中的洼地填平。
常见做法是用开源库pysheds,它在处理流向、填洼、汇流累积上比桌面GIS更轻量,也更容易嵌入脚本流程。
from pysheds.grid import Grid grid = Grid.from_raster('county_dem.tif') dem = grid.read_raster('county_dem.tif') filled = grid.fill_depressions(dem)这段代码先用Grid.from_raster读取DEM文件完成网格初始化,然后read_raster把高程数组载入,最后fill_depressions返回填洼后的数组。填洼结果的数组可以直接传给D8流向计算,也可以另存为TIF,看后续需求。pysheds的fill_depressions默认会把所有内部洼地填到最低出水口高度,参数很少,基本不用调。
注意:填洼只用于水文分析,不要为了做坡度或渲染去做填洼,它会抹掉真实的地形凹陷信息,让坡度图和山体阴影变得失真。
5. DEM数据落地五个常见坑:现象、原因与解决办法
从拿到zip到跑出地形因子,这中间每一步都有对应的坑。我按踩坑频率找几个最高发的列出来,每条都先说现象,再给原因,最后是解决办法。这一章可以直接当排查手册用。
| 现象 | 根因 | 一句话解决 |
|---|---|---|
| TIF拖入GIS一片黑 | NoData被当成有效值参与渲染 | 检查并重写NoData |
| 坡度值高得离谱 | 经纬度坐标直接算坡度 | 先重投影再加比例因子 |
| 水文线在湖面断开 | 水体区域高程是空洞 | 填洼或先补洞 |
| 拼接时内存溢出一夜白跑 | 输出超4GB且未开BIGTIFF | 先buildvrt再warp |
| 拼好后有明显接缝台阶 | 分幅间高程基准不一致 | 检查数据源,统一基准后重拼 |
5.1 打开一片黑:NODATA没被识别
现象:分幅TIF在GIS里加载后,图层区域大面积是全黑或全白,拉伸符号化时,影像看起来像一张噪点图。很多人这时候会认为是文件坏了,重新去解压一份,结果一样。
原因:TIF的NoData值没有被GIS读取到。常见的NoData值是-9999或-32768,如果这个标签丢失,软件会把无效区域的像元当成正常的高程值参与统计与拉伸,整个色带被无效值霸占。尤其是高程类型为Int16时,-9999看起来更像真实值,黑屏概率更高。
解决:用gdal_translate重写一次NoData标签,具体做法是gdal_translate -a_nodata -9999 -co COMPRESS=DEFLATE input.tif output.tif。如果你的数据源本身没有NoData概念,却用0表示海洋或空洞,那就把0也设成NoData,但前提是你确认0不是真实地形。
5.2 坡度值离谱:直接在经纬度坐标上算slope
现象:从同一份TIF跑出来的slope,绝大多数像元值直接在50度以上,甚至接近90度,整张坡度图全是红色。换一个分幅还是这样,怎么看都不对。
原因:输入栅格是经纬度坐标,水平单位是度,高程单位是米,两者在数值上差了约10万倍。gdaldem按单位一致处理,自然把每个像元的高差当成在“度”的水平尺度上变化,坡度值必然离谱。
解决:回到第3章,先用gdalwarp重投影到平面坐标,再跑坡度。如果因为某种原因必须保留经纬度坐标,gdaldem提供-p参数来标记地理坐标系,同时需要配-s设置合理的尺度因子,但不同GDAL版本对这个参数的默认行为有差异,我一般直接重投影,不跟它赌。
5.3 河流断流:水体空洞让水文分析失效
现象:水文分析生成的河流网络,经过某个大湖或水库区域时断成几截,或者河流在某片平坦区域突然消失,整个流域边界也是碎的。
原因:水体在原始DEM里往往没有有效高程值,要么是NODATA,要么是填充的固定值。这些区域在水文分析时相当于“无数据区”,水流经过就断了,无法继续向下游累积。
解决:做水文分析前先做填洼和补洞。填洼用第4章的pysheds代码即可,但如果空洞区域过大,比如一大片水面占了几千个像元,填洼可能不够,需要用邻域插值或外部水体边界数据把水面高程补上,再把水面区域的流向设置为强制排水。检查方法是看TIF的NODATA占比,超过5%就该重视。
5.4 内存溢出:一上来就merge全国分幅
现象:用gdal_merge.py直接拼几百个分幅TIF,跑了两三个小时,最后抛出一个“iomem”或“gdal_merge.py: out of memory”之类的错误,前面时间全白费。
原因:gdal_merge.py在处理大量输入时,会在内存里同时维护多个输入文件的数据块窗口。分幅多、窗口尺寸大时,内存占用量直线上升;再加上输出文件超过4GB时没有BIGTIFF支持,写入直接失败。
解决:先gdalbuildvrt建立虚拟拼接,再用gdalwarp带-co BIGTIFF=YES -co TILED=YES输出正式TIF。这个组合会让GDAL按块读写,内存占用可控。如果单次输出仍然太大,可以按纬度带分批warp再合并,但一般情况下buildvrt+warp已经足够。
5.5 拼接处接缝断层:分幅间高程基准或时相不一致
现象:拼好的DEM在山体阴影图里能看到明显的条带,拼接处一边高一边低,形成台阶状纹理。放大后看,接缝两侧的高程值有系统性的几米到几十米差异。
原因:分幅数据来自不同批次,甚至不同源数据。高程基准、像元中心对齐、时相带来的地表变化,都会在接缝处产生不连续。DEM是浮点栅格,这种差异不会像影像拼接那样因为羽化而自动消隐。
解决:先把每个分幅的高程统计信息打印出来,看接缝两侧的均值差异。如果差异是固定值,用gdal_calc.py做差值修正再拼接;如果差异没有规律,说明是不同源数据,别硬拼,回到数据源层面确定处理方案。接缝处坚持用nearest重采样可以减少过渡带的异常值插值,但根本解法永远是保证源数据一致。
6. 把预处理变成流水线:一个DEM质量体检脚本,拿去改改就能用
处理到这一步,相信你已经意识到:整个DEM预处理链中,最花时间的不是跑命令,而是检查每一批分幅数据是否货真价实。第2章我教你用gdalinfo人工看一个文件,但到了全国数据几百个分幅的场景,人工根本看不过来。我自己现在接到新的DEM数据包,第一件事就是跑一个批量体检脚本,把每个分幅的关键指标打出来,几秒钟就能判断这份数据能不能用。
from pathlib import Path import rasterio import numpy as np for tif in sorted(Path('dem').glob('*.tif')): with rasterio.open(tif) as src: data = src.read(1) nodata = src.nodata if nodata is not None: nodata_mask = data == nodata else: nodata_mask = np.isnan(data) valid = data[~nodata_mask] print( tif.name, f"size={src.width}x{src.height}", f"res={src.res[0]:.2f}", f"nodata={nodata_mask.mean()*100:.2f}%", f"min={valid.min():.0f}", f"max={valid.max():.0f}", f"crs={src.crs}", )这段脚本遍历dem目录下所有TIF,逐个读取波段数据,统计分辨率、NODATA占比、有效值最小最大和坐标参考。判定规则很简单:NODATA占比超过5%的分幅要重点检查;高程min低于-500或者max超过9000,基本可以判断是异常值或坐标错乱;res出现两种以上数值,说明分幅间分辨率不统一;crs为None的分幅直接标红。
脚本里src.read(1)会把整个分幅读入内存,对几百MB的大分幅会有IO压力。如果文件特别大,可以改成只读取元数据和采样统计,但当前DEM分幅一般几十MB,直接读没压力。跑完一遍输出后,把异常项揪出来先处理,再走后续的buildvrt和warp,整个流程的返工率能降一半以上。
我自己早年处理第一批全国DEM时就没做体检,直接buildvrt后跑重投影,最后出来的底图在某个省份区域出现了整片空洞,排查了很久发现是一个分幅的NoData值和其他的不一样。从那以后,任何DEM数据拿过来都先让脚本过一遍再做拼接。体检不花多少时间,但能避免你在第5章那些坑里反复横跳。希望帮到你。
本文还有配套的精品资源,点击获取