Processing MODIS remote sensing data based on Python

Preface

MODIS is a satellite remote sensing instrument that collects data globally every day at a resolution of 250-500 meters. Learn how to import, clean, and plot MODIS data in Python.

1. Introduction to MODIS images

The Moderate Resolution Imaging Spectroradiometer (MODIS) is a satellite-based instrument that continuously collects data on the Earth's surface. Currently, MODIS has the best temporal resolution of publicly available remote sensing data, covering the entire Earth every 24 hours.

MODIS collects data in 36 spectral bands; however, for this blog, you will only use the first 7 bands.

(1) MODIS surface reflectance (MOD09GA product)

There are many different MODIS data products. These are data sets that are processed for use in science. In this course, we are using the MOD09GA product, a reflectivity product that contains the first 7 bands of MODIS.

The normal range of surface reflectance values ​​is 0 to 1, where 1 is the brightest value and 0 is the darkest value. Surface reflectance is a measure of the spectral reflectance of the Earth's surface, as measured on the ground. You can think of it like what your eyes would see, except of course your eyes can't see light outside the visible part of the electromagnetic spectrum.

MODIS offers a number of standardized products, including the surface reflectance MOD09GA product you will use in this course. The MOD09GA product provides surface reflectance at a spatial resolution of 500m in the 7 spectral bands listed in the table above.

According to the Land Surface Reflectance Science Computing Facility , which created the MOD09 products , these products are estimates of the surface spectral reflectance for each band as it would be measured at the ground as if there were no atmospheric scattering or absorption. It corrects for the effects of atmospheric gases, aerosols and thin cirrus clouds.

(2) Band metadata of MOD09GA product

To better understand the MODIS data, view the detailed table for the MOD09GA product on page 14 of the MODIS User Guide.

Some forms are as follows:

Using this table for the MOD09GA product, answer the following questions:

  • What is the range of valid values ​​for our data?

  • What has no data value?

  • What is the scaling factor relevant to our data?

(3) Identify MODIS bands used for NBR calculations

In this blog, you will calculate NBR using MODIS data. However, even though you can calculate the same vegetation index using many different remote sensing products, remember that the bands of each remote sensing data are different.

Check out the table above, which shows the band range of MODIS sensors. Recall that the NBR index applies to any multispectral sensor with a NIR band between 760 - 900 nm and a SWIR band between 2080 - 2350 nm.

2. Open the MODIS image

You will learn how to open MODIS data using pre-fire MODIS imagery from the Cold Springs Fire Study Area in Colorado.

Before starting, import the following packages and make sure the working directory is set.

from glob import glob
import os

import numpy as np
import numpy.ma as ma
import matplotlib.pyplot as plt
import geopandas as gpd
import rioxarray as rxr
import xarray as xr
from rasterio.plot import plotting_extent
import earthpy as et
import earthpy.spatial as es
import earthpy.plot as ep
from shapely.geometry import box

data = et.data.get_data('cold-springs-fire')
os.chdir(os.path.join(et.io.HOME, 'earth-analytics', 'data'))
Downloading from https://ndownloader.figshare.com/files/10960109
Extracted output to /root/earth-analytics/data/cold-springs-fire/.

In the previous lesson, you used glob("*keyword*.tif") to create a list of all files that simultaneously:

  • Contains specific keywords represented by an asterisk (e.g. *band*) and

  • Contains the extension .tif.

First create a list of MODIS surface reflection rasters using glob using the keyword and extension *sur_refl_b*.tif.

# Create list of MODIS rasters for surface reflectance
modis_bands_pre_list = glob(os.path.join("cold-springs-fire",
                                         "modis",
                                         "reflectance",
                                         "07_july_2016",
                                         "crop",
                                         "*_sur_refl_b*.tif"))

# Sort the list of bands
modis_bands_pre_list.sort()
modis_bands_pre_list
['cold-springs-fire/modis/reflectance/07_july_2016/crop/MOD09GA.A2016189.h09v05.006.2016191073856_sur_refl_b01_1.tif',
 'cold-springs-fire/modis/reflectance/07_july_2016/crop/MOD09GA.A2016189.h09v05.006.2016191073856_sur_refl_b02_1.tif',
 'cold-springs-fire/modis/reflectance/07_july_2016/crop/MOD09GA.A2016189.h09v05.006.2016191073856_sur_refl_b03_1.tif',
 'cold-springs-fire/modis/reflectance/07_july_2016/crop/MOD09GA.A2016189.h09v05.006.2016191073856_sur_refl_b04_1.tif',
 'cold-springs-fire/modis/reflectance/07_july_2016/crop/MOD09GA.A2016189.h09v05.006.2016191073856_sur_refl_b05_1.tif',
 'cold-springs-fire/modis/reflectance/07_july_2016/crop/MOD09GA.A2016189.h09v05.006.2016191073856_sur_refl_b06_1.tif',
 'cold-springs-fire/modis/reflectance/07_july_2016/crop/MOD09GA.A2016189.h09v05.006.2016191073856_sur_refl_b07
def combine_tifs(tif_list):
    """A function that combines a list of tifs in the same CRS
    and of the same extent into an xarray object

    Parameters
    ----------
    tif_list : list
        A list of paths to the tif files that you wish to combine.

    Returns
    -------
    An xarray object with all of the tif files in the listmerged into 
    a single object.

    """

    out_xr = []
    for i, tif_path in enumerate(tif_list):
        out_xr.append(rxr.open_rasterio(tif_path, masked=True).squeeze())
        out_xr[i]["band"] = i+1

    return xr.concat(out_xr, dim="band")

Next, use the function combine_tifs to create a rioxarray object from the list of bands you created using glob. You can then import the MODIS bands and create an RGB map.

# Open file list with function

modis_bands_pre = combine_tifs(modis_bands_pre_list)
# Plot MODIS RGB

ep.plot_rgb(modis_bands_pre.values,
            rgb=[0, 3, 2],
            title="Surface Reflectance \n MODIS RGB Bands")
plt.show()

MODIS surface reflectance using RGB bands for the pre-cold seep fire time period.

3. Check the value of data

To start exploring your data, you can calculate the minimum and maximum values ​​of a selected band to see the range of values. For example, you can calculate these values ​​for the first band (red) of the MODIS stack.

# Identify minimum and maximum values of band 1 (red)
print(modis_bands_pre[1].min(), modis_bands_pre[1].max())
<xarray.DataArray ()>
array(-100., dtype=float32)
Coordinates:
    band         int64 2
    spatial_ref  int64 0 <xarray.DataArray ()>
array(10039., dtype=float32)
Coordinates:
    band         int64 2
    spatial_ref  int64 0

The data opening is great! However, you also need to trim the data for the job. You can do this in two easy steps.

  • Reproject the fire boundaries so they are in the same CRS as your MODIS data

  • MODIS data is clipped using fire bounds and the xarray_name.rio.clip() function. To use this function with a GeoPandas object, you must call the geometry column of the geopandas object.

xarray_name.rio.clip(crop_bound.geometry)

但是,此方法会将场景剪裁成矢量形状的确切轮廓。 这可能很有用,但通常您会希望剪切覆盖矢量形状边界的矩形,而不是精确的形状。 为此,您可以使用从 shapely.geometry 导入的 box 函数在要剪切的几何体周围创建一个框。

xarray_name.rio.clip([box(*crop_bound.total_bounds)])

您可以在下面看到这两种方法剪辑场景的不同之处!

# Open fire boundary
fire_boundary_path = os.path.join("cold-springs-fire",
                                  "vector_layers",
                                  "fire-boundary-geomac",
                                  "co_cold_springs_20160711_2200_dd83.shp")
fire_boundary = gpd.read_file(fire_boundary_path)
fire_bound_sin = fire_boundary.to_crs(modis_bands_pre.rio.crs)

fire_bound_box = [box(*fire_bound_sin.total_bounds)]


# MODIS Clipped to Geometry
modis_clip_geometry = modis_bands_pre.rio.clip(fire_bound_sin.geometry,
                                               all_touched=True,
                                               from_disk=True)

# MODIS Clipped to Bounds
modis_clip = modis_bands_pre.rio.clip(fire_bound_box,
                                      all_touched=True,
                                      from_disk=True)

extent = plotting_extent(modis_clip[0].values, modis_clip.rio.transform())

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 15))

# Plotting Geometry Clip
ep.plot_rgb(modis_clip.values,
            rgb=[0, 3, 2],
            ax=ax1,
            extent=extent,
            title='MODIS Clipped to Bounds')

fire_bound_sin.boundary.plot(ax=ax1)

# Plotting Bounds Clip
ep.plot_rgb(modis_clip_geometry.values,
            rgb=[0, 3, 2],
            ax=ax2,
            extent=extent,
            title='MODIS Clipped to Geometry')

fire_bound_sin.boundary.plot(ax=ax2)

plt.show()

出于 NDVI 和 NBR 分析的目的,我们对周围区域以及直接位于几何体内部的像素感兴趣,因此对于此分析,我们将使用边界方法。

要探索裁剪后的数据,请回想一下,您还可以使用 earthpy 中的 plot_bands 函数来创建每个波段的图。

# Create a list of titles
titles = ["Red Band", "Near Infrared (NIR) Band", "Blue/Green Band", "Green Band",
          "Near Infrared (NIR) Band", "Mid-infrared Band", "Mid-infrared Band"]

# Plot all bands individually
ep.plot_bands(modis_clip,
              cols=3,
              title=titles,
              figsize=(10, 6))
plt.show()

来自 MODIS 的表面反射率裁剪图像,适用于冷泉前火灾的所有波段。

使用 RGB 波段的 MODIS 裁剪表面反射率,用于预冷泉火灾。

# Create a colors and titles list to use in the histogram
colors = ['r', 'k', 'b', 'g', 'k', 'y', 'y']
titles = ["Red Band", "Near Infrared (NIR) Band", "Blue/Green Band",
          "Green Band", "Near Infrared (NIR) Band",
          "Mid-infrared Band", "Mid-infrared Band"]

# Plot histogram
ep.hist(modis_clip.values,
        colors=colors,
        title=titles,
        cols=2)
plt.show()

来自 MODIS 的裁剪表面反射率直方图,用于预冷泉火灾的所有波段。

从直方图中,您可以看到表面反射率的值范围似乎更合适,但仍不在 0 和 1 之间。

4、MODIS 影像中的反射率值

如博客前面所述,反射率值的正常范围是 0 到 1,其中 1 是最亮的值,0 是最暗的值。

再次查看上面为波段 1 计算的最小值和最大值。您注意到什么?

如您所见,最小值和最大值大大超出 0 到 1 的预期范围。查看波段 1 的直方图,您还可以看到值的范围不是您所期望的。

是什么原因造成的? 要回答这个问题,您需要先更好地理解数据,然后才能更多地使用它。

(1)比例因子

使用遥感数据时,比例因子很常见。 数据很大,比例因子用于保持数据较小。 例如,存储带小数的数字(称为浮点数)比存储整数需要更多的空间。 因此,通常删除传感数据应用了一个比例因子,可用于

查看 MOD09GA 产品的表格,您可以看到 MODIS 数据的比例因子为 0.0001。 这意味着您应该将每一层乘以该值以获得数据的实际反射率值。

您可以使用 numpy 数组数学(有时在 GIS 工具中称为栅格数学)将此比例因子值应用于堆栈中的所有图层。 此处将整个数组乘以 .0001 以缩放每个图层或波段。

# Scale values of MODIS imagery stack
modis_bands_pre_scaled = modis_clip * 0.0001
# Identify minimum and maximum values of scaled band 1 (red)

print(modis_bands_pre_scaled[1].min(), modis_bands_pre_scaled[1].max())
<xarray.DataArray ()>
array(0.2496, dtype=float32)
Coordinates:
    band         int64 2
    spatial_ref  int64 0 <xarray.DataArray ()>
array(0.3013, dtype=float32)
Coordinates:
    band         int64 2
    spatial_ref  int64 0
# Create a colors and titles list to use in the histogram
colors = ['r', 'k', 'b', 'g', 'k', 'y', 'y']
titles = ["Red Band", "Near Infrared (NIR) Band", "Blue/Green Band", "Green Band",
          "Near Infrared (NIR) Band", "Mid-infrared Band", "Mid-infrared Band"]

# Plot histogram
ep.hist(modis_bands_pre_scaled.values,
        colors=colors,
        title=titles,
        cols=2)
plt.show()

来自 MODIS 的裁剪和缩放表面反射率的直方图,用于预冷泉火灾的所有波段。

Guess you like

Origin blog.csdn.net/u010329292/article/details/129268849