保姆级教程:用Python处理MODIS 11A2地表温度数据,从QC筛选到出图一步到位
Python自动化处理MODIS 11A2地表温度数据实战指南地表温度数据在气候研究、城市热岛效应分析等领域具有重要价值。MODIS 11A2作为NASA提供的8天合成地表温度产品因其全球覆盖和长期连续性备受研究者青睐。但对于非编程背景的用户来说从原始HDF文件到可分析的清洁数据往往需要跨越多个技术门槛。本文将用Python构建一套完整的自动化处理流程涵盖数据读取、质量筛选、空间转换到可视化输出全链条解决方案。1. 环境配置与数据准备处理MODIS数据需要特定的地理空间库支持。推荐使用conda创建独立环境以避免依赖冲突conda create -n modis python3.9 conda activate modis conda install -c conda-forge gdal numpy matplotlib pandas rasterio从LAADS DAAC下载的MYD11A2数据通常包含多个科学数据集(SDS)。关键文件结构如下HDF4_EOS:EOS_GRID:MYD11A2.A2021001.h25v05.006.2021005034651.hdf:MOD_Grid_Daily_1km_LST:LST_Day_1km HDF4_EOS:EOS_GRID:MYD11A2.A2021001.h25v05.006.2021005034651.hdf:MOD_Grid_Daily_1km_LST:QC_Day_1km建议建立如下目录结构管理数据project/ ├── raw_data/ # 存放原始HDF ├── processed/ # 输出处理结果 └── scripts/ # Python脚本2. 数据读取与质量解码使用GDAL读取HDF文件需要特别注意SDS路径格式。以下代码演示如何同时提取温度数据和QC层import gdal import numpy as np def read_modis_hdf(hdf_path): lst_ds gdal.Open(fHDF4_EOS:EOS_GRID:{hdf_path}:MOD_Grid_Daily_1km_LST:LST_Day_1km) qc_ds gdal.Open(fHDF4_EOS:EOS_GRID:{hdf_path}:MOD_Grid_Daily_1km_LST:QC_Day_1km) lst_array lst_ds.ReadAsArray() * 0.02 # 转换为摄氏度 qc_array qc_ds.ReadAsArray() return lst_array, qc_arrayQC字段的二进制解码是质量控制的核心。每个像元的QC值包含8个比特位关键位含义如下表所示比特位含义取值说明0-1数据质量标志00理想质量01-11递减质量2-3数据衍生状态00未处理01-11处理状态4-5发射率误差范围00≤0.01...11≥0.036-7地表温度误差范围00≤1K01≤2K10≤3K基于此我们可以构建不同严格程度的质量掩膜def generate_quality_mask(qc_array, max_error1): if max_error 1: return qc_array 63 # 00111111 elif max_error 2: return qc_array 127 # 01111111 else: return qc_array ! 0 # 仅排除无效值3. 高级质量控制策略基础的质量筛选可能仍会保留部分异常值。我们可以结合多维度条件创建更智能的过滤器def enhanced_quality_filter(lst_array, qc_array): # 创建复合条件掩膜 mask ( ((qc_array 0b00000011) ! 3) # 排除最差质量 ((qc_array 0b11000000) 0) # LST误差≤1K (lst_array -20) # 合理温度范围 (lst_array 60) # 避免极端值 ) return mask对于需要更高精度的应用可以单独处理每个像元的QC位def parse_qc_bits(qc_value): return { quality: qc_value 0b00000011, derived_state: (qc_value 0b00001100) 2, emissivity_err: (qc_value 0b00110000) 4, lst_err: (qc_value 0b11000000) 6 }4. 空间参考与可视化输出MODIS数据采用正弦投影(Sinusoidal)直接可视化可能导致变形。使用rasterio进行投影转换import rasterio from rasterio.warp import calculate_default_transform, reproject def reproject_to_wgs84(input_array, output_path): # 设置原始投影参数 src_crs PROJCS[unnamed,GEOGCS[Unknown datum based upon the custom spheroid,...]] with rasterio.open(output_path, w, driverGTiff, heightinput_array.shape[0], widthinput_array.shape[1], count1, dtypestr(input_array.dtype)) as dst: dst.write(input_array, 1) dst_crs EPSG:4326 with rasterio.open(output_path) as src: transform, width, height calculate_default_transform( src.crs, dst_crs, src.width, src.height, *src.bounds) kwargs src.meta.copy() kwargs.update({ crs: dst_crs, transform: transform, width: width, height: height }) with rasterio.open(output_path.replace(.tif, _wgs84.tif), w, **kwargs) as dst: reproject( sourcerasterio.band(src, 1), destinationrasterio.band(dst, 1), src_transformsrc.transform, src_crssrc.crs, dst_transformtransform, dst_crsdst_crs, resamplingrasterio.enums.Resampling.nearest)创建专业级温度分布图import matplotlib.pyplot as plt import cartopy.crs as ccrs def plot_lst_map(lst_array, extent, titleLST Distribution): fig plt.figure(figsize(12, 8)) ax fig.add_subplot(1, 1, 1, projectionccrs.PlateCarree()) img ax.imshow(lst_array, originupper, extentextent, cmapjet, vminnp.nanmin(lst_array), vmaxnp.nanmax(lst_array)) ax.coastlines() ax.gridlines(draw_labelsTrue) plt.colorbar(img, axax, labelTemperature (°C)) plt.title(title) plt.savefig(lst_distribution.png, dpi300, bbox_inchestight)5. 批处理与自动化流程对于长期序列分析我们需要构建批处理管道。以下代码实现自动日期识别与多文件处理from pathlib import Path import re def process_modis_batch(input_dir, output_dir): input_dir Path(input_dir) output_dir Path(output_dir) for hdf_file in input_dir.glob(MYD11A2*.hdf): # 从文件名提取日期 match re.search(rA(\d{7}), hdf_file.name) if not match: continue date_str match.group(1) print(fProcessing {date_str}...) # 主处理流程 lst_array, qc_array read_modis_hdf(str(hdf_file)) mask enhanced_quality_filter(lst_array, qc_array) clean_lst np.where(mask, lst_array, np.nan) # 保存结果 output_path output_dir / fLST_{date_str}.tif reproject_to_wgs84(clean_lst, str(output_path))添加异常处理确保流程稳定性try: lst_array, qc_array read_modis_hdf(hdf_path) except RuntimeError as e: print(fError reading {hdf_path}: {str(e)}) continue6. 实用技巧与性能优化处理大范围数据时内存管理至关重要。使用分块处理策略def process_by_chunks(hdf_path, chunk_size1000): ds gdal.Open(fHDF4_EOS:EOS_GRID:{hdf_path}:MOD_Grid_Daily_1km_LST:LST_Day_1km) xsize, ysize ds.RasterXSize, ds.RasterYSize for y in range(0, ysize, chunk_size): y_offset min(chunk_size, ysize - y) for x in range(0, xsize, chunk_size): x_offset min(chunk_size, xsize - x) chunk ds.ReadAsArray(x, y, x_offset, y_offset) # 处理分块数据...对于长期监测项目建议将处理结果存储为Zarr格式以便高效访问import zarr def save_as_zarr(lst_arrays, output_path): root zarr.open(output_path, modew) for i, arr in enumerate(lst_arrays): root.create_dataset(ftemp/{i}, dataarr, chunks(500, 500)) root.attrs[description] Processed MODIS LST data实际项目中温度数据的日变化分析往往比单一时相更有价值。我们可以扩展流程计算温度异常def calculate_anomaly(lst_stack, baseline_period(2000, 2010)): # lst_stack形状为(time, height, width) baseline_mask (lst_stack.time.dt.year baseline_period[0]) \ (lst_stack.time.dt.year baseline_period[1]) baseline_mean lst_stack[baseline_mask].mean(dimtime) return lst_stack - baseline_mean