Indices
Contents
Indices#
Indices calculated from multispectral satellite imagery are powerful ways to
quantitatively analyze these data.
They take advantage of different spectral properties of materials to
differentiate between them.
Many of these indices can be calculated with simple arithmetic operations.
So now that our data are in xarray.Dataset
’s it’s fairly easy to
calculate them.
NDVI#
As an example, let’s load two example scenes from the Brumadinho tailings dam disaster:
import xlandsat as xls
import matplotlib.pyplot as plt
path_before = xls.datasets.fetch_brumadinho_before()
path_after = xls.datasets.fetch_brumadinho_after()
before = xls.load_scene(path_before)
after = xls.load_scene(path_after)
We can calculate the NDVI for these scenes to see if we can isolate the effect of the flood following the dam collapse:
before = before.assign(
ndvi=(before.nir - before.red) / (before.nir + before.red),
)
after = after.assign(
ndvi=(after.nir - after.red) / (after.nir + after.red),
)
# Set some metadata for xarray to find
before.ndvi.attrs["long_name"] = "normalized difference vegetation index"
before.ndvi.attrs["units"] = "dimensionless"
after.ndvi.attrs["long_name"] = "normalized difference vegetation index"
after.ndvi.attrs["units"] = "dimensionless"
after
<xarray.Dataset> Dimensions: (easting: 400, northing: 300) Coordinates: * easting (easting) float64 5.844e+05 5.844e+05 ... 5.963e+05 5.964e+05 * northing (northing) float64 -2.232e+06 -2.232e+06 ... -2.223e+06 -2.223e+06 Data variables: blue (northing, easting) float16 0.0686 0.07043 ... 0.05823 0.0564 green (northing, easting) float16 0.1027 0.09839 ... 0.07593 0.07043 red (northing, easting) float16 0.09778 0.09778 ... 0.06799 0.06177 nir (northing, easting) float16 0.2988 0.2715 0.2881 ... 0.2637 0.251 swir1 (northing, easting) float16 0.2311 0.2274 0.2316 ... 0.1608 0.142 swir2 (northing, easting) float16 0.145 0.1442 0.144 ... 0.09961 0.08655 ndvi (northing, easting) float16 0.5073 0.4705 0.4907 ... 0.5903 0.605 Attributes: (12/19) Conventions: CF-1.8 title: Landsat 8 scene from 2019-01-30 (path/row=218... digital_object_identifier: https://doi.org/10.5066/P9OGBGM6 origin: Image courtesy of the U.S. Geological Survey landsat_product_id: LC08_L2SP_218074_20190130_20200829_02_T1 processing_level: L2SP ... ... ellipsoid: WGS84 date_acquired: 2019-01-30 scene_center_time: 12:57:09.1851220Z wrs_path: 218 wrs_row: 74 mtl_file: GROUP = LANDSAT_METADATA_FILE\n GROUP = PROD...
And now we can make pseudo-color plots of the NDVI:
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 12))
# Limit the scale to [-1, +1] so the plots are easier to compare
before.ndvi.plot(ax=ax1, vmin=-1, vmax=1, cmap="RdBu_r")
after.ndvi.plot(ax=ax2, vmin=-1, vmax=1, cmap="RdBu_r")
ax1.set_title(f"Before: {before.attrs['title']}")
ax2.set_title(f"After: {after.attrs['title']}")
ax1.set_aspect("equal")
ax2.set_aspect("equal")
plt.show()

Finally, we can calculate the change in NDVI from one scene to the other by taking the difference:
ndvi_change = before.ndvi - after.ndvi
ndvi_change.name = "ndvi_change"
ndvi_change.attrs["long_name"] = (
f"NDVI change between {before.attrs['date_acquired']} and "
f"{after.attrs['date_acquired']}"
)
ndvi_change
<xarray.DataArray 'ndvi_change' (northing: 300, easting: 370)> array([[ 0.05908 , 0.06323 , 0.0542 , ..., 0.004395, -0.009766, 0.07324 ], [ 0.0498 , 0.07764 , 0.07495 , ..., 0.0957 , 0.012695, 0.003906], [-0.010254, 0.11743 , 0.0747 , ..., 0.03125 , 0.01807 , 0.04004 ], ..., [-0.000977, 0.01123 , 0.000977, ..., 0.00928 , 0.01367 , 0.00708 ], [ 0.00879 , 0.02344 , 0.01318 , ..., 0.006836, 0.00586 , 0.001221], [-0.01221 , 0.02637 , 0.006836, ..., 0.01343 , 0.01221 , 0.0105 ]], dtype=float16) Coordinates: * easting (easting) float64 5.844e+05 5.844e+05 ... 5.954e+05 5.955e+05 * northing (northing) float64 -2.232e+06 -2.232e+06 ... -2.223e+06 -2.223e+06 Attributes: long_name: NDVI change between 2019-01-14 and 2019-01-30
Did you notice?
The keen-eyed among you may have noticed that the number of points along
the "easting"
dimension has decreased. This is because xarray
only makes the calculations for pixels where the two scenes coincide. In
this case, there was an East-West shift between scenes but our calculations
take that into account.
Now lets plot it:
fig, ax = plt.subplots(1, 1, figsize=(10, 6))
ndvi_change.plot(ax=ax, vmin=-1, vmax=1, cmap="PuOr")
ax.set_aspect("equal")
plt.show()

There’s some noise in the cloudy areas of both scenes in the northeast but otherwise this plots highlights the area affected by flooding from the dam collapse in bright red at the center.