Python GDAL实战:ASTER GDEM V003全国DEM高程数据处理(1123幅分幅合并→34省裁切,30m精度)

Python GDAL实战:ASTER GDEM V003全国DEM高程数据处理(1123幅分幅合并→34省裁切,30m精度)

摘要 :本文详细记录了基于 Python(rasterio + GDAL)对 ASTER GDEM V003 原始数据进行全国34省级行政区DEM裁切的完整工程实践。涵盖数据下载、分幅合并(mosaic)、矢量裁切(mask)、质量检验全流程,附完整可复现代码。数据覆盖N18°N53°、E73°E134°,成果34个GeoTIFF文件,总计10.61GB,124.8亿有效像素。

关键词:DEM高程数据、ASTER GDEM、Python rasterio、GDAL、遥感数据处理、GeoTIFF、空间数据裁切、数字高程模型

标签GIS 遥感 空间数据库 数据处理 地理信息系统


成果免费获取方式

数据由WX号 YouGIS顽石 整理分享,完全免费。两种获取方式任选其一:

方式一:关键词(推荐)

关注 YouGIS顽石 ,发送:DEM-省份简称 获取省份成果数据,DEM-分幅号 获取分幅数据

复制代码
DEM-{省份简称} → 获取省份裁切成果数据,如 DEM-河南
DEM-{分幅号}  → 获取原始分幅数据,如 DEM-N30E14

方式二:YouGIS 数据助手

平台 入口
小程序 搜索「YouGIS数据助手」
PC 端 https://yougis.com.cn/res/home

支持在线查看数据元信息(分辨率、坐标系、覆盖范围),适合批量检索多个数据集。

一、数据背景与选型

1.1 ASTER GDEM V003 vs V002 vs SRTM

对比项 SRTM C-band ASTER GDEM V002 ASTER GDEM V003
分辨率 30m 30m 30m
纬度覆盖 N60°~S56° N83°~S83° N83°~S83°
垂直精度(RMSE) ~16m 17m 8.5m
数据源年份 2000年2月 2000-2010 2000-2018
空洞密度 较多 大幅减少
中国高纬度覆盖 差(漠河N53°已覆盖,但更北缺失)

选型结论:全国DEM处理选 ASTER GDEM V003,兼顾覆盖范围(含高纬度)和精度。SRTM适合低纬度区域专项研究。

1.2 数据获取来源

来源 网址 特点
NASA EarthData https://earthdata.nasa.gov 原始来源,需注册
地理空间数据云 https://www.gscloud.cn 国内镜像,下载速度快
ASTER GDEM官网 https://asterweb.jpl.nasa.gov/gdem.asp 产品说明文档

二、原始数据技术规格

2.1 核心参数

text 复制代码
数据产品:ASTER GDEM V003
发布机构:NASA & METI
发布时间:2019年8月
分幅规则:1°×1° 经纬度网格
单幅像素:3601 × 3601
单幅大小:~41 MB
空间分辨率:0.000278°(赤道约30m)
数据类型:signed 16-bit integer (int16)
空间参考:WGS84 / EPSG:4326
NoData值:-9999
文件格式:GeoTIFF
命名规则:ASTGTMV003_N{纬度}E{经度}_dem.tif

2.2 全国覆盖统计

text 复制代码
分幅总数:1,123幅
覆盖范围:N18°~N53°, E73°~E134°
总数据量:~49 GB
单幅示例:ASTGTMV003_N39E116_dem.tif → 北京
         ASTGTMV003_N30E114_dem.tif → 武汉

2.3 分幅筛选------根据省份bbox确定所需图幅

python 复制代码
import numpy as np

def get_tile_range(min_lat, max_lat, min_lon, max_lon):
    """根据经纬度范围获取需要的ASTER GDEM分幅编号"""
    lats = range(int(np.floor(min_lat)), int(np.ceil(max_lat)) + 1)
    lons = range(int(np.floor(min_lon)), int(np.ceil(max_lon)) + 1)
    tiles = [f"N{lat}E{lon}" for lat in lats for lon in lons]
    return tiles

# 示例:河南省范围约 N31°~N36°, E110°~E117°
tiles = get_tile_range(31, 36, 110, 117)
print(f"河南需 {len(tiles)} 幅: {tiles}")
# 输出: 河南需 42 幅: ['N31E110', 'N31E111', ..., 'N36E117']

三、处理流程与核心代码

复制代码
┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│  数据下载  │ -> │  分幅合并  │ -> │  省界裁切  │ -> │  质量检验  │
│ 1123幅   │    │ 按省镶嵌  │    │ 矢量裁切  │    │  统计+核查 │
└──────────┘    └──────────┘    └──────────┘    └──────────┘
   ~49 GB          临时大文件       34个TIF       _summary.json

3.1 分幅合并(Mosaic)

python 复制代码
import rasterio
from rasterio.merge import merge
from rasterio.transform import array_bounds

def mosaic_tiles(tile_paths, output_path):
    """合并多个DEM分幅为单个TIFF(LZW压缩)"""
    src_files = [rasterio.open(p) for p in tile_paths]
    mosaic, transform = merge(src_files)

    profile = src_files[0].profile.copy()
    profile.update({
        'height': mosaic.shape[1],
        'width': mosaic.shape[2],
        'transform': transform,
        'compress': 'lzw',
        'tiled': True,        # 启用分块写入,提升读取性能
        'blockxsize': 256,
        'blockysize': 256,
    })

    with rasterio.open(output_path, 'w', **profile) as dst:
        dst.write(mosaic)

    for src in src_files:
        src.close()

    print(f"[OK] 合并完成: {output_path} ({mosaic.shape[2]}×{mosaic.shape[1]})")

3.2 省界裁切(Mask)

python 复制代码
import rasterio
from rasterio.mask import mask
import json

def clip_to_province(input_tif, geojson_path, output_tif, nodata=-9999):
    """用省级行政区划矢量(GeoJSON)裁切栅格"""
    with open(geojson_path) as f:
        geojson = json.load(f)

    # 处理 FeatureCollection / Feature / Geometry 等不同结构
    if geojson['type'] == 'FeatureCollection':
        shapes = [feat['geometry'] for feat in geojson['features']]
    elif geojson['type'] == 'Feature':
        shapes = [geojson['geometry']]
    else:
        shapes = [geojson]

    with rasterio.open(input_tif) as src:
        out_image, out_transform = mask(
            src, shapes, crop=True, nodata=nodata, all_touched=True
        )
        profile = src.profile.copy()
        profile.update({
            'height': out_image.shape[1],
            'width': out_image.shape[2],
            'transform': out_transform,
            'nodata': nodata,
            'compress': 'lzw',
            'tiled': True,
        })

        with rasterio.open(output_tif, 'w', **profile) as dst:
            dst.write(out_image)

    print(f"[OK] 裁切完成: {output_tif}")

3.3 质量检验

python 复制代码
import rasterio
import numpy as np
from pathlib import Path
import json

def validate_dem(tif_path):
    """验证DEM数据质量,返回统计字典"""
    with rasterio.open(str(tif_path)) as src:
        arr = src.read(1)
        valid = arr[arr != src.nodata]

        return {
            'code': Path(tif_path).stem,
            'width': src.width,
            'height': src.height,
            'crs': str(src.crs),
            'dtype': str(src.dtypes[0]),
            'total_pixels': int(arr.size),
            'valid_pixels': int(valid.size),
            'valid_ratio': round(valid.size / arr.size * 100, 2),
            'elev_min': int(valid.min()),
            'elev_max': int(valid.max()),
            'elev_mean': round(float(valid.mean()), 1),
            'size_mb': Path(tif_path).stat().st_size // (1024 * 1024),
        }

# 批量检验
results = {}
for tif in sorted(Path('a_output').glob('*.tif')):
    stats = validate_dem(tif)
    results[stats['code']] = stats
    print(f"{stats['code']}: {stats['valid_pixels']:>12,} px | "
          f"{stats['elev_min']:>5}~{stats['elev_max']:<5}m | "
          f"{stats['size_mb']}MB")

# 导出汇总JSON
with open('_summary.json', 'w') as f:
    json.dump(results, f, ensure_ascii=False, indent=2)

四、成果数据总览

4.1 整体统计

指标 数值
成果文件数 34个
总数据量 10,868 MB (10.61 GB)
总有效像素 12,476,309,771(约124.8亿)
有效像素占比 43.42%
高程范围 -275m ~ 8802m
数据类型 int16
坐标系 WGS84 (EPSG:4326)
NoData值 -9999
压缩方式 LZW

4.2 34省完整清单

编码 省份 图幅数 像素尺寸 有效像素 高程范围 大小(MB)
110000 北京 6 4008×2865 9,258,626 3~2296m 19
120000 天津 4 3605×3491 8,632,021 -5~1097m 7
130000 河北 39 10845×9470 65,857,002 0~2818m 199
140000 山西 27 9010×7195 44,388,292 148~3055m 195
150000 内蒙古 464 28848×19948 280,552,490 91~2840m 1230
210000 辽宁 31 10846×9001 62,045,913 -275~1337m 162
220000 吉林 43 12642×8930 65,444,891 0~2630m 214
230000 黑龙江 84 18054×13394 145,335,435 0~1671m 519
310000 上海 5 3962×2889 6,837,004 -10~352m 4
320000 江苏 30 9014×6900 38,332,587 -32~685m 61
330000 浙江 25 7937×7226 34,425,836 0~1920m 118
340000 安徽 27 9010×7019 42,437,091 -2~1869m 122
350000 福建 23 8292×8292 40,449,760 0~2160m 150
360000 江西 29 9369×7958 47,502,717 -1~2145m 177
370000 山东 40 10849×7407 45,702,055 -11~1532m 134
410000 河南 42 10489×8529 50,054,549 12~2411m 156
420000 湖北 40 12646×9014 62,375,665 -6~3097m 201
430000 湖南 35 10489×9728 56,001,688 19~2097m 235
440000 广东 32 10493×9366 55,021,568 0~1901m 185
450000 广西 48 12646×10090 65,193,980 -16~2109m 271
460000 海南 30 9370×6300 23,508,420 0~1839m 32
500000 重庆 21 7585×6845 36,514,212 26~2776m 106
510000 四川 70 16258×14022 125,472,821 169~7523m 658
520000 贵州 30 9369×8650 54,395,708 247~2896m 223
530000 云南 60 14479×11586 103,015,277 76~6710m 506
540000 西藏 151 28848×21612 260,749,872 122~8802m 1476
610000 陕西 40 11711×9013 65,016,912 166~3762m 266
620000 甘肃 187 20704×16057 172,464,233 615~5786m 507
630000 青海 97 19853×16625 205,670,309 1682~6751m 801
640000 宁夏 12 6127×5412 22,346,251 1090~3554m 56
650000 新疆 224 34260×23418 357,088,649 -154~8199m 1833
710000 台湾 30 6482×7938 19,908,780 0~3886m 44
810000 香港 2 1622×1801 1,582,840 0~958m 1
820000 澳门 1 1082×1441 762,668 -2~171m 0

五、工程注意事项

5.1 大文件内存管理

新疆成果文件 650000.tif 像素尺寸达 34260×23418(8亿+像素),直接全量读取会触发内存溢出。

python 复制代码
# ❌ 错误:全量读取大文件
with rasterio.open('650000.tif') as src:
    arr = src.read(1)  # 1.6GB内存!

# ✅ 正确:分块读取(windowed read)
with rasterio.open('650000.tif') as src:
    for window in src.block_windows(1):
        block = src.read(1, window=window[1])
        # 逐块处理...

5.2 坐标系一致性检查

裁切前务必确认矢量与栅格坐标系一致,否则裁切结果为空或错位。

python 复制代码
import rasterio
import geopandas as gpd

with rasterio.open('merged.tif') as src:
    raster_crs = src.crs.to_epsg()

gdf = gpd.read_file('province_boundary.geojson')
vector_crs = gdf.crs.to_epsg()

if raster_crs != vector_crs:
    print(f"[WARNING] 坐标系不一致! 栅格: EPSG:{raster_crs}, 矢量: EPSG:{vector_crs}")
    gdf = gdf.to_crs(raster_crs)  # 统一到栅格坐标系

5.3 噪声像素过滤

650000.tif(新疆)存在 104 个极端异常像素(-32287~32008m),使用前过滤:

python 复制代码
import rasterio
import numpy as np

with rasterio.open('650000.tif') as src:
    profile = src.profile.copy()
    arr = src.read(1).astype(np.float32)
    # 过滤物理不可能的高程值
    arr[(arr < -1000) | (arr > 9000)] = -9999

    with rasterio.open('650000_filtered.tif', 'w', **profile) as dst:
        dst.write(arr.astype(np.int16), 1)

5.4 投影转换注意事项

数据为经纬度坐标(EPSG:4326),进行以下分析前需投影:

分析类型 推荐投影 原因
面积/体积计算 Albers等积投影 保持面积不变
距离/坡度分析 UTM投影 局部区域变形小
流域提取 UTM投影 水流方向计算需平面坐标
python 复制代码
# 使用GDAL进行投影转换(命令行)
# gdalwarp -t_srs EPSG:32649 input.tif output_utm49n.tif

# Python方式
import subprocess
subprocess.run([
    'gdalwarp', '-t_srs', 'EPSG:32649',
    '-r', 'bilinear', '-of', 'GTiff',
    '-co', 'COMPRESS=LZW',
    'input.tif', 'output_utm49n.tif'
])

六、数据读取与可视化

python 复制代码
import rasterio
import numpy as np
import matplotlib.pyplot as plt

with rasterio.open('410000.tif') as src:
    dem = src.read(1)
    dem = np.where(dem == -9999, np.nan, dem).astype(np.float32)

    fig, ax = plt.subplots(figsize=(10, 8))
    im = ax.imshow(dem, cmap='terrain')
    plt.colorbar(im, label='高程 (m)', shrink=0.8)
    ax.set_title('河南省 DEM (ASTER GDEM V003)', fontsize=14)
    ax.set_xlabel('列号')
    ax.set_ylabel('行号')
    plt.tight_layout()
    plt.savefig('henan_dem.png', dpi=150)
    plt.show()

七、总结

阶段 内容 规模
原始数据 ASTER GDEM V003 1123幅,~49GB
处理 下载→合并→裁切→检验 rasterio + GDAL
成果 int16, EPSG:4326, LZW压缩 34省TIF,10.61GB

处理流程全代码开源、可复现,所有34个成果文件技术规格统一。


参考文献

  1. NASA/METI. ASTER GDEM Version 3. (2019). https://asterweb.jpl.nasa.gov/gdem.asp
  2. NASA EarthData Search. https://earthdata.nasa.gov
  3. 地理空间数据云. https://www.gscloud.cn
  4. rasterio Documentation. https://rasterio.readthedocs.io
  5. GDAL Documentation. https://gdal.org

如果觉得有用,点赞收藏关注一键三连,有问题评论区交流 ✌️

相关推荐
jinshw1 天前
自己实现GIS配图软件(一)
rust·开源·gis
RestCloud4 天前
ETL是什么?全域数据集成平台核心能力解析
数据仓库·etl·数据清洗·数据处理·etlcloud·数据集成工具
zhz52144 天前
GIS项目中空间参考转换与MBTiles偏移:问题成因、解法与避坑
python·vue·gis
xiao5kou4chang6kai44 天前
遥感技术在生态系统碳储量、碳收支、碳循环以及人为源排放反演等领域的技术
遥感·生态系统·碳排放·碳储量·碳估算
xiao5kou4chang6kai45 天前
GIS数据制备,空间分析与高级建模
arcgis·gis·空间数据
三维地图技术社区5 天前
GPS/北斗多设备实时定位接入系统架构设计与实践:人员+无人机+RID雷达+AIS船舶一图统览
gis·无人机·三维地图·北斗定位·实时定位
WebGIS开发6 天前
WebGIS开发进阶教程①:WebGL坐标系
gis·webgl·gis开发
前网易架构师-高司机6 天前
带标注的SDD-SAR遥感船舶识别数据集,识别率92.9%,7000张图,支持yolo,coco json,voc xml,文末有模型训练代码
yolo·json·数据集·遥感·sar·船舶·sdd
4154116 天前
MyBatis-Plus + PostGIS 实战(二):空间查询与空间索引
java·mybatis·gis·postgis