当前位置: 首页 > news >正文

FY3D/MERSI 哈默投影 NDVI/EVI - EPSG:4326 投影转换 - Littlefish

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FY3D/MERSI 哈默投影 NDVI/EVI -> EPSG:4326
修复:Y 轴方向翻转导致的纬度整体偏低问题
"""
import os
import h5py
import numpy as np
from pyproj import CRS
import rasterio
from rasterio.transform import from_bounds
from rasterio.warp import calculate_default_transform, reproject, Resampling

# ---------- 用户配置 ----------
H5_FILE_PATH      = "FY3D_MERSI_0000_L3_NVI_MLT_HAM_20251130_AOTD_0250M_MS.HDF"
OUTPUT_TIF_PATH   = "FY3D_NDVI_4326_fixed.tif"
DATASET_NAME      = "250M_10day_NDVI"   # 或 EVI
OUTPUT_NODATA     = -9999.0
EARTH_RADIUS_KM   = 6363.961
EARTH_RADIUS_M    = EARTH_RADIUS_KM * 1000.0
# --------------------------------

def get_fy3d_meta_params(h5_file):
    attrs = h5_file.attrs
    def get(k, d=0.0): return attrs.get(k, d)
    return dict(
        center_lon=float(get("Projection_Center_Longitude", 0.0)),
        center_lat=float(get("Projection_Center_Latitude",  0.0)),
        left_top_x=float(get("Left-Top_X", 0.0)),
        left_top_y=float(get("Left-Top_Y", 0.0)),
        resolution_x=float(get("Resolution_X", 0.25)),
        resolution_y=float(get("Resolution_Y", 0.25)),
        data_lines=int(get("Data_Lines", 4000)),
        data_pixels=int(get("Data_Pixels", 4000)),
        fill_value=int(get("FillValue", -32768)),
        slope=float(get("Slope", 1.0e-4)),
        intercept=float(get("Intercept", 0.0)),
    )

def build_xy_coords_km(pp):
    """返回像素中心坐标(km)"""
    W, H = pp["data_pixels"], pp["data_lines"]
    dx, dy = pp["resolution_x"], pp["resolution_y"]
    x0, y0 = pp["left_top_x"], pp["left_top_y"]
    x = x0 + (np.arange(W) + 0.5) * dx
    y = y0 + (np.arange(H) + 0.5) * dy   # ✅ 关键修复:Y 轴向上
    return x, y

def write_reprojected_from_hammer(ndvi_array, x_km, y_km, out_tif,
                                  center_lon=0.0, nodata=OUTPUT_NODATA,
                                  R=EARTH_RADIUS_M):
    H, W = ndvi_array.shape
    assert W == x_km.size and H == y_km.size
    dx_km, dy_km = x_km[1] - x_km[0], y_km[1] - y_km[0]
    hx, hy = dx_km/2, dy_km/2
    xmin_km = float(x_km.min() - hx)
    xmax_km = float(x_km.max() + hx)
    ymin_km = float(y_km.min() - hy)
    ymax_km = float(y_km.max() + hy)

    # 转米
    xmin_m, xmax_m = xmin_km*1000, xmax_km*1000
    ymin_m, ymax_m = ymin_km*1000, ymax_km*1000

    src_trans = from_bounds(xmin_m, ymin_m, xmax_m, ymax_m, W, H)
    src_crs = CRS.from_proj4(f"+proj=hammer +lon_0={center_lon} +R={int(R)} +units=m +no_defs")
    dst_crs = CRS.from_epsg(4326)

    dst_trans, dst_w, dst_h = calculate_default_transform(
        src_crs, dst_crs, W, H, left=xmin_m, bottom=ymin_m, right=xmax_m, top=ymax_m)

    dst = np.full((dst_h, dst_w), nodata, np.float32)
    src = np.where(np.isnan(ndvi_array), nodata, ndvi_array).astype(np.float32)

    reproject(source=src, destination=dst,
              src_transform=src_trans, src_crs=src_crs, src_nodata=nodata,
              dst_transform=dst_trans, dst_crs=dst_crs, dst_nodata=nodata,
              resampling=Resampling.bilinear)

    meta = dict(driver="GTiff", height=dst_h, width=dst_w, count=1,
                dtype=dst.dtype, crs=dst_crs, transform=dst_trans,
                nodata=nodata, compress="LZW", tiled=True)
    with rasterio.open(out_tif, "w", **meta) as f:
        f.write(dst, 1)

    print("写出完成 →", out_tif)
    ul = dst_trans * (0, 0)
    lr = dst_trans * (dst_w, dst_h)
    print("输出左上 (lon lat):", ul)
    print("输出右下 (lon lat):", lr)

def main(h5_path, ds_name, out_tif):
    if not os.path.isfile(h5_path):
        print("文件不存在:", h5_path)
        return
    with h5py.File(h5_path, "r") as h5:
        print("HDF keys:", list(h5.keys()))
        if ds_name not in h5:
            print("数据集不存在:", ds_name); return
        raw = h5[ds_name][()]
        pp  = get_fy3d_meta_params(h5)

    # 清洗 NDVI
    fill, slope, off = pp["fill_value"], pp["slope"], pp["intercept"]
    ndvi = raw.astype(np.float32)
    ndvi = np.where(ndvi == fill, np.nan, ndvi) * slope + off
    ndvi = np.clip(ndvi, -1.0, 1.0)
    print("NDVI 范围:", np.nanmin(ndvi), "~", np.nanmax(ndvi))

    x_km, y_km = build_xy_coords_km(pp)
    print("X km 范围:", x_km.min(), "~", x_km.max())
    print("Y km 范围:", y_km.min(), "~", y_km.max())

    write_reprojected_from_hammer(ndvi, x_km, y_km, out_tif,
                                  center_lon=pp["center_lon"])

if __name__ == "__main__":
    main(H5_FILE_PATH, DATASET_NAME, OUTPUT_TIF_PATH)
 
http://www.jsqmd.com/news/69035/

相关文章:

  • 02_mysql数据库的数据类型
  • 为你的STM32毕设项目加点“料”:AI智能照明助手光环境自适应控制系统
  • 2025 年选制杯设备不踩坑:纸杯机、全伺服纸杯机、纸碗机、杯盖机及纸盘机厂家实力与设备稳定性评测
  • 北京抵押担保律师所法律服务测评排行榜:3 大微信小程序 VS2 家律所 权威解析靠谱之选
  • 2025 年 12 月北京下水道疏通/疏通马桶服务权威推荐榜:朝阳区高效上门,专业解决管道堵塞难题
  • 甘肃全屋定制五大推荐,欧比亚全屋定制公司领衔品质之选:涵盖旧房改造、装修公司、家具定制、全屋整装
  • 2025 年烟台网站建设 / 外贸站建设服务商推荐榜:数字化转型优质合作伙伴指南
  • 2025 十大图库网站精选推荐,找图片看这篇!可下载的图片素材
  • 2025 十大免费版权图库推荐:高清图片素材下载优质网站合集
  • 2026 北京建设工程律师 TOP8 精选排名榜:工程诉讼专业顾问权威推荐
  • 2025 十大商用素材网站推荐:高清正版图片、视频资源任选
  • 01_mysql_数据库创建、删除、使用
  • 2025年十大CRM系统推荐:全域能力哪款最适合你的企业?
  • 获取磁盘iops
  • 获取磁盘iops
  • 专业零售CRM软件首选推荐:南讯客道MA以AI全域能力赋能品牌增长
  • 走出情绪化误区,你我贷投诉官方渠道为权益保驾护航
  • 走出情绪化误区,你我贷投诉官方渠道为权益保驾护航
  • 双进风风机公司排行榜TOP1!双进风风机哪个品牌好?
  • BSS研究方向路线
  • 2025年河北算力服务器租用平台权威推荐榜单:河北算力服务器租用多少钱/河北服务器算力租用体验/河北租用服务器算力营销服务商精选
  • AI写论文工具隐藏技巧揭秘:5分钟生成25000字文献综述,引用真实全文
  • 详细介绍:Apple Pay 与 Google Pay 开发与结算全流程文档
  • 详细介绍:Apple Pay 与 Google Pay 开发与结算全流程文档
  • AgentScope Java v1.0 发布,让 Java 开发者轻松构建企业级 Agentic 应用
  • 让家会呼吸的定制之选:甘肃五大全屋定制品牌,欧比亚以环保与匠心领跑
  • 2025年大型企业如何建设BI系统?企业如何应用BI系统?
  • 2025年市面上服务好的楼板搭建公司,现浇别墅搭建/现浇楼梯/现浇钢筋混凝土楼板/楼板搭建/现浇楼梯/现浇搭建报价口碑推荐榜
  • Docker:Debian更新源并安装docker
  • 正规股票配资平台最新排行榜,实盘指南