Advertisement

【数据集】2000-2020年中国4 km日尺度气象数据集 (CDMet)

阅读量:

目录

  • 数据概述

    • 数据集的技术特点
  • 代码获取-GITHUB

    • 数据获取

    • 数据处理

      • 导入并进行可视化处理(提供完整的Python源代码)
    • 批量裁剪处理后保存为.tif文件(以2020年份的数据为例)

    • 参考

在研究气候变化的过程中,具有高空间分辨率的多变量格点气象数据扮演着核心角色.本文将阐述中国CDMet项目(覆盖2000年至2020年)的日尺度4公里分辨率气象数据集的情况,并提供下载途径.

数据概述

该研究基于J2024-A这一标识符,构建了一个覆盖中国自20世纪初至二十一世纪初期间每日间隔、每平方公里网格划分的气象数据集

本研究开发了 自2000年代以来至2020年代末期中国大陆每隔4公里的空间分辨率气象数据集(中国每日气象数据集 (CDMet)

空间分辨率:
数据集的空间分辨率为 2.5弧分(约 4公里),覆盖中国大陆地区。

时间范围:
数据集包含 2000年1月1日至2020年12月31日的逐日气象数据。

数据来源: 本研究采用了中国 699个气象站 的观测资料,并综合运用了六个影响因素(包括地理位置信息、地形地貌特征以及再分析数据等多个方面)进行建模分析。

请添加图片描述

该数据集包括九个气象变量:

  • 二度气象要素中的最高温度为maxtmp
  • 二度气象要素中的最低温度为mintmp
  • 二度气象要素中的平均温度为meantmp
  • 总降水量指标为pre
  • 地表表面温度变化情况为gstskin temperature
  • 10米高度处的风力强度为win
  • 相对湿度数值为rhu
  • 地面大气压力值为prs
  • 日辐射时长指标为ssd

数据集的技术特点

1、插值方法:
该研究采用薄板样条法与随机森林模型对观测数据实施插值,在考虑六种协变量(包括地形特征及再分析资料)的基础上构建空间–时间动态预测模型。该方法显著提升了数据的空间分辨率与时间分辨率水平,并确保了预测精度。

2、数据验证: 数据集基于独立采集的气象观测数据经过验证过程表明,在中国区域显示出较高的可靠性和准确性。

在这里插入图片描述

该系统采用自适应插值方案生成其空间分布场,并基于薄板样条法与随机森林方法构建插值模型。研究设计了六种空间位置配置及地形特征组合,并将这些参数与其他气象分析数据一并纳入模型作为协变数。通过独立观测站网络与现有气象数据集进行验证分析表明,该系统输出结果具有良好的精度表现,并较好地反映了季节变化特征及地理空间分布差异性。研究结果表明该系统变量全面且空间分辨率较高,在水文模拟、农业预测以及生态评估等多个领域均有广泛的应用潜力。

代码下载-Github

Github-CDMet

在这里插入图片描述

数据下载

该研究平台:构建了一个覆盖中国从2000年到2020年的每日4公里网格气象数据集。

在这里插入图片描述

数据下载:

在这里插入图片描述

数据处理

数据读取并绘图(Python全代码)

绘制2020年1月1日湿度数据如下:

请添加图片描述

Python代码如下:

复制代码
    import os
    import numpy as np
    import geopandas as gpd
    from netCDF4 import Dataset
    import matplotlib.pyplot as plt
    
    # 设置全局字体为 Times New Roman
    plt.rcParams["font.family"] = "Times New Roman"
    
    # 数据文件夹路径
    data_dir = r"C:\Database\3_CDMet\Relative humidity"  # 替换为您的文件夹路径
    variable_name = "rhu"  # 相对湿度变量名
    
    # SHP 文件路径
    Chinashp_file_path = "C:/PycharmProjects/GBA_Data_Process/shpFile/China/中国.shp"
    
    # 主程序:加载站点数据和 SHP 文件
    gdf_China = gpd.read_file(Chinashp_file_path)
    
    def read_daily_rhu(data_dir, year, day, lat_range=None, lon_range=None):
    """
    从 NetCDF 文件中读取指定年份和某一天的湿度数据,并应用缩放和无效值处理。
    可根据给定的经纬度范围裁剪数据。
    :param data_dir: 包含 NetCDF 文件的文件夹路径
    :param year: 年份 (如 2020)
    :param day: 天索引 (0 表示 1 月 1 日)
    :param lat_range: 纬度范围 (min_lat, max_lat),默认 None 表示全范围
    :param lon_range: 经度范围 (min_lon, max_lon),默认 None 表示全范围
    :return: 湿度数据二维数组 (lat, lon) 和裁剪后的经纬度数组
    """
    # 构造文件路径
    file_path = os.path.join(data_dir, f"CDMet_rhu_{year}.nc")  # 文件名为 CDMet_rhu_{year}.nc
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件未找到: {file_path}")
    
    # 打开 NetCDF 文件
    with Dataset(file_path, mode="r") as nc_file:
        # 获取湿度变量
        rhu_var = nc_file.variables[variable_name]
    
        # 获取缩放因子和偏移量
        scale_factor = getattr(rhu_var, "scale_factor", 1.0)
        add_offset = getattr(rhu_var, "add_offset", 0.0)
        fill_value = getattr(rhu_var, "_FillValue", -32767.0)
    
        # 获取经纬度
        lat = nc_file.variables["lat"][:]
        lon = nc_file.variables["lon"][:]
    
        # 读取指定天的数据 (二维数组: lat × lon)
        daily_data = rhu_var[day, :, :].astype(np.float32)
    
        # 应用缩放因子和偏移量
        # daily_data = daily_data * scale_factor + add_offset
    
        # 处理无效值 (_FillValue / missing_value)
        daily_data[daily_data == fill_value] = np.nan
    
        # 裁剪经纬度范围(如果指定)
        if lat_range:
            lat_min, lat_max = lat_range
            lat_idx = np.where((lat >= lat_min) & (lat <= lat_max))[0]
            lat = lat[lat_idx]  # 更新纬度数组
            daily_data = daily_data[lat_idx, :]  # 裁剪数据的纬度维度
    
        if lon_range:
            lon_min, lon_max = lon_range
            lon_idx = np.where((lon >= lon_min) & (lon <= lon_max))[0]
            lon = lon[lon_idx]  # 更新经度数组
            daily_data = daily_data[:, lon_idx]  # 裁剪数据的经度维度
    
    return daily_data, lat, lon
    
    
    def plot_rhu_daily_with_inset(data, lat, lon, title, lat_range, lon_range, china_lat_range=None, china_lon_range=None):
    """
    绘制某一天的湿度数据,并在右下角添加中国边界的小窗口。
    :param data: 二维湿度数据 (lat, lon)
    :param lat: 纬度数组
    :param lon: 经度数组
    :param title: 图像标题
    :param lat_range: 主图纬度范围 (min_lat, max_lat)
    :param lon_range: 主图经度范围 (min_lon, max_lon)
    :param china_lat_range: 小窗口纬度范围 (min_lat, max_lat)
    :param china_lon_range: 小窗口经度范围 (min_lon, max_lon)
    """
    # 创建主图
    fig, ax_main = plt.subplots(figsize=(10, 6))
    
    # 绘制湿度数据
    rhu_plot = ax_main.pcolormesh(lon, lat, data, cmap="coolwarm", shading="auto")  # 使用经纬度作为坐标
    cbar = plt.colorbar(rhu_plot, ax=ax_main, fraction=0.05, pad=0.04)
    cbar.set_label("Relative Humidity (%)", fontsize=14)
    
    # 设置标题和坐标轴标签
    ax_main.set_title(title, fontsize=16)
    ax_main.set_xlabel("Longitude (°E)", fontsize=14)
    ax_main.set_ylabel("Latitude (°N)", fontsize=14)
    ax_main.set_xlim(lon_range)
    ax_main.set_ylim(lat_range)
    
    # 设置经纬度刻度字体大小
    ax_main.tick_params(axis="both", which="major", labelsize=13)
    
    # 绘制网格
    ax_main.grid(True, linestyle="--", linewidth=0.5, alpha=0.7)
    
    # 绘制中国边界
    gdf_China.boundary.plot(ax=ax_main, color="black", linewidth=1, label="China Boundary")
    # ax_main.legend()
    
    # 创建小窗口 (inset) 位于主图右下角,与主图边框完全契合
    inset_width = 0.16  # 小窗口宽度占主图的比例
    inset_height = 0.26  # 小窗口高度占主图的比例
    # ax_inset = fig.add_axes([1 - inset_width - 0.05, 0.05, inset_width, inset_height])  # [x, y, width, height]
    ax_inset = fig.add_axes([0.682, 0.11, inset_width, inset_height])  # [x, y, width, height]
    
    # 设置小窗口经纬度范围
    if china_lat_range:
        ax_inset.set_ylim(china_lat_range)
    if china_lon_range:
        ax_inset.set_xlim(china_lon_range)
    
    # 绘制中国边界在小窗口中
    gdf_China.boundary.plot(ax=ax_inset, color="black", linewidth=1)
    
    # 在小窗口中标注主图的经纬度范围
    """
    rect = plt.Rectangle(
        (lon_range[0], lat_range[0]),  # 起点 (左下角)
        lon_range[1] - lon_range[0],  # 矩形宽度
        lat_range[1] - lat_range[0],  # 矩形高度
        linewidth=1.5, edgecolor="red", facecolor="none"
    )
    ax_inset.add_patch(rect)
    
    # 设置小窗口坐标标签
    ax_inset.set_xlabel("Lon (°E)", fontsize=10)
    ax_inset.set_ylabel("Lat (°N)", fontsize=10)
    ax_inset.tick_params(labelsize=8)    
    """
    
    # 隐藏小窗口的经纬度标签,仅保留方框
    ax_inset.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
    
    # 显示图像
    plt.show()
    
    # 主程序
    if __name__ == "__main__":
    # 指定年份和天数
    year = 2020
    day = 0  # 0 表示 1 月 1 日
    
    # 指定绘图的经纬度范围 (例如中国区域)
    lat_range = (16, 54)  # 主图纬度范围
    lon_range = (73, 137)  # 主图经度范围
    
    # 指定小窗口的经纬度范围 (覆盖整个中国)
    china_lat_range = (2, 25)  # 小窗口纬度范围
    china_lon_range = (102, 125)  # 小窗口经度范围
    
    # 读取 2020 年 1 月 1 日的湿度数据
    daily_data, lat, lon = read_daily_rhu(data_dir, year, day, lat_range, lon_range)
    
    # 打印数据信息
    print(f"2020 年 1 月 1 日数据形状: {daily_data.shape}")
    print(f"纬度范围: {lat.min()} 至 {lat.max()}")
    print(f"经度范围: {lon.min()} 至 {lon.max()}")
    print(f"数据范围: 最小值={np.nanmin(daily_data)}, 最大值={np.nanmax(daily_data)}")  # 排除 NaN
    
    # 绘制湿度分布图,带小窗口
    plot_rhu_daily_with_inset(
        daily_data, lat, lon,
        title="RHU in Jan.1 2020",
        lat_range=lat_range,
        lon_range=lon_range,
        china_lat_range=china_lat_range,
        china_lon_range=china_lon_range
    )

数据批量裁剪并保存为.tif文件(以2020年数据为例)

Python代码如下:

复制代码
    import os
    import numpy as np
    from netCDF4 import Dataset
    import rasterio
    from rasterio.transform import from_origin
    
    
    def read_nc_file(file_path):
    """
    读取 .nc 文件,提取变量信息和经纬度数据。
    """
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件未找到: {file_path}")
    
    # 打开 NetCDF 文件
    nc_file = Dataset(file_path, mode="r")
    return nc_file
    
    
    def extract_data_in_bbox(nc_file, variable_name, bbox, day):
    """
    提取指定经纬度范围的数据,并应用 scale_factor 和 add_offset。
    同时将缺测值 (_FillValue) 替换为 np.nan。
    """
    # 获取变量和经纬度
    rhu_var = nc_file.variables[variable_name]
    lat = nc_file.variables["lat"][:]
    lon = nc_file.variables["lon"][:]
    
    # 获取变量属性
    """
    scale_factor = getattr(rhu_var, "scale_factor", 1.0)
    print(f"缩放因子scale_factor为{scale_factor}")
    add_offset = getattr(rhu_var, "add_offset", 0.0)
    print(f"偏移值add_offset为{add_offset}")    
    """
    
    fill_value = getattr(rhu_var, "_FillValue", np.nan)
    # print(f"缺失值fill_value为{fill_value}")
    
    # 经纬度范围索引
    lat_min, lat_max, lon_min, lon_max = bbox
    lat_indices = np.where((lat >= lat_min) & (lat <= lat_max))[0]
    lon_indices = np.where((lon >= lon_min) & (lon <= lon_max))[0]
    
    if len(lat_indices) == 0 or len(lon_indices) == 0:
        raise ValueError("指定的经纬度范围无数据")
    
    # 提取当天数据
    daily_data = rhu_var[day, :, :].astype(np.float32)
    
    # 替换缺测值为 np.nan
    daily_data[daily_data == fill_value] = np.nan
    
    # 应用 scale_factor 和 add_offset
    # daily_data = daily_data * scale_factor + add_offset
    
    # 提取指定经纬度范围的数据子集
    subset_data = daily_data[np.min(lat_indices):np.max(lat_indices) + 1,
                             np.min(lon_indices):np.max(lon_indices) + 1]
    subset_lat = lat[np.min(lat_indices):np.max(lat_indices) + 1]
    subset_lon = lon[np.min(lon_indices):np.max(lon_indices) + 1]
    
    return subset_data, subset_lat, subset_lon
    
    
    def save_as_tif(output_dir, year, day, data, lat, lon, file_prefix="CDMet_rhu_GBA"):
    """
    将数据保存为 .tif 文件,并设置 NoData 值为 np.nan。
    """
    # 创建年份目录
    year_dir = os.path.join(output_dir, f"RHU_GBA_{year}")
    if not os.path.exists(year_dir):
        os.makedirs(year_dir)
    
    # 计算栅格分辨率和仿射变换参数
    lon_res = lon[1] - lon[0]  # 经度分辨率
    lat_res = lat[1] - lat[0]  # 纬度分辨率
    
    # 仿射变换 (左上角原点)
    transform = from_origin(west=lon[0], north=lat[-1], xsize=lon_res, ysize=-lat_res)
    
    # 设置保存路径
    output_file = os.path.join(year_dir, f"{file_prefix}_{year}_{day + 1:03d}.tif")
    
    # 保存为 GeoTIFF 文件
    with rasterio.open(
        output_file,
        "w",
        driver="GTiff",
        height=data.shape[0],
        width=data.shape[1],
        count=1,
        dtype=data.dtype,
        crs="EPSG:4326",
        transform=transform,
        nodata=np.nan  # 设置 NoData 值为 np.nan
    ) as dst:
        dst.write(data, 1)
    
    print(f"文件已保存: {output_file}")
    
    
    def process_nc_to_tif(file_path, output_dir, bbox, year, variable_name="rhu"):
    """
    主函数:读取 .nc 文件,提取指定经纬度范围的数据并保存为 .tif 文件。
    """
    # 打开 NetCDF 文件(在主函数中打开,避免上下文管理器关闭文件)
    nc_file = read_nc_file(file_path)
    
    # 获取时间维度大小
    num_days = nc_file.variables[variable_name].shape[0]
    print(f"检测到 {year} 年有 {num_days} 天的数据")
    
    # 遍历每一天的数据
    for day in range(num_days):
        print(f"正在处理 {year} 年第 {day + 1} 天的数据...")
        # 提取指定范围的数据
        subset_data, subset_lat, subset_lon = extract_data_in_bbox(
            nc_file, variable_name, bbox, day
        )
    
        # 保存为 .tif 文件
        save_as_tif(output_dir, year, day, subset_data, subset_lat, subset_lon)
    
    # 关闭 NetCDF 文件
    nc_file.close()
    print(f"{year} 年的数据处理完成!")
    
    
    def batch_process_nc_files(input_dir, output_dir, bbox, start_year, end_year, variable_name="rhu"):
    """
    批量处理指定年份范围内的 .nc 文件。
    """
    for year in range(start_year, end_year + 1):
        print(f"开始处理 {year} 年的数据...")
        # 构造 .nc 文件路径
        file_path = os.path.join(input_dir, f"CDMet_rhu_{year}.nc")
        if not os.path.exists(file_path):
            print(f"警告: {file_path} 不存在,跳过该年份!")
            continue
    
        # 处理当前年份的 .nc 文件
        process_nc_to_tif(file_path, output_dir, bbox, year, variable_name)
    
    print("所有年份的数据处理完成!")
    
    
    # 批量处理调用示例
    if __name__ == "__main__":
    # 输入目录和输出目录
    input_directory = r"C:\Database\3_CDMet\Relative humidity"
    output_directory = r"C:\Database\3_CDMet\RHU_GBA"
    bounding_box = (22, 24, 111, 115)  # 纬度范围 (22-24),经度范围 (111-115)
    
    # 年份范围
    start_year = 2000
    end_year = 2020
    
    # 批量处理
    batch_process_nc_files(input_directory, output_directory, bounding_box, start_year, end_year)

参考

全部评论 (0)

还没有任何评论哟~