用Python和rioxarray搞定MODIS数据:从下载到可视化,手把手教你分析科罗拉多州山火前后变化
·
用Python和rioxarray搞定MODIS数据:从下载到可视化,手把手教你分析科罗拉多州山火前后变化
当科罗拉多州的冷泉山火在2016年肆虐时,卫星遥感数据成为科学家评估灾害影响的重要工具。MODIS作为全球覆盖最频繁的中分辨率卫星传感器,其地表反射率产品(MOD09GA)为植被变化分析提供了关键数据支撑。本文将带您用Python生态中的 rioxarray 和 earthpy 工具包,完成从数据获取到灾害评估的全流程实战,特别适合GIS分析师和环境科学研究者需要快速掌握遥感处理核心技能的场景。
1. 环境配置与数据准备
1.1 搭建Python遥感分析环境
推荐使用conda创建专属的遥感分析环境,避免库版本冲突:
conda create -n modis_analysis python=3.8
conda activate modis_analysis
conda install -c conda-forge rioxarray earthpy matplotlib geopandas
关键库功能说明:
| 库名称 | 核心功能 | 版本要求 |
|---|---|---|
| rioxarray | 地理栅格数据处理 | ≥0.11.0 |
| earthpy | 遥感专用工具集 | ≥0.9.4 |
| geopandas | 矢量数据处理 | ≥0.10.0 |
1.2 获取冷泉山火数据集
EarthPy库内置了本次分析的示范数据集,通过以下代码自动下载:
import earthpy as et
# 下载科罗拉多州冷泉山火数据集
data_path = et.data.get_data('cold-springs-fire')
print(f"数据下载到:{data_path}")
数据集包含:
- MODIS地表反射率数据(火灾前后各一期)
- 火灾边界矢量文件(GeoMAC格式)
- 地形辅助数据
提示:首次运行会自动从Figshare下载约850MB数据,建议保持网络畅通
2. MODIS数据预处理实战
2.1 理解MOD09GA数据特性
MODIS地表反射率产品采用HDF-EOS格式存储,但示范数据已转换为GeoTIFF。关键参数需要特别注意:
import numpy as np
# MOD09GA产品关键元数据
modis_metadata = {
"scale_factor": 0.0001, # 缩放因子
"fill_value": -28672, # 无效值标记
"valid_range": (-100, 16000), # 有效值范围
"bands": {
1: "Red (620-670nm)",
2: "NIR1 (841-876nm)",
4: "Green (545-565nm)",
6: "SWIR1 (1628-1652nm)"
}
}
2.2 数据加载与质量控制
使用rioxarray进行智能加载,自动处理无效值和坐标系统:
import rioxarray as rxr
from glob import glob
import os
# 加载火灾前MODIS数据
modis_pre_path = os.path.join(data_path, "modis/reflectance/07_july_2016/crop/*.tif")
modis_pre = rxr.open_rasterio(glob(modis_pre_path)[0], masked=True)
# 查看数据属性
print(f"空间分辨率: {modis_pre.rio.resolution()}米")
print(f"坐标系: {modis_pre.rio.crs}")
print(f"无效值: {modis_pre.rio.nodata}")
2.3 空间裁剪与值域转换
针对研究区域进行空间裁剪,并应用缩放因子:
import geopandas as gpd
from shapely.geometry import box
# 加载火灾边界
fire_boundary = gpd.read_file(
os.path.join(data_path, "vector_layers/fire-boundary-geomac/co_cold_springs_20160711_2200_dd83.shp")
)
# 坐标系统统一
fire_boundary = fire_boundary.to_crs(modis_pre.rio.crs)
# 创建裁剪范围
clip_box = [box(*fire_boundary.total_bounds)]
# 执行裁剪并缩放
modis_clipped = modis_pre.rio.clip(clip_box) * modis_metadata["scale_factor"]
3. 植被指数计算与分析
3.1 归一化燃烧指数(NBR)原理
NBR通过近红外(NIR)和短波红外(SWIR)波段计算,公式为:
$$ NBR = \frac{NIR - SWIR}{NIR + SWIR} $$
在MODIS中对应波段:
- NIR: 波段2 (841-876nm)
- SWIR: 波段6 (1628-1652nm)
3.2 Python实现NBR计算
def calculate_nbr(dataset, nir_band=1, swir_band=5):
"""计算归一化燃烧指数"""
nir = dataset[nir_band]
swir = dataset[swir_band]
return (nir - swir) / (nir + swir)
# 计算火灾前后NBR
nbr_pre = calculate_nbr(modis_clipped)
3.3 结果可视化对比
使用matplotlib创建专业级专题图:
import matplotlib.pyplot as plt
import earthpy.plot as ep
# 创建对比子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
# 火灾前NBR
ep.plot_bands(
nbr_pre,
ax=ax1,
cmap="RdYlGn",
title="火灾前NBR (2016-07)",
vmin=-1, vmax=1
)
# 添加图例和比例尺
fire_boundary.boundary.plot(ax=ax1, color='black', linewidth=1)
ax1.annotate('植被健康区', xy=(0.1, 0.9), xycoords='axes fraction', color='darkgreen')
4. 变化检测与灾害评估
4.1 差异NBR(dNBR)计算
# 加载火灾后数据并计算NBR
modis_post_path = os.path.join(data_path, "modis/reflectance/09_sept_2016/crop/*.tif")
modis_post = rxr.open_rasterio(glob(modis_post_path)[0], masked=True)
nbr_post = calculate_nbr(modis_post.rio.clip(clip_box) * 0.0001)
# 计算dNBR
dnbr = nbr_pre - nbr_post
4.2 烧伤严重度分级
根据USGS标准划分烧伤等级:
| dNBR范围 | 烧伤程度 | RGB颜色值 |
|---|---|---|
| >0.66 | 高重度烧伤 | #FF0000 |
| 0.44-0.66 | 中度烧伤 | #FFA500 |
| 0.27-0.44 | 低度烧伤 | #FFFF00 |
| <0.27 | 未烧伤/恢复区 | #00FF00 |
实现分级可视化:
from matplotlib.colors import ListedColormap
# 自定义颜色映射
burn_colors = ['#00FF00', '#FFFF00', '#FFA500', '#FF0000']
burn_cmap = ListedColormap(burn_colors)
# 创建分类边界
bounds = [0, 0.27, 0.44, 0.66, 1]
ep.plot_bands(
dnbr,
cmap=burn_cmap,
title="冷泉山火烧伤严重度分级",
cbar=False
)
# 添加自定义图例
legend_labels = {
'未烧伤/恢复区': '#00FF00',
'低度烧伤': '#FFFF00',
'中度烧伤': '#FFA500',
'高重度烧伤': '#FF0000'
}
patches = [plt.plot([],[], marker="s", ms=10, ls="", color=color,
label=label)[0] for label, color in legend_labels.items()]
plt.legend(handles=patches, bbox_to_anchor=(1.05, 1))
4.3 统计烧伤面积
# 创建掩膜分类
burn_classes = np.digitize(dnbr.values, bounds)
# 计算各类像素数量
class_counts = np.bincount(burn_classes.flatten())
# 转换为面积(假设500m分辨率)
pixel_area = 500 * 500 # 平方米
burn_areas = class_counts * pixel_area / 10000 # 转换为公顷
print(f"高重度烧伤面积: {burn_areas[3]:.1f} 公顷")
print(f"总受影响面积: {sum(burn_areas[1:]):.1f} 公顷")
在完成整个分析流程后,建议将关键结果保存为GeoTIFF以便后续使用:
# 保存dNBR结果
dnbr.rio.to_raster("cold_springs_dnbr.tif", dtype="float32")
# 保存分类结果
rxr.DataArray(burn_classes).rio.to_raster("burn_severity_classes.tif")
通过这个完整案例,我们不仅掌握了MODIS数据的处理技巧,更建立起了一套可复用的山火影响评估方法。在实际项目中,可以进一步结合气象数据、地形因素等进行多维分析,为生态恢复决策提供更全面的数据支持。
更多推荐


所有评论(0)