Python:如何将MCD12Q1\MOD11A2\MOD13A2原始数据集批量输出为TIFF文件(镶嵌/重投影/)?

博客已同步微信公众号:GIS茄子;若博客出现纰漏或有更多问题交流欢迎关注GIS茄子,或者邮箱联系(推荐-见主页).

微信公众号

00 前言

之前一段时间一直使用ENVI IDL处理遥感数据,但是确实对于一些比较新鲜的东西IDL并没有python那么好的及时性,封装的东西也较为底层需要自己实现的东西相对python还是不那么方便,当然它也训练了我对于数据的处理能力。另外最近一直在探索深度学习,IDL相对小众,在时间不充裕的情况,我将倾向于使用Python进行学习而非IDL。因此,目前相当长一段时间我将同时兼容Python和IDL两门语言,但正如你所见,IDL的趋势正在减弱。

目前网络上似乎没有关于MODIS GRID产品预处理的完整处理流程而仅仅是片段代码,因此本博客持续更新,希望可以完善这方面的内容,如果代码处理过程中遇到问题或者存在纰漏,请公众号私信我或者邮箱联系(CSDN联系时常无法接收)。

处理流程包括:

  • 数据集无效值去除(依据数据集属性)、单位换算(土地利用无需)
  • 相同时间尺度的图像镶嵌
  • 重投影(Sinu正弦投影转WGS84)
  • HDF4文件输出为GeoTIFF文件

另外该脚本支持:

  • 支持并行处理
  • 批量处理

程序所需环境:

numpypyhdfgdal
1.24.40.10.53.4.3

01 数据说明

目前由于一些关系,地理空间数据云下载的NDVI、地表温度产品无法满足需求,需要移步NASA官网进行下载原始数据集,包括

  1. MCD12Q1(土地利用数据集)
  2. MOD12A2(地表温度数据集)
  3. MOD13A2(NDVI数据集)

数据集情况如下:

  • MCD12Q1数据集:
    MCD12Q1数据集
    包括2001~2020年,一年4景影像(需要进行拼接)

  • MOD12A2数据集
    MOD12A2数据集
    包括2000~2022年,每8天4景影像(同样需要拼接),处理上与上面稍有不同,体现在时间维度上。

  • MOD13A2数据集
    MOD13A2数据集

上述三个数据集均为MODIS GRID格式(Sinusoidal正弦投影),关于正弦投影内容见:

Python:如何解决MODIS GRID(正弦投影/GCTP_SNSOID)的重投影问题?

02 自定义函数说明

2.1 镶嵌函数(not just mosaic)

镶嵌函数img_mosaic实际上包含从多个HDF4文件 ⇒ 镶嵌得到的栅格矩阵(np.ndarray)的整个过程,其中包含:

  • HDF4文件的数据集读取和属性获取
  • 数据集的无效值设定、单位换算
  • 投影参数的获取、仿射参数的获取
  • 镶嵌

如下为详细代码:

def img_mosaic(mosaic_paths: list, mosaic_ds_name: str, return_all: bool = True, img_nodata: Union[int, float] = np.nan,
               img_type: Union[np.int32, np.float32, None] = None, unit_conversion: bool = False,
               scale_factor_op: str = 'multiply'):
    """
    该函数用于对列表中的所有HDF4文件进行镶嵌
    :param scale_factor_op: 比例因子的运算符, 默认是乘以(可选: multiply, divide), 该参数尽在unit_conversion为True时生效
    :param unit_conversion: 是否进行单位换算
    :param mosaic_ds_name: 待镶嵌的数据集名称
    :param mosaic_paths: 多个HDF4文件路径组成的字符串列表
    :param return_all: 是否一同返回仿射变换、镶嵌数据集的坐标系等参数
    :return: 默认返回镶嵌好的数据集
    :param img_type: 待镶嵌影像的数据类型
    :param img_nodata: 影像中的无效值设置

    镶嵌策略是last模式, 即如果有存在像元重叠, mosaic_paths中靠后影像的像元将覆盖其.
    """

    # 获取镶嵌范围
    x_mins, x_maxs, y_mins, y_maxs = [], [], [], []
    for mosaic_path in mosaic_paths:
        hdf = SD(mosaic_path)  # 默认只读
        # 获取元数据
        metadata = hdf.__getattr__('StructMetadata.0')
        # 获取角点信息
        ul_pt = [float(x) for x in re.findall(r'UpperLeftPointMtrs=\((.*)\)', metadata)[0].split(',')]
        lr_pt = [float(x) for x in re.findall(r'LowerRightMtrs=\((.*)\)', metadata)[0].split(',')]
        x_mins.append(ul_pt[0])
        x_maxs.append(lr_pt[0])
        y_mins.append(lr_pt[1])
        y_maxs.append(ul_pt[1])
    else:
        # 计算分辨率
        col = int(re.findall(r'XDim=(.*?)\n', metadata)[0])
        row = int(re.findall(r'YDim=(.*?)\n', metadata)[0])
        x_res = (lr_pt[0] - ul_pt[0]) / col
        y_res = (ul_pt[1] - lr_pt[1]) / row
        # 如果img_type没有指定, 那么数据类型默认为与输入相同
        if img_type is None:
            img_type = hdf.select(mosaic_ds_name)[:].dtype
        # 获取数据集的坐标系参数并转化为proj4字符串格式
        projection_param = [float(_param) for _param in re.findall(r'ProjParams=\((.*?)\)', metadata)[0].split(',')]
        mosaic_img_proj4 = "+proj={} +R={:0.4f} +lon_0={:0.4f} +lat_0={:0.4f} +x_0={:0.4f} " \
                           "+y_0={:0.4f} ".format('sinu', projection_param[0], projection_param[4], projection_param[5],
                                                  projection_param[6], projection_param[7])
        # 关闭文件, 释放资源
        hdf.end()
    x_min, x_max, y_min, y_max = min(x_mins), max(x_maxs), min(y_mins), max(y_maxs)

    # 镶嵌
    col = ceil((x_max - x_min) / x_res)
    row = ceil((y_max - y_min) / y_res)
    mosaic_img = np.full((col, row), img_nodata, dtype=img_type)  # 初始化
    for ix, mosaic_path in enumerate(mosaic_paths):
        hdf = SD(mosaic_path)
        target_ds = hdf.select(mosaic_ds_name)
        # 读取数据集和预处理
        target = target_ds.get().astype(img_type)
        valid_range = target_ds.attributes()['valid_range']
        target[(target < valid_range[0]) | (target > valid_range[1])] = img_nodata  # 限定有效范围
        if unit_conversion:  # 进行单位换算
            scale_factor = target_ds.attributes()['scale_factor']
            add_offset = target_ds.attributes()['add_offset']
            # 判断比例因子的运算符
            if scale_factor_op == 'multiply':
                target[target != img_nodata] = target[target != img_nodata] * scale_factor + add_offset
            elif scale_factor_op == 'divide':
                target[target != img_nodata] = target[target != img_nodata] / scale_factor + add_offset
            # 计算当前镶嵌范围
        start_col = floor(((x_mins[ix] + x_res / 2) - x_min) / x_res)
        start_row = floor((y_max - (y_maxs[ix] - x_res / 2)) / y_res)
        end_col = start_col + target.shape[0]
        end_row = start_row + target.shape[1]
        mosaic_img[start_row:end_row, start_col:end_col] = target  # 覆盖

        # 释放资源
        target_ds.endaccess()
        hdf.end()

    if return_all:
        return mosaic_img, [x_min, x_res, 0, y_max, 0, -y_res], mosaic_img_proj4

    return mosaic_img

2.2 重投影(GLT校正)

重投影核心部分通过gdal.Warp函数实现,并没有自己从底层写,因为GDAL已经封装的很好.其中对于正弦投影的关键部分在上文博客提及,这里我们通过proj4进行坐标系的简洁表达。

def img_warp(src_img: np.ndarray, out_path: str, transform: list, src_proj4: str, out_res: float,
             nodata: Union[int, float] = None, resample: str = 'nearest') -> None:
    """
    该函数用于对正弦投影下的栅格矩阵进行重投影(GLT校正), 得到WGS84坐标系下的栅格矩阵并输出为TIFF文件
    :param src_img: 待重投影的栅格矩阵
    :param out_path: 输出路径
    :param transform: 仿射变换参数([x_min, x_res, 0, y_max, 0, -y_res], 旋转参数为0是常规选项)
    :param out_res: 输出的分辨率(栅格方形)
    :param nodata: 设置为NoData的数值
    :param out_type: 输出的数据类型
    :param resample: 重采样方法(默认是最近邻, ['nearest', 'bilinear', 'cubic'])
    :param src_proj4: 表达源数据集(src_img)的坐标系参数(以proj4字符串形式)
    :return: None
    """

    # 输出数据类型
    if np.issubdtype(src_img.dtype, np.integer):
        out_type = gdal.GDT_Int32
    elif np.issubdtype(src_img.dtype, np.floating):
        out_type = gdal.GDT_Float32
    else:
        raise ValueError("当前待校正数组类型为不支持的数据类型")
    resamples = {'nearest': gdal.GRA_NearestNeighbour, 'bilinear': gdal.GRA_Bilinear, 'cubic': gdal.GRA_Cubic}
    # 原始数据集创建(正弦投影)
    driver = gdal.GetDriverByName('MEM')  # 在内存中临时创建
    src_ds = driver.Create("", src_img.shape[1], src_img.shape[0], 1, out_type)  # 注意: 先传列数再传行数, 1表示单波段
    srs = osr.SpatialReference()
    srs.ImportFromProj4(src_proj4)  # 依据
    """
    对于src_proj4, 依据元数据StructMetadata.0知:
        Projection=GCTP_SNSOID; ProjParams=(6371007.181000,0,0,0,0,0,0,0,0,0,0,0,0)
    或数据集属性(MODIS_Grid_8Day_1km_LST/Data_Fields/Projection)知:
        :grid_mapping_name = "sinusoidal";
        :longitude_of_central_meridian = 0.0; // double
        :earth_radius = 6371007.181; // double
    """
    src_ds.SetProjection(srs.ExportToWkt())  # 设置投影信息
    src_ds.SetGeoTransform(transform)  # 设置仿射参数
    src_ds.GetRasterBand(1).WriteArray(src_img)  # 写入数据
    src_ds.GetRasterBand(1).SetNoDataValue(nodata)
    # 重投影信息(WGS84)
    dst_srs = osr.SpatialReference()
    dst_srs.ImportFromEPSG(4326)
    # 重投影
    dst_ds = gdal.Warp(out_path, src_ds, dstSRS=dst_srs, xRes=out_res, yRes=out_res, dstNodata=nodata,
                       outputType=out_type, multithread=True, format='GTiff', resampleAlg=resamples[resample])
    if dst_ds:  # 释放缓存和资源
        dst_ds.FlushCache()
        src_ds, dst_ds = None, None

2.3 年积日转年月日函数

这个实现基本上就是调用datetime模块即可,也无需自己重写。

def ydays2ym(file_path: str) -> str:
    """
    获取路径中的年积日并转化为年月日
    :param file_path: 文件路径
    :return: 返回表达年月日的字符串
    """

    file_name = os.path.basename(file_path)
    ydays = file_name[9:16]
    date = datetime.strptime(ydays, "%Y%j")

    return date.strftime("%Y_%m")

2.4 并行处理相关函数

由于对于相同的数据集例如NDVI,不同时间点上的镶嵌过程都是类似,这里为了更好处理并行处理的简洁性,对原先的process_id闭包,这使得process_id可以记住被创建的环境,无需在每次调用process_id函数时调用重复的变量。

def process_task(union_id, process_paths, ds_name, out_dir, description, nodata, out_res, resamlpe='nearest',
                 temperature=False, img_type=np.float32, unit_conversion=True, scale_factor_op='multiply'):
    print_lock = Lock()  # 线程锁
    # 处理
    def process_id(id: any = None):
        start_time = time.time()
        cur_mosaic_ixs = [_ix for _ix, _id in enumerate(union_id) if _id == id]
        # 镶嵌
        mosaic_paths = [process_paths[_ix] for _ix in cur_mosaic_ixs]
        mosaic_img, transform, mosaic_img_proj4 = img_mosaic(mosaic_paths, ds_name, img_nodata=nodata,
                                                             img_type=img_type, unit_conversion=unit_conversion,
                                                             scale_factor_op=scale_factor_op)
        if temperature:  # 若设置temperature, 则说明当前处理数据集为地表温度, 需要开尔文 ==> 摄氏度
            mosaic_img[mosaic_img != nodata] -= 273.15
        # 重投影
        reproj_path = os.path.join(out_dir, description + '_' + id + '.tiff')
        img_warp(mosaic_img, reproj_path, transform, mosaic_img_proj4, out_res, nodata, resample=resamlpe)
        end_time = time.time()

        with print_lock:  # 避免打印混乱
            print("{}-{} 处理完毕: {:0.2f}s".format(description, id, end_time - start_time))

    return process_id

当然,为了更好了解数据处理的过程,后面也会以注释形式贴出非并行处理的代码。

03 完整代码

完整代码如下:

# @Author   : ChaoQiezi
# @Time     : 2023/12/14  6:31
# @Email    : chaoqiezi.one@qq.com

"""
This script is used to 对MODIS GRID产品(hdf4文件)进行批量镶嵌和重投影并输出为GeoTIFF文件

<说明>
# pyhdf模块相关
对于读取HDF4文件的pyhdf模块需要依据python版本安装指定的whl文件才可正常运行,
下载wheel文件见: https://www.lfd.uci.edu/~gohlke/pythonlibs/
安装: cmd ==> where python ==> 跳转指定python路径 ==> cd Scripts ==> pip install wheel文件的绝对路径

# 数据集
MCD12Q1为土地利用数据
MOD11A2为地表温度数据
MOD13A2为植被指数数据(包括NDVI\EVI)

temp: 数据对比 + 无效值的指定
"""

import os
import re
import time
from glob import glob
from typing import Union
from datetime import datetime
from math import ceil, floor
from threading import Lock
from concurrent.futures import ThreadPoolExecutor  # 线程池

import numpy as np
from pyhdf.SD import SD
from osgeo import gdal, osr


def img_mosaic(mosaic_paths: list, mosaic_ds_name: str, return_all: bool = True, img_nodata: Union[int, float] = np.nan,
               img_type: Union[np.int32, np.float32, None] = None, unit_conversion: bool = False,
               scale_factor_op: str = 'multiply'):
    """
    该函数用于对列表中的所有HDF4文件进行镶嵌
    :param scale_factor_op: 比例因子的运算符, 默认是乘以(可选: multiply, divide), 该参数尽在unit_conversion为True时生效
    :param unit_conversion: 是否进行单位换算
    :param mosaic_ds_name: 待镶嵌的数据集名称
    :param mosaic_paths: 多个HDF4文件路径组成的字符串列表
    :param return_all: 是否一同返回仿射变换、镶嵌数据集的坐标系等参数
    :return: 默认返回镶嵌好的数据集
    :param img_type: 待镶嵌影像的数据类型
    :param img_nodata: 影像中的无效值设置

    镶嵌策略是last模式, 即如果有存在像元重叠, mosaic_paths中靠后影像的像元将覆盖其.
    """

    # 获取镶嵌范围
    x_mins, x_maxs, y_mins, y_maxs = [], [], [], []
    for mosaic_path in mosaic_paths:
        hdf = SD(mosaic_path)  # 默认只读
        # 获取元数据
        metadata = hdf.__getattr__('StructMetadata.0')
        # 获取角点信息
        ul_pt = [float(x) for x in re.findall(r'UpperLeftPointMtrs=\((.*)\)', metadata)[0].split(',')]
        lr_pt = [float(x) for x in re.findall(r'LowerRightMtrs=\((.*)\)', metadata)[0].split(',')]
        x_mins.append(ul_pt[0])
        x_maxs.append(lr_pt[0])
        y_mins.append(lr_pt[1])
        y_maxs.append(ul_pt[1])
    else:
        # 计算分辨率
        col = int(re.findall(r'XDim=(.*?)\n', metadata)[0])
        row = int(re.findall(r'YDim=(.*?)\n', metadata)[0])
        x_res = (lr_pt[0] - ul_pt[0]) / col
        y_res = (ul_pt[1] - lr_pt[1]) / row
        # 如果img_type没有指定, 那么数据类型默认为与输入相同
        if img_type is None:
            img_type = hdf.select(mosaic_ds_name)[:].dtype
        # 获取数据集的坐标系参数并转化为proj4字符串格式
        projection_param = [float(_param) for _param in re.findall(r'ProjParams=\((.*?)\)', metadata)[0].split(',')]
        mosaic_img_proj4 = "+proj={} +R={:0.4f} +lon_0={:0.4f} +lat_0={:0.4f} +x_0={:0.4f} " \
                           "+y_0={:0.4f} ".format('sinu', projection_param[0], projection_param[4], projection_param[5],
                                                  projection_param[6], projection_param[7])
        # 关闭文件, 释放资源
        hdf.end()
    x_min, x_max, y_min, y_max = min(x_mins), max(x_maxs), min(y_mins), max(y_maxs)

    # 镶嵌
    col = ceil((x_max - x_min) / x_res)
    row = ceil((y_max - y_min) / y_res)
    mosaic_img = np.full((col, row), img_nodata, dtype=img_type)  # 初始化
    for ix, mosaic_path in enumerate(mosaic_paths):
        hdf = SD(mosaic_path)
        target_ds = hdf.select(mosaic_ds_name)
        # 读取数据集和预处理
        target = target_ds.get().astype(img_type)
        valid_range = target_ds.attributes()['valid_range']
        target[(target < valid_range[0]) | (target > valid_range[1])] = img_nodata  # 限定有效范围
        if unit_conversion:  # 进行单位换算
            scale_factor = target_ds.attributes()['scale_factor']
            add_offset = target_ds.attributes()['add_offset']
            # 判断比例因子的运算符
            if scale_factor_op == 'multiply':
                target[target != img_nodata] = target[target != img_nodata] * scale_factor + add_offset
            elif scale_factor_op == 'divide':
                target[target != img_nodata] = target[target != img_nodata] / scale_factor + add_offset
            # 计算当前镶嵌范围
        start_col = floor(((x_mins[ix] + x_res / 2) - x_min) / x_res)
        start_row = floor((y_max - (y_maxs[ix] - x_res / 2)) / y_res)
        end_col = start_col + target.shape[0]
        end_row = start_row + target.shape[1]
        mosaic_img[start_row:end_row, start_col:end_col] = target  # 覆盖

        # 释放资源
        target_ds.endaccess()
        hdf.end()

    if return_all:
        return mosaic_img, [x_min, x_res, 0, y_max, 0, -y_res], mosaic_img_proj4

    return mosaic_img


def img_warp(src_img: np.ndarray, out_path: str, transform: list, src_proj4: str, out_res: float,
             nodata: Union[int, float] = None, resample: str = 'nearest') -> None:
    """
    该函数用于对正弦投影下的栅格矩阵进行重投影(GLT校正), 得到WGS84坐标系下的栅格矩阵并输出为TIFF文件
    :param src_img: 待重投影的栅格矩阵
    :param out_path: 输出路径
    :param transform: 仿射变换参数([x_min, x_res, 0, y_max, 0, -y_res], 旋转参数为0是常规选项)
    :param out_res: 输出的分辨率(栅格方形)
    :param nodata: 设置为NoData的数值
    :param out_type: 输出的数据类型
    :param resample: 重采样方法(默认是最近邻, ['nearest', 'bilinear', 'cubic'])
    :param src_proj4: 表达源数据集(src_img)的坐标系参数(以proj4字符串形式)
    :return: None
    """

    # 输出数据类型
    if np.issubdtype(src_img.dtype, np.integer):
        out_type = gdal.GDT_Int32
    elif np.issubdtype(src_img.dtype, np.floating):
        out_type = gdal.GDT_Float32
    else:
        raise ValueError("当前待校正数组类型为不支持的数据类型")
    resamples = {'nearest': gdal.GRA_NearestNeighbour, 'bilinear': gdal.GRA_Bilinear, 'cubic': gdal.GRA_Cubic}
    # 原始数据集创建(正弦投影)
    driver = gdal.GetDriverByName('MEM')  # 在内存中临时创建
    src_ds = driver.Create("", src_img.shape[1], src_img.shape[0], 1, out_type)  # 注意: 先传列数再传行数, 1表示单波段
    srs = osr.SpatialReference()
    srs.ImportFromProj4(src_proj4)  # 依据
    """
    对于src_proj4, 依据元数据StructMetadata.0知:
        Projection=GCTP_SNSOID; ProjParams=(6371007.181000,0,0,0,0,0,0,0,0,0,0,0,0)
    或数据集属性(MODIS_Grid_8Day_1km_LST/Data_Fields/Projection)知:
        :grid_mapping_name = "sinusoidal";
        :longitude_of_central_meridian = 0.0; // double
        :earth_radius = 6371007.181; // double
    """
    src_ds.SetProjection(srs.ExportToWkt())  # 设置投影信息
    src_ds.SetGeoTransform(transform)  # 设置仿射参数
    src_ds.GetRasterBand(1).WriteArray(src_img)  # 写入数据
    src_ds.GetRasterBand(1).SetNoDataValue(nodata)
    # 重投影信息(WGS84)
    dst_srs = osr.SpatialReference()
    dst_srs.ImportFromEPSG(4326)
    # 重投影
    dst_ds = gdal.Warp(out_path, src_ds, dstSRS=dst_srs, xRes=out_res, yRes=out_res, dstNodata=nodata,
                       outputType=out_type, multithread=True, format='GTiff', resampleAlg=resamples[resample])
    if dst_ds:  # 释放缓存和资源
        dst_ds.FlushCache()
        src_ds, dst_ds = None, None


def ydays2ym(file_path: str) -> str:
    """
    获取路径中的年积日并转化为年月日
    :param file_path: 文件路径
    :return: 返回表达年月日的字符串
    """

    file_name = os.path.basename(file_path)
    ydays = file_name[9:16]
    date = datetime.strptime(ydays, "%Y%j")

    return date.strftime("%Y_%m")


# 闭包
def process_task(union_id, process_paths, ds_name, out_dir, description, nodata, out_res, resamlpe='nearest',
                 temperature=False, img_type=np.float32, unit_conversion=True, scale_factor_op='multiply'):
    print_lock = Lock()  # 线程锁
    # 处理
    def process_id(id: any = None):
        start_time = time.time()
        cur_mosaic_ixs = [_ix for _ix, _id in enumerate(union_id) if _id == id]
        # 镶嵌
        mosaic_paths = [process_paths[_ix] for _ix in cur_mosaic_ixs]
        mosaic_img, transform, mosaic_img_proj4 = img_mosaic(mosaic_paths, ds_name, img_nodata=nodata,
                                                             img_type=img_type, unit_conversion=unit_conversion,
                                                             scale_factor_op=scale_factor_op)
        if temperature:  # 若设置temperature, 则说明当前处理数据集为地表温度, 需要开尔文 ==> 摄氏度
            mosaic_img[mosaic_img != nodata] -= 273.15
        # 重投影
        reproj_path = os.path.join(out_dir, description + '_' + id + '.tiff')
        img_warp(mosaic_img, reproj_path, transform, mosaic_img_proj4, out_res, nodata, resample=resamlpe)
        end_time = time.time()

        with print_lock:  # 避免打印混乱
            print("{}-{} 处理完毕: {:0.2f}s".format(description, id, end_time - start_time))

    return process_id

# 准备
in_dir = 'F:\Cy_modis'  # F:\Cy_modis\MCD12Q1_2001_2020、F:\Cy_modis\MOD11A2_2000_2022、F:\Cy_modis\MOD13A2_2001_2020
out_dir = 'H:\Datasets\Objects\Veg'
landuse_name = 'LC_Type1'  # Land Cover Type 1: Annual International Geosphere-Biosphere Programme (IGBP) classification
lst_name = 'LST_Day_1km'
ndvi_name = '1 km 16 days NDVI'  # 注意panoply上显示为: 1_km_16_days_NDVI, 实际上是做了显示上的优化, 原始名称为当前
out_landuse_res = 0.0045  # 500m
out_lst_res = 0.009  # 1000m
out_ndvi_res = 0.009
# 预准备
out_landuse_dir = os.path.join(out_dir, 'landuse')
out_lst_dir = os.path.join(out_dir, 'lst')
out_ndvi_dir = os.path.join(out_dir, 'ndvi')
_ = [os.makedirs(_dir, exist_ok=True) for _dir in [out_landuse_dir, out_lst_dir, out_ndvi_dir]]

# 对MCD12Q1数据集(土地利用数据集)进行镶嵌和重投影(GLT校正)
landuse_paths = glob(os.path.join(in_dir, '**', 'MCD12Q1*.hdf'), recursive=True)  # 迭代
union_id = [os.path.basename(_path)[9:13] for _path in landuse_paths]  # 基于年份进行合并镶嵌的字段(年份-此处)
unique_id = set(union_id)  # unique_id = np.unique(np.asarray(union_id))  # 不使用set是为保证原始顺序
# 多线程处理
with ThreadPoolExecutor() as executer:
    start_time = time.time()
    process_id = process_task(union_id, landuse_paths, landuse_name, out_landuse_dir, 'Landuse', 255, out_landuse_res,
                              img_type=np.int32, unit_conversion=False)
    executer.map(process_id, unique_id)
    end_time = time.time()
    print('MCD12Q1(土地利用数据集预处理完毕: {:0.2f})'.format(end_time - start_time))
# # 常规处理
# for id in unique_id:
#     start_time = time.time()
#     cur_mosaic_ixs = [_ix for _ix, _id in enumerate(union_id) if _id == id]
#     # 镶嵌
#     mosaic_paths = [landuse_paths[_ix] for _ix in cur_mosaic_ixs]
#     mosaic_img, transform, mosaic_img_proj4 = img_mosaic(mosaic_paths, landuse_name, img_nodata=255, img_type=np.int32)
#     # 重投影
#     reproj_path = os.path.join(out_landuse_dir, 'landuse_' + id + '.tiff')
#     img_warp(mosaic_img, reproj_path, transform, mosaic_img_proj4, out_landuse_res, 255, resample='nearest')
#
#     # 打印输出
#     end_time = time.time()
#     print("Landuse-{} 处理完毕: {:0.2f}s".format(id, end_time - start_time))

# 对MOD12A2数据集(地表温度数据集)进行镶嵌和重投影(GLT校正)
lst_paths = glob(os.path.join(in_dir, '**', 'MOD11A2*.hdf'), recursive=True)
union_id = [ydays2ym(_path) for _path in lst_paths]
unique_id = set(union_id)
# 多线程处理
with ThreadPoolExecutor() as executer:
    start_time = time.time()
    process_id = process_task(union_id, lst_paths, lst_name, out_lst_dir, 'LST', -65535, out_lst_res, resamlpe='cubic',
                              temperature=True, unit_conversion=True)
    executer.map(process_id, unique_id)
    end_time = time.time()
    print('MCD11A2(地表温度数据集预处理完毕: {:0.2f})s'.format(end_time - start_time))
# # 常规处理
# for id in unique_id:
#     start_time = time.time()
#     cur_mosaic_ixs = [_ix for _ix, _id in enumerate(union_id) if _id == id]
#     # 镶嵌
#     mosaic_paths = [lst_paths[_ix] for _ix in cur_mosaic_ixs]
#     mosaic_img, transform, mosaic_img_proj4 = img_mosaic(mosaic_paths, lst_name, img_nodata=-65535,
#                                                          img_type=np.float32, unit_conversion=True)
#     # 开尔文 ==> 摄氏度
#     mosaic_img -= 273.15
#     # 重投影
#     reproj_path = os.path.join(out_lst_dir, 'lst_' + id + '.tiff')
#     img_warp(mosaic_img, reproj_path, transform, mosaic_img_proj4, out_lst_res, -65535, resample='cubic')
#
#     # 打印输出
#     end_time = time.time()
#     print("LST-{} 处理完毕: {:0.2f}s".format(id, end_time - start_time))

# 对MOD13A2数据集(NDVI数据集)进行镶嵌和重投影(GLT校正)
ndvi_paths = glob(os.path.join(in_dir, '**', 'MOD13A2*.hdf'), recursive=True)
union_id = [ydays2ym(_path) for _path in ndvi_paths]
unique_id = np.unique(np.asarray(union_id))
# 多线程处理
with ThreadPoolExecutor() as executer:
    start_time = time.time()
    process_id = process_task(union_id, ndvi_paths, ndvi_name, out_ndvi_dir, 'NDVI', -65535, out_ndvi_res,
                              resamlpe='cubic', unit_conversion=True, scale_factor_op='divide')
    executer.map(process_id, unique_id)
    end_time = time.time()
    print('MCD13A2(NDVI数据集预处理完毕: {:0.2f})s'.format(end_time - start_time))
# 常规处理
# for id in unique_id:
#     start_time = time.time()
#     cur_mosaic_ixs = [_ix for _ix, _id in enumerate(union_id) if _id == id]
#     # 镶嵌
#     mosaic_paths = [ndvi_paths[_ix] for _ix in cur_mosaic_ixs]
#     mosaic_img, transform, mosaic_img_proj4 = img_mosaic(mosaic_paths, ndvi_name, img_nodata=-65535, img_type=np.float32,
#                                                          unit_conversion=True, scale_factor_op='divide')
#     # 重投影
#     reproj_path = os.path.join(out_ndvi_dir, 'ndvi_' + id + '.tiff')
#     img_warp(mosaic_img, reproj_path, transform, mosaic_img_proj4, out_ndvi_res, -65535, resample='cubic')
#
#     # 打印输出
#     end_time = time.time()
#     print("NDVI-{} 处理完毕: {:0.2f}s".format(id, end_time - start_time))

当然,由于水平有限,代码中可能仍存在细节方面的纰漏。希望指正,这将对我帮助很大,这里表示我的感谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/248952.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】使用官方脚本自动安装 Docker(Ubuntu 22.04)

前言 Docker是一种开源平台&#xff0c;用于开发、交付和运行应用程序。它利用了容器化技术&#xff0c;使开发人员能够将应用程序及其依赖项打包到一个称为Docker容器的可移植容器中。这些容器可以在任何运行Docker的机器上快速、一致地运行&#xff0c;无论是开发环境、测试…

微服务架构之争:Quarkus VS Spring Boot

在容器时代&#xff08;“Docker时代”&#xff09;&#xff0c;无论如何&#xff0c;Java仍然活着。Java在性能方面一直很有名&#xff0c;主要是因为代码和真实机器之间的抽象层&#xff0c;多平台的成本&#xff08;一次编写&#xff0c;随处运行——还记得吗&#xff1f;&a…

Ubuntu虚拟机怎么设置静态IP

1 首先先ifconfig看一下使用的是哪个网络接口&#xff1a; 2 编辑 sudo vi /etc/netplan/00-installer-config.yamlnetwork:ethernets:ens33: # 根据您的网络接口进行修改&#xff0c;有的是eth0&#xff0c;有的是ens33&#xff0c;具体看第一步显示的是哪个网络接口addres…

【css】css实现文字两端对齐效果:

文章目录 一、方法1&#xff1a;二、方法2&#xff1a;三、注意&#xff1a; 一、方法1&#xff1a; 给元素设置 text-align: justify;text-align-last: justify;并且加上text-justify: distribute-all-line; 目的是兼容ie浏览器 p{width: 130px;text-align: justify;text-alig…

教育数字化转型 赋能家庭场景自主学习习惯养成

北京市气象台12月12日22时升级发布暴雪橙色预警信号&#xff0c;北京市教委决定自12月13日开始&#xff0c;全市中小学幼儿园采取学生临时居家学习措施。自疫情以来&#xff0c;家庭已经成为另一个学习中心&#xff0c;学校不再是教育的孤岛。 学习方式的变革&#xff0c;数字…

【️什么是分布式系统的一致性 ?】

&#x1f60a;引言 &#x1f396;️本篇博文约8000字&#xff0c;阅读大约30分钟&#xff0c;亲爱的读者&#xff0c;如果本博文对您有帮助&#xff0c;欢迎点赞关注&#xff01;&#x1f60a;&#x1f60a;&#x1f60a; &#x1f5a5;️什么是分布式系统的一致性 &#xff1f…

【C++】POCO学习总结(十七):日志系统(级别、通道、格式化、记录流)

【C】郭老二博文之&#xff1a;C目录 1、Poco::Message 日志消息 1.1 说明 所有日志消息都在Poco::Message对象中存储和传输。 头文件&#xff1a;#include “Poco/Message.h” 一条消息包含如下内容&#xff1a;优先级、来源、一个文本、一个时间戳、进程和线程标识符、可选…

MacOS下载配置OpenCV

主要参考的是OpenCV官方的这篇文章&#xff1a;OpenCV: Installation in MacOS 安装OpenCV需要下载一些安装包&#xff1a;CMake3.9、Git、Python这些我之前已经下载好&#xff0c;这里就不过多阐述了&#xff0c;自行百度安装即可 1.从Git库获取OpenCV&#xff1a; git clon…

Chrome安装Vue插件vue-devtools

1.下载 下载扩展文件&#xff0c;可以去官网下载 GitHub - vuejs/devtools: ⚙️ Browser devtools extension for debugging Vue.js applications. 可以在这里下载&#xff0c;比较方便 https://gitee.com/zhang_banglong/vue-devtools 2.解压 下载好之后解压文件 3.打开控制…

vue-实现高德地图-省级行政区地块显示+悬浮显示+标签显示

<template><div><div id"container" /><div click"showFn">显示</div><div click"removeFn">移除</div></div> </template><script> import AMapLoader from amap/amap-jsapi-load…

基于Nexus搭建Maven私服基础入门

什么是Nexus&#xff1f;它有什么优势&#xff1f; 要了解为什么需要nexus的存在&#xff0c;我们不妨从以下几个问题来简单了解一下: 为什么需要搭建私服&#xff1f;如果没有私服会出现什么问题&#xff1f; 对于企业开发而言&#xff0c;如果没有私服&#xff0c;我们所有…

浅析AI视频分析与视频管理系统EasyCVR平台及场景应用

人工智能的战略重要性导致对视频智能分析的需求不断增加。鉴于人工智能视觉技术的巨大潜力&#xff0c;人们的注意力正在从传统的视频监控转移到计算机视觉的监控过程自动化。 1、什么是视频分析&#xff1f; 视频分析或视频识别技术&#xff0c;是指从视频片段中提取有用信息…

自动驾驶学习笔记(十八)——Lidar感知

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo 社区开发者圆桌会》免费报名—>传送门 文章目录 前言 Lidar感知 运动补偿 点云分割 总结…

2021年数维杯国际大学生数学建模A题新冠肺炎背景下港口资源优化配置策略求解全过程文档及程序

2021年数维杯国际大学生数学建模 A题 新冠肺炎背景下港口资源优化配置策略 原题再现&#xff1a; 2020年初&#xff0c;新型冠状病毒&#xff08;COVID-19&#xff09;在全球迅速蔓延。根据世界卫生组织2021年7月31日的报告&#xff0c;新冠病毒疫情对人类的影响可能比原先预…

动手学深度学习-自然语言处理-预训练

词嵌入模型 将单词映射到实向量的技术称为词嵌入。 为什么独热向量不能表达词之间的相似性&#xff1f; 自监督的word2vec。 word2vec将每个词映射到一个固定长度的向量&#xff0c;这些向量能更好的表达不同词之间的相似性和类比关系。 word2vec分为两类&#xff0c;两类…

RT-DETR 图片目标计数 | 特定目标进行计数

全类别计数特定类别计数如何使用 RT-DETR 进行对象计数 有很多同学留言说想学 RT-DETR 目标计数。那么今天这篇博客,我将教大家如何使用 RT-DETR 进行对象计数。RT-DETR 是一种非常强大的对象检测模型,它可以识别图像中的各种对象。我们将学习如何利用这个模型对特定对象进行…

JAVA 异常分类及处理

JAVA 异常分类及处理 概念 如果某个方法不能按照正常的途径完成任务&#xff0c;就可以通过另一种路径退出方法。在这种情况下会抛出一个封装了错误信息的对象。此时&#xff0c;这个方法会立刻退出同时不返回任何值。另外&#xff0c;调用这个方法的其他代码也无法继续执行&…

如何查看Linux中glibc的Version

用ldd --version ldd --version 运行libc.so 你没有看错&#xff0c;libc.so是一个可执行程序。 但前提是你要找到它。因为它并不在PATH所包含的目录下。 ppdell:~$ ldd which cat | grep libclibc.so.6 > /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e6fb34000)ppdell:~…

【单调栈]LeetCode84: 柱状图中最大的矩形

作者推荐 【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数 本文涉及的知识点 单调栈 题目 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形…

【JVM从入门到实战】(七)运行时数据区的组成

运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域&#xff0c;称之为运行时数据区。 《Java虚拟机规范》中规定了每一部分的作用 线程不共享&#xff1a;程序计数器、虚拟机栈、本地方法栈 线程共享&#xff1a;方法区&#xff0c;堆 1. 程序计数器(Program Count…