Python+GEE遥感开发之计算遥感生态指数RSEI

  • 0 使用的遥感数据
  • 1 植被指数的计算(NDVI)
  • 2 湿度指数的计算(WET)
  • 2.1 MOD09A1计算WET(GEE代码)
  • 2.2 Landsat8计算Wet(Python代码)
  • 3 温度指数的计算(LST)
  • 3.1 MOD11A1获取LST(GEE代码)
  • 3.2 Landsat获取LST(Python代码)
  • 4 干度指数的计算(NDBISI)
  • 4.1 MOD09A1计算NDBISI(GEE代码)
  • 4.2 Landsat计算NDBISI(python代码)
  • 5 RSEI的计算(Python代码)



前言:遥感生态指数RSEI, 是一种使用卫星遥感影像数据通过反演计算得到的数据,可以用来对城市的生态状况进行快速监测和评价,该指数主要利用主成分分析方法,对植被指数、湿度、地表温度以及建筑指数四项指数指标进行集成。这个指数是由徐涵秋发明的,更多详细信息还请参考阅读:徐涵秋.城市遥感生态指数的创建及其应用[J].生态学报,2013,33(24):7853-7862.


0 使用的遥感数据

本篇博客主要介绍MODIS下计算RSEI的方法和Landsat计算RSEI的方式,两者的差别会在下面的指数计算中给出不同点。
其中MODIS计算RSEI主要用到了MOD09A1波段(1~7波段分别表示为:红光、近红外、蓝光、绿光、近红外2、短波红外1、短波红外2)的数据和MOD11A1下的LST数据。
Landsat计算RSEI主要用到了Landsat8 OLI波段数据。

1 植被指数的计算(NDVI)

植被指数主要推荐使用NDVI,NDVI的计算主要是用到了红外和近红外波段,可以使用MODIS或者Landsat的NDVI产品,也可以使用波段完成计算,这里给出一个使用GEE平台和MODIS下的MOD09A1产品计算年合成的NDVI,具体代码如下所示。当然也可以参考我的其它GEE计算NDVI的博客。

var geometry = ee.FeatureCollection('users/www1573979951/hkh_boundary');
Map.centerObject(geometry,6);
var mod09a1 = ee.ImageCollection('MODIS/061/MOD09A1').filterBounds(geometry);
print(mod09a1)
mod09a1 = mod09a1.map(function(img)
{   var ndvi = img.expression('((nir-red)/(nir+red))',
    {
        'red': img.select(['sur_refl_b01']).multiply(0.0001),
        'nir': img.select(['sur_refl_b02']).multiply(0.0001)
    })
    // Clip NDBSI values to the range [-1, 1] and mask out values outside this range
    var clipped_ndvi = ndvi.clip(geometry).updateMask(ndvi.gte(-1).and(ndvi.lte(1)));
    return img = img.addBands(clipped_ndvi.rename('NDVI'));
});

for(var i=2001;i<=2023;i++){
    var data_collection = mod09a1.filterDate(i+'-01-01',i+'-12-31').select('NDVI');
    var YR_collection = data_collection.mean().clip(geometry);
      Export.image.toDrive({
      image: YR_collection,
      description: i,
      fileNamePrefix: i,
      scale: 1000,
      crs: "EPSG:4326",
      region: geometry,
      maxPixels: 1e13,
      folder: 'Modis'
    })
}

2 湿度指数的计算(WET)

2.1 MOD09A1计算WET(GEE代码)

湿度的计算主要用到了MOD09A1产品的7个波段,主要参考CSDN博主“空中旋转篮球”给出的WET的计算方式。

RESI遥感指数 遥感指数计算_RESI遥感指数


MOD09A1计算WET的GEE代码如下:

var geometry = ee.FeatureCollection('users/www1573979951/hkh_boundary');
Map.centerObject(geometry,6);
var mod09a1 = ee.ImageCollection('MODIS/061/MOD09A1').filterBounds(geometry);
print(mod09a1)
mod09a1 = mod09a1.map(function(img)
{     // 湿度函数:Wet
  var Wet = img.expression('((0.1147 * red) + (0.2489 * infrared) + (0.2408 * blue) + (0.3132 * green) - (0.3122 * short_infrared) - (0.6416 * middle_infrared1) - (0.5087 * middle_infrared2))',{
       'red': img.select(['sur_refl_b01']).multiply(0.0001),
       'infrared': img.select(['sur_refl_b02']).multiply(0.0001),
       'blue': img.select(['sur_refl_b03']).multiply(0.0001),
       'green': img.select(['sur_refl_b04']).multiply(0.0001),
       'short_infrared': img.select(['sur_refl_b05']).multiply(0.0001),
       'middle_infrared1': img.select(['sur_refl_b06']).multiply(0.0001),
       'middle_infrared2': img.select(['sur_refl_b07']).multiply(0.0001)
     })   
     var clipped_wet = Wet.clip(geometry).updateMask(Wet.gte(-1).and(Wet.lte(1)));
     return  img.addBands(clipped_wet.rename('WET'))
});


for(var i=2001;i<=2023;i++){
    var data_collection = mod09a1.filterDate(i+'-01-01',i+'-12-31').select('WET');
    var YR_collection = data_collection.mean().clip(geometry);
      Export.image.toDrive({
      image: YR_collection,
      description: 'wet'+i,
      fileNamePrefix: 'wet'+i,
      scale: 1000,
      crs: "EPSG:4326",
      region: geometry,
      maxPixels: 1e13,
      folder: 'Modis'
    })
}

2.2 Landsat8计算Wet(Python代码)

计算方法来源于《基于Python计算Landsat8OLI遥感生态指数RSEI

def wet(blue, green, red, nir, swir1, swir2):
    """
    计算湿度 wet
    :param blue: landsat8的B2波段,蓝色波段blue
    :param green: landsat8的B3波段,绿色波段green
    :param red: landsat8的B4波段,红外波段red
    :param nir: landsat8的B5波段,近红外波段nir
    :param swir1: landsat8的B6波段,短波红外1 swir1
    :param swir2: landsat8的B7波段,短波红外2 swir2
    :return: wet 计算结果
    """
    blue = blue * 0.0001
    green = green * 0.0001
    red = red * 0.0001
    nir = nir * 0.0001
    swir1 = swir1 * 0.0001
    swir2 = swir2 * 0.0001
    wet = blue * 0.1511 + green * 0.1972 + red * 0.3283 + nir * 0.3407 + swir1 *    (-0.7117) + swir2 * (-0.4559)
    return wet

3 温度指数的计算(LST)

3.1 MOD11A1获取LST(GEE代码)

这里使用的是MOD11A1产品下的LST产品,取了Night和Day的平均值。

var geometry = ee.FeatureCollection('users/www1573979951/hkh_boundary');
Map.centerObject(geometry,6);
var lst = ee.ImageCollection('MODIS/061/MOD11A1').filterBounds(geometry);
print(lst)
lst = lst.map(function(img)
{   var lst_mean = img.expression('((Day+Night)/2)',
    {
      'Day': img.select(['LST_Day_1km']),
	  'Night': img.select(['LST_Night_1km'])
    })
    return img = img.addBands(lst_mean.rename('LST'));
});


for(var i=2001;i<=2023;i++){
    var data_collection = lst.filterDate(i+'-01-01',i+'-12-31').select('LST');
    data_collection = data_collection.map(function(img){
      var date = img.get('system:time_start');
      return img.multiply(0.02).subtract(273.15).set('system:time_start', date);
    });
    var YR_collection = data_collection.mean().clip(geometry);
      Export.image.toDrive({
      image: YR_collection,
      description: 'lst'+i,
      fileNamePrefix: 'lst'+i,
      scale: 1000,
      crs: "EPSG:4326",
      region: geometry,
      maxPixels: 1e13,
      folder: 'Modis'
    })
}

3.2 Landsat获取LST(Python代码)

计算方法来源于《基于Python计算Landsat8OLI遥感生态指数RSEI
landsat8的第10波段是热红外波段,可以用其辐射亮度结合植被覆盖度和比辐射率来计算热度LST。

def lst(tirs1, ndvi):
    """
    计算热度指数 lst
    :param tirs1: landsat8的B10波段,热红外波段1 tirs1
    :param ndvi: ndvi
    :return:
    """
    # 选择第Band10辐射亮度波段
    tirs1_fsld = tirs1 * 0.1
    # 计算植被覆盖度pv,假设最低值为0.05,最大值为0.95
    pv = np.power((ndvi-0.05) / (0.95-0.05), 2)
    # 计算比辐射率c
    c = 0.004 * pv + 0.986
    # 计算lst
    lst = (tirs1_fsld / (1 + (0.00109 * (tirs1_fsld / 1.438)) * np.log(c))) - 273.15
    return lst

4 干度指数的计算(NDBISI)

NDBSI的计算需要先算出裸土指数SI和建筑指数IBI,进而再求出NDBSI。

4.1 MOD09A1计算NDBISI(GEE代码)

NDBISI的计算方式同样来源于CSDN博主“空中旋转篮球”给出的方法。

RESI遥感指数 遥感指数计算_MOD09A1计算_02

var geometry = ee.FeatureCollection('users/www1573979951/hkh_boundary');
Map.centerObject(geometry,6);
var mod09a1 = ee.ImageCollection('MODIS/061/MOD09A1').filterBounds(geometry);

mod09a1 = mod09a1.map(function(img) {   
    // 干度函数:ndbsi = ( ibi + si ) / 2
    var ibi = img.expression('(((float(2*b5))/(float(b5+b4))-((float(b4))/(float(b4+b3))+(float(b2))/(float(b2+b5))))/((float(2*b5))/(float(b5+b4))+((float(b4))/(float(b4+b3))+(float(b2))/(float(b2+b5)))))', {
        'b2': img.select('sur_refl_b02').multiply(0.0001),
        'b3': img.select('sur_refl_b03').multiply(0.0001),
        'b4': img.select('sur_refl_b04').multiply(0.0001),
        'b5': img.select('sur_refl_b05').multiply(0.0001)
    });
  
    var si = img.expression('((float((b3+b5)-(b1+b4)))/((b3+b5)+(b1+b4)))', {
        'b1': img.select('sur_refl_b01').multiply(0.0001),
        'b2': img.select('sur_refl_b02').multiply(0.0001),
        'b3': img.select('sur_refl_b03').multiply(0.0001),
        'b4': img.select('sur_refl_b04').multiply(0.0001),
        'b5': img.select('sur_refl_b05').multiply(0.0001)
    });
  
    var ndbsi = (ibi.add(si)).divide(2);
  
    // Clip NDBSI values to the range [-1, 1] and mask out values outside this range
    var clipped_ndbsi = ndbsi.clip(geometry).updateMask(ndbsi.gte(-1).and(ndbsi.lte(1)));
    
    return img.addBands(clipped_ndbsi.rename('NDBISI'));
});

for(var i=2023; i<=2023; i++){
        var data_collection = mod09a1.filterDate(i+'-01-01', i+'-12-31').select('NDBISI');
        var YR_collection = data_collection.mean().clip(geometry);
      Export.image.toDrive({
      image: YR_collection,
      description: 'ndbisi'+i,
      fileNamePrefix: 'ndbisi'+i,
      scale: 1000,
      crs: "EPSG:4326",
      region: geometry,
      maxPixels: 1e13,
      folder: 'Modis'
    })
}

4.2 Landsat计算NDBISI(python代码)

计算方法来源于《基于Python计算Landsat8OLI遥感生态指数RSEI

def ndbsi(blue, green, red, nir, swir1):
    """
    计算干度 ndbsi
    :param blue: landsat8的B2波段,蓝色波段blue
    :param green: landsat8的B3波段,绿色波段green
    :param red: landsat8的B4波段,红外波段red
    :param nir: landsat8的B5波段,近红外波段nir
    :param swir1: landsat8的B6波段,短波红外1 swir1
    :return: ndbsi 计算结果
    """
    blue = blue * 0.0001
    green = green * 0.0001
    red = red * 0.0001
    nir = nir * 0.0001
    swir1 = swir1 * 0.0001
    # 计算裸土指数 si
    si = ((swir1 + red) - (nir + blue)) / ((swir1 + red) + (nir + blue))
    # 计算建筑指数 ibi
    ibi = (2 * swir1 / (swir1 + nir) - (nir / (nir + red) + green / (green + swir1))) / \
          (2 * swir1 / (swir1 + nir) + (nir / (nir + red) + green / (green + swir1)))
    # 计算干度
    ndbsi = (si + ibi) / 2
    return ndbsi

5 RSEI的计算(Python代码)

首先对NDVI、LST、WET、NDBISI数据的归一化,然后使用PCA进行主成分分析计算出RSEI指数。

import os
import numpy as np
from osgeo import gdal, gdalnumeric
from sklearn.decomposition import PCA

def normalize(array):
    """
    线性归一化
    :param array: 数组
    :return: 处理后的数组
    """
    # 线性归一化
    max = np.nanmax(array)
    min = np.nanmin(array)
    normalize_value = (array-min) / (max-min)
    return normalize_value


def rsei(ndvi, wet, ndbsi, lst):
    """
    PCA主成分分析并计算RSEI
    :param ndvi: ndvi
    :param wet: wet
    :param ndbsi: ndbsi
    :param lst: lst
    :return: rsei, pc_ratio(保留成分的方差百分比)
    """
    # 将四个指标合成一个多维数组,此时data的shape是(4, m, n),是一个三维数据
    data = np.array([wet, ndvi, lst, ndbsi])
    # 对数组进行掩膜,不让nan参与PCA
    panel_data = np.hstack([data[i, :, :].flatten().reshape(-1, 1) for i in range(data.shape[0])])
    mask = np.isnan(panel_data).sum(axis=1) > 0
    train_x = panel_data[~mask, :]
    # 主成分分析
    pca = PCA(n_components=1)
    pca_data = pca.fit_transform(train_x)
    # 保留成分的方差百分比(如果用不到可以不用返回,可以去掉)
    pc_ratio = pca.explained_variance_ratio_

    final_result = np.ones_like(mask) * 1.0
    final_result[~mask] = pca_data.flatten()
    final_result[mask] = np.NAN
    result_data = final_result.reshape(data.shape[1:])

    # 计算rsei
    rsei = 1 - result_data
    # 对rsei进行归一化
    rsei = normalize(rsei)
    return rsei, pc_ratio

def save_tif(data, file, output):
    ds = gdal.Open(file)
    shape = data.shape
    driver = gdal.GetDriverByName("GTiff")
    dataset = driver.Create(output, shape[1], shape[0], 1, gdal.GDT_Float32)  # 以float类型进行存储
    dataset.SetGeoTransform(ds.GetGeoTransform())
    dataset.SetProjection(ds.GetProjection())
    dataset.GetRasterBand(1).WriteArray(data)


def read_tif02(filepath):
    data = gdalnumeric.LoadFile(filepath)
    data = data.astype(np.float32)
    a = data[0][0]
    data[data == a] = np.nan
    return data

def get_data_list(file_path, out = ""):
    list1 = []  # 文件的完整路径
    if os.path.isdir(file_path):
        fileList = os.listdir(file_path)
        if out != "":
            for f in fileList:
                out_data = out + "\\" + f
                list1.append(out_data)
        else:
            for f in fileList:
                pre_data = file_path + '\\' + f  # 文件的完整路径
                list1.append(pre_data)
        return list1

if __name__ == '__main__':
    file_ndvi_list = get_data_list(r"E:\AAWORK\work\研究方向\rsei\data\月\ndvi01")
    file_lst_list = get_data_list(r"E:\AAWORK\work\研究方向\rsei\data\月\lst01")
    file_ndbisi_list = get_data_list(r"E:\AAWORK\work\研究方向\rsei\data\月\ndbisi01")
    file_wet_list = get_data_list(r"E:\AAWORK\work\研究方向\rsei\data\月\wet01")
    file_rsei_list = get_data_list(r"E:\AAWORK\work\研究方向\rsei\data\月\ndvi01",r"E:\AAWORK\work\研究方向\rsei\data\月\rsei01")
    for file_ndvi,file_lst,file_ndbisi,file_wet,file_rsei in zip(file_ndvi_list,file_lst_list,file_ndbisi_list,file_wet_list,file_rsei_list):
        ndvi_data = normalize(read_tif02(file_ndvi))
        lst_data = normalize(read_tif02(file_lst))
        ndbisi_data = normalize(read_tif02(file_ndbisi))
        wet_data = normalize(read_tif02(file_wet))
        print(file_ndvi)
        print(file_lst)
        print(file_ndbisi)
        print(file_wet)
        print(file_rsei)
        print("---------")
        rsei_data, pc_ratio = rsei(ndvi_data,wet_data,ndbisi_data,lst_data)
        save_tif(rsei_data,file_ndvi,file_rsei)