基于python和google earth engine的MODIS数据质量控制————以MOD11A1为例

MODIS质量控制文件,对MODIS产品进行提取

MODIS数据简介

我们拿到的MODIS数据,多数人认为只要有值的地方,就是准确数据,我们直接就可以拿来使用,只有空值的区域,数据才会异常(多数本科生是这样认为的);然而并非如此,往往一个MODIS产品一个像元处,只有当所有输入的反演参数都为异常值时,这个像元才会被设置为异常,即设置为空值。 因此,我们所能看到的拥有像元值的地方,就会因为输入的反演参数都为异常程度,会有不同的质量。MODIS数据的生产商,也考虑到了数据生产过程中的数据异常情况,为了让客户能够更好的使用数据,为此提供了质量空值文件(Qc,Qa)。这些信息的进一步了解,可以查看官方提供的pdf文档,如 MOD11_User_Guide_V6.pdf. 质量空值文件多以二进制形式进行编码,并且将多个数据图层的质量控制参数,编写在HDF文件的一个数据集中.本文章以MOD11A1陆表温度日产品为例,使用python读取二进制文件数据,以掩膜的形式将满足要求的栅格值,提取到一个新的TIF文件中,供后续进一步使用。下图分别为一个HDF数据的图层(数据集)分布和QC_Day白天质量控制文件。

MODIS HDF 文件数据集分布
QC的二进制解译

每一个产品像元对应一个质量控制图层的像元.每个质量控制像元包含一个8位的整型数值,我们需要将其转化位二进制数值,才能进行读取\解译.下图为解译的示意图:

字节的二进制解译

如图所示,从左开始0\1位代表Mandatory QA flags,2\3位代表Data quality flag,4\5位为Emis Error flag,6\7位代表LST LST Error flag。(在python代码中,0位在最左边)。基于上面的理论,我们使用python读取QC_Day的tif文件(由于前期涉及到镶嵌\投影等步骤,所以使用MRT软件,将HDF数据图层,转化为TIF文件,然后再使用python代码进行批量处理。)

python 代码

下面贴上代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""

    本代码,将质量控制文件,进行提取,将满足要求的保存为tif。
    质量 LST error flag  <= 1k

@version: Anaconda
@author: LeYongkang
@contact: [email protected]
@software: PyCharm
@file: 12_CombineLst_PredictedAndModis.py
@time: 2020/2/5 0005 下午 11:10
"""
import numpy as np
from osgeo import gdal
import os
import pandas as pd

def opentif(filepath):
    """
        输入文件名,返回数组、宽度、高度、仿射矩阵信息、投影信息
    :param filepath: 文件的完整路径
    :return: im_data,im_width,im_height,im_geotrans,im_proj
    """
    dataset = gdal.Open(filepath)
    im_width = dataset.RasterXSize #栅格矩阵的列数
    im_height = dataset.RasterYSize #栅格矩阵的行数
    data = dataset.ReadAsArray(0,0,im_width,im_height)#获取数据
    # data = dataset.ReadAsArray()  # 获取数据
    im_data = np.array(data)
    print("opentif的shape")
    print(im_data.shape)

    im_geotrans = dataset.GetGeoTransform()#获取仿射矩阵信息
    im_proj = dataset.GetProjection()#获取投影信息
    return(im_data,im_width,im_height,im_geotrans,im_proj)

def savetif(dataset,path,im_width,im_height,im_geotrans,im_proj):
    """
        将数组保存为tif文件
    :param dataset: 需要保存的数组
    :param path: 需要保存出去的路径,包含文件名
    :param im_width: 数组宽度
    :param im_height: 数组宽度
    :param im_geotrans: 仿射矩阵信息
    :param im_proj: 投影信息
    :return:
    """
    print(dataset)
    driver = gdal.GetDriverByName("GTiff")
    outdataset = driver.Create(path, im_width, im_height, 1, gdal.GDT_Float32)
    print(path)
    outdataset.SetGeoTransform(im_geotrans)  # 写入仿射变换参数
    outdataset.SetProjection(im_proj)  # 写入投影
    outdataset.GetRasterBand(1).WriteArray(dataset)
    outdataset.GetRasterBand(1).SetNoDataValue(0)
    print("yes")

if __name__ == "__main__":
    inDir = r"I:\2018_Parper2\MYD_2018\MYD11A1\.GeoTif_Mosaic\.Tif"
    # inFile = "MOD11A1.A2018001.QC_Day.tif"
    Out_Dir = r"I:\2018_Parper2\MYD_2018\MYD11A1\.GeoTif_Mosaic\.Tif_Masked"
    # 获取LST质量控制文件的列表
    InList_Qc = [infile for infile in os.listdir(inDir) if infile.endswith(".QC_Day.tif")]

    for InFile in InList_Qc:
        ##################################################################################################
        if not os.path.exists(Out_Dir +
                              os.sep + InFile[:-10] + "LST_Day_1km.tif"):
        ##################################################################################################

            # 获取完整路径
            in_Full_Dir = inDir + os.sep + InFile
            # 打开TIF文件,获取TIF文件的信息
            InData = opentif(in_Full_Dir)
            in_Array = InData[0]
            in_Array= np.array(in_Array,dtype = np.uint8)

            print(in_Array)
            print("   ")
            # 将十进制转回到二进制
            binary_repr_v = np.vectorize(np.binary_repr)
            new = binary_repr_v(in_Array, 8)
            print(new)
            # 6-7位,是控制LST质量的字段,‘00’代表 LST error flag  <= 1k
            # start=0,end=2:代表 LST LST Error flag
            # start=2,end=4:代表 Emis Error flag
            # start=4,end=6:代表Data quality flag
            # start=6,end=8:代表Mandatory QA flags
            Error_mask = np.char.count(new,'00',start=0,end=2) == 1
            print(Error_mask)

            # 打开LST文件,获取文件名
            # G:\2018\MODIS\MOD11A1\.GeoTif_Mosaic_10000\Tif\MOD11A1.A2018002.QC_Day.tif         质量控制文件
            # G:\2018\MODIS\MOD11A1\.GeoTif_Mosaic_10000\Tif\MOD11A1.A2018002.LST_Day_1km.tif    LST文件
            in_Full_Dir_Lst = in_Full_Dir[:-10] + "LST_Day_1km.tif"
            Lst_Array = opentif(in_Full_Dir_Lst)[0]
            # 将满足质量条件的提取出来,不满足条件的设置为0,后续设置为nodata
            Out_Lst_Array = np.where(Error_mask,Lst_Array,0)
            print(Out_Lst_Array)

            print(in_Full_Dir_Lst.split("\\")[-1])
            # 将masked后的LST保存,将 0 设置为SetNoDataValue()
            if not os.path.exists(Out_Dir + os.sep + in_Full_Dir_Lst.split("\\")[-1]):
                print(os.path.exists(Out_Dir + os.sep + in_Full_Dir_Lst.split("\\")[-1]))
                savetif(Out_Lst_Array,
                        Out_Dir + os.sep + in_Full_Dir_Lst.split("\\")[-1],
                        InData[1],InData[2],InData[3],InData[4])

代码运行结果

运行结果展示
上图为运行结果展示,彩色为 Lst error flag <= 1k,底图为未使用代码提取的所有LST像元点。

官方也提供了基于arcgis的python工具箱方法(arcgis-modis-python-toolbox-v1.0)\LDOPE-1.7软件,但是本人短时间也没搞明白批量处理. 此博客,为本人第一次编写,若有错误不妥之处,还望批评指正。此外,本人较多使用python对地理数据进行处理,对地理模块相对熟悉,大家可以联系我,一起学习。

对于待定像元如何处理

名为“Naga Climber”的粉丝把这个图片发给我了。其实处理起来也很简单,谁需要的时候可以加我qq,上面代码里面有我的联系方式。我不是要私聊收费啊,就是我会写,但是我没有应用需求,懒得写,谁需要我就给他写个。
在这里插入图片描述

更新的Google Earth Engine 代码

var geometry: FeatureCollection (1 element)

//就是这里,导出的格式写为SHP
Export.table.toDrive({
  collection:geometry,
  description: "geometry_studyArea_parper",
  fileNamePrefix: "geometry",
  fileFormat: "SHP"
});

  

// Helper function to extract the values from specific bits
// The input parameter can be a ee.Number() or ee.Image()
// Code adapted from https://gis.stackexchange.com/a/349401/5160
var bitwiseExtract = function(input, fromBit, toBit) {
  var maskSize = ee.Number(1).add(toBit).subtract(fromBit)
  var mask = ee.Number(1).leftShift(maskSize).subtract(1)
  return input.rightShift(fromBit).bitwiseAnd(mask)
}
 
 
var dataset = ee.ImageCollection('MODIS/061/MYD11A1')
                  .filter(ee.Filter.date('2020-02-01', '2020-02-05'));
                  
var landSurfaceTemperature = dataset.select('LST_Day_1km','QC_Day');
                  

var bitwiseExtract = function(input, fromBit, toBit) {
  var maskSize = ee.Number(1).add(toBit).subtract(fromBit);
  var mask = ee.Number(1).leftShift(maskSize).subtract(1);
  return input.rightShift(fromBit).bitwiseAnd(mask);
};


var qcControlForMod11a1 = function(qcDayLayer, lstDayLayer){
  var lstMasked = function(image){
    var qcDay = image.select(qcDayLayer);
    var lstDay = image.select(lstDayLayer);
    var qaMask = bitwiseExtract(qcDay, 0, 1).lte(1);
    var dataQualityMask = bitwiseExtract(qcDay, 2, 3).eq(0);
    var lstErrorMask = bitwiseExtract(qcDay, 6, 7).eq(0);
    var mask = qaMask.and(dataQualityMask).and(lstErrorMask);
    return lstDay.updateMask(mask);
  };
  return lstMasked; // this is very important!!!
};

var mod11a1WithQc = landSurfaceTemperature.map(qcControlForMod11a1('QC_Day', 'LST_Day_1km'));


var vis = {
  min: 13000.0,
  max: 16500.0,
  palette: [
    '040274', '040281', '0502a3', '0502b8', '0502ce', '0502e6',
    '0602ff', '235cb1', '307ef3', '269db1', '30c8e2', '32d3ef',
    '3be285', '3ff38f', '86e26f', '3ae237', 'b5e22e', 'd6e21f',
    'fff705', 'ffd611', 'ffb613', 'ff8b13', 'ff6e08', 'ff500d',
    'ff0000', 'de0101', 'c21301', 'a71001', '911003'
  ],
};

Map.addLayer(landSurfaceTemperature.select('LST_Day_1km').first().clip(geometry), vis, 'Original_1');

Map.addLayer(mod11a1WithQc.select('LST_Day_1km').first().clip(geometry), vis, 'After QC_1');

Map.addLayer(ee.Image(landSurfaceTemperature.select('LST_Day_1km').toList(landSurfaceTemperature.size()).get(1))
  .clip(geometry), vis, 'Original_2');

Map.addLayer(ee.Image(mod11a1WithQc.select('LST_Day_1km').toList(landSurfaceTemperature.size()).get(1))
  .clip(geometry), vis, 'After QC_2');


print(landSurfaceTemperature.size());


var list = mod11a1WithQc.select('LST_Day_1km').toList(landSurfaceTemperature.size()); 


for (var i=0; i<367; i++){
  
  var image = ee.Image(list.get(i));
  var id1 = image.get("system:index");
  
  // var stringALB = ee.String('ERA5_LAND_01Dgre_mm_HB_Daily_Prec_');
  // var string1 = ee.String(id1);
  // var nameOut = (stringALB.cat(string1)).getInfo();
  
  var nameOut = 'MYD11A1_'+  // 导出数据
  Export.image.toDrive({
    image: out.unmask(-9999),
    description: nameOut, //数据名
    folder:'MYD11A1', //存储文件夹
    scale: 1000, //分辨率
    region:geometry,
    maxPixels:1e13,
  });id1.getInfo()+'_LST_Day_1km';
  
  print(nameOut);
  
  // var nameOut = 'ERA5_LAND_01Dgre_K_HB_Daily_Temp_' +  id1.getInfo();
  
  var out =image.clip(geometry)//.float().multiply(0.02);
  
  // // print(out);
  
  // 导出数据
  Export.image.toDrive({
    image: out.unmask(-9999),
    description: nameOut, //数据名
    folder:'MYD11A1', //存储文件夹
    scale: 1000, //分辨率
    region:geometry,
    maxPixels:1e13,
  });
  
}

代码运行结果

基于python和google earth engine的MODIS数据质量控制————以MOD11A1为例_第1张图片

你可能感兴趣的:(Python,GEE,python)