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-30Did 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.