The Grizzly Creek Fire, Colorado, USA¶

In [4]:
# Install the development version of the earthpy package
!pip install git+https://github.com/earthlab/earthpy@apppears
Collecting git+https://github.com/earthlab/earthpy@apppears
  Cloning https://github.com/earthlab/earthpy (to revision apppears) to /tmp/pip-req-build-co0_z09e
  Running command git clone --filter=blob:none --quiet https://github.com/earthlab/earthpy /tmp/pip-req-build-co0_z09e
  Running command git checkout -b apppears --track origin/apppears
  Switched to a new branch 'apppears'
  Branch 'apppears' set up to track remote branch 'apppears' from 'origin'.
  Resolved https://github.com/earthlab/earthpy to commit 7241165d59af510d62bba312e48c7f513bc9dc05
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Requirement already satisfied: geopandas in /opt/conda/lib/python3.11/site-packages (from earthpy==0.10.0) (0.14.2)
Requirement already satisfied: matplotlib>=2.0.0 in /opt/conda/lib/python3.11/site-packages (from earthpy==0.10.0) (3.8.4)
Requirement already satisfied: numpy>=1.14.0 in /opt/conda/lib/python3.11/site-packages (from earthpy==0.10.0) (1.24.3)
Requirement already satisfied: rasterio in /opt/conda/lib/python3.11/site-packages (from earthpy==0.10.0) (1.3.9)
Requirement already satisfied: scikit-image in /opt/conda/lib/python3.11/site-packages (from earthpy==0.10.0) (0.22.0)
Requirement already satisfied: requests in /opt/conda/lib/python3.11/site-packages (from earthpy==0.10.0) (2.31.0)
Requirement already satisfied: keyring in /opt/conda/lib/python3.11/site-packages (from earthpy==0.10.0) (25.1.0)
Requirement already satisfied: contourpy>=1.0.1 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (1.2.0)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (0.11.0)
Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (4.51.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (1.4.4)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (24.0)
Requirement already satisfied: pillow>=8 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (10.3.0)
Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (3.0.9)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=2.0.0->earthpy==0.10.0) (2.9.0)
Requirement already satisfied: fiona>=1.8.21 in /opt/conda/lib/python3.11/site-packages (from geopandas->earthpy==0.10.0) (1.9.6)
Requirement already satisfied: pandas>=1.4.0 in /opt/conda/lib/python3.11/site-packages (from geopandas->earthpy==0.10.0) (2.2.1)
Requirement already satisfied: pyproj>=3.3.0 in /opt/conda/lib/python3.11/site-packages (from geopandas->earthpy==0.10.0) (3.6.1)
Requirement already satisfied: shapely>=1.8.0 in /opt/conda/lib/python3.11/site-packages (from geopandas->earthpy==0.10.0) (2.0.4)
Requirement already satisfied: jaraco.classes in /opt/conda/lib/python3.11/site-packages (from keyring->earthpy==0.10.0) (3.4.0)
Requirement already satisfied: jaraco.functools in /opt/conda/lib/python3.11/site-packages (from keyring->earthpy==0.10.0) (4.0.1)
Requirement already satisfied: jaraco.context in /opt/conda/lib/python3.11/site-packages (from keyring->earthpy==0.10.0) (5.3.0)
Requirement already satisfied: importlib-metadata>=4.11.4 in /opt/conda/lib/python3.11/site-packages (from keyring->earthpy==0.10.0) (7.1.0)
Requirement already satisfied: SecretStorage>=3.2 in /opt/conda/lib/python3.11/site-packages (from keyring->earthpy==0.10.0) (3.3.3)
Requirement already satisfied: jeepney>=0.4.2 in /opt/conda/lib/python3.11/site-packages (from keyring->earthpy==0.10.0) (0.8.0)
Requirement already satisfied: affine in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (2.3.0)
Requirement already satisfied: attrs in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (23.2.0)
Requirement already satisfied: certifi in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (2024.2.2)
Requirement already satisfied: click>=4.0 in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (8.1.7)
Requirement already satisfied: cligj>=0.5 in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (0.7.2)
Requirement already satisfied: snuggs>=1.4.1 in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (1.4.7)
Requirement already satisfied: click-plugins in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (1.1.1)
Requirement already satisfied: setuptools in /opt/conda/lib/python3.11/site-packages (from rasterio->earthpy==0.10.0) (69.5.1)
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/conda/lib/python3.11/site-packages (from requests->earthpy==0.10.0) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.11/site-packages (from requests->earthpy==0.10.0) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/conda/lib/python3.11/site-packages (from requests->earthpy==0.10.0) (2.2.1)
Requirement already satisfied: scipy>=1.8 in /opt/conda/lib/python3.11/site-packages (from scikit-image->earthpy==0.10.0) (1.12.0)
Requirement already satisfied: networkx>=2.8 in /opt/conda/lib/python3.11/site-packages (from scikit-image->earthpy==0.10.0) (3.1)
Requirement already satisfied: imageio>=2.27 in /opt/conda/lib/python3.11/site-packages (from scikit-image->earthpy==0.10.0) (2.33.1)
Requirement already satisfied: tifffile>=2022.8.12 in /opt/conda/lib/python3.11/site-packages (from scikit-image->earthpy==0.10.0) (2023.4.12)
Requirement already satisfied: lazy_loader>=0.3 in /opt/conda/lib/python3.11/site-packages (from scikit-image->earthpy==0.10.0) (0.3)
Requirement already satisfied: six in /opt/conda/lib/python3.11/site-packages (from fiona>=1.8.21->geopandas->earthpy==0.10.0) (1.16.0)
Requirement already satisfied: zipp>=0.5 in /opt/conda/lib/python3.11/site-packages (from importlib-metadata>=4.11.4->keyring->earthpy==0.10.0) (3.17.0)
Requirement already satisfied: pytz>=2020.1 in /opt/conda/lib/python3.11/site-packages (from pandas>=1.4.0->geopandas->earthpy==0.10.0) (2024.1)
Requirement already satisfied: tzdata>=2022.7 in /opt/conda/lib/python3.11/site-packages (from pandas>=1.4.0->geopandas->earthpy==0.10.0) (2023.3)
Requirement already satisfied: cryptography>=2.0 in /opt/conda/lib/python3.11/site-packages (from SecretStorage>=3.2->keyring->earthpy==0.10.0) (42.0.5)
Requirement already satisfied: more-itertools in /opt/conda/lib/python3.11/site-packages (from jaraco.classes->keyring->earthpy==0.10.0) (10.2.0)
Requirement already satisfied: backports.tarfile in /opt/conda/lib/python3.11/site-packages (from jaraco.context->keyring->earthpy==0.10.0) (1.1.1)
Requirement already satisfied: cffi>=1.12 in /opt/conda/lib/python3.11/site-packages (from cryptography>=2.0->SecretStorage>=3.2->keyring->earthpy==0.10.0) (1.16.0)
Requirement already satisfied: pycparser in /opt/conda/lib/python3.11/site-packages (from cffi>=1.12->cryptography>=2.0->SecretStorage>=3.2->keyring->earthpy==0.10.0) (2.22)
In [5]:
import getpass
import json
import os
import pathlib
from glob import glob

import earthpy.appeears as eaapp
import hvplot.pandas
import hvplot.xarray
import rioxarray as rxr
import xarray as xr
import geopandas as gpd
import pandas as pd
In [6]:
data_dir = os.path.join(pathlib.Path.home(), 'grizzly-creek-fire')
# Make the data directory
os.makedirs(data_dir, exist_ok=True)
In [7]:
# Download the Cameron Peak fire boundary
fire_url_gdf = gpd.read_file(
    "https://services3.arcgis.com/T4QMspbfLg3qTGWY/arcgis/rest/services/"
    "WFIGS_Interagency_Perimeters/FeatureServer/0/query?where"
    "=poly_IncidentName%20%3D%20'GRIZZLY%20CREEK'&outFields"
    "=*&outSR=4326&f=json"
)

fire_url_gdf
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:403: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore", utc=True)
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
/opt/conda/lib/python3.11/site-packages/geopandas/io/file.py:399: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_datetime without passing `errors` and catch exceptions explicitly instead
  as_dt = pd.to_datetime(df[k], errors="ignore")
Out[7]:
OBJECTID poly_SourceOID poly_IncidentName poly_FeatureCategory poly_MapMethod poly_GISAcres poly_CreateDate poly_DateCurrent poly_PolygonDateTime poly_IRWINID ... attr_Source attr_IsCpxChild attr_CpxName attr_CpxID attr_SourceGlobalID GlobalID Shape__Area Shape__Length attr_IncidentComplexityLevel geometry
0 11275 13459 Grizzly Creek Wildfire Final Fire Perimeter GPS-Walked 322.00 NaT 2023-03-14 15:13:42.810000+00:00 2020-09-05 03:13:00+00:00 {3478A04D-5A7E-4E89-9342-80DE610BA9B3} ... FODR None None None {6668D67B-68EE-4CF8-AC3E-EAAD67743DA1} d5a38ca3-2c28-45eb-b06c-ca4394ecd056 0.000143 0.079356 None POLYGON ((-122.40703 42.27382, -122.40700 42.2...
1 14453 13272 Grizzly Creek Wildfire Final Fire Perimeter Infrared Image 32431.62 NaT 2023-03-14 15:13:42.810000+00:00 2020-09-04 00:37:00+00:00 {BC150C8C-D9C8-4C14-8725-2B84D7695302} ... FODR None None None {E4E21DCE-C8EE-4133-B9F7-5CA2E5FBF79C} c9133643-c537-4030-a059-04d80c6c3a96 0.013763 1.346520 None MULTIPOLYGON (((-107.19151 39.66880, -107.1914...

2 rows × 115 columns

In [8]:
ans_gdf = _
gdf_pts = 0

if isinstance(ans_gdf, gpd.GeoDataFrame):
    print('\u2705 Great work! You downloaded and opened a GeoDataFrame')
    gdf_pts +=2
else:
    print('\u274C Hmm, your answer is not a GeoDataFrame')

print('\u27A1 You earned {} of 2 points for downloading data'.format(gdf_pts))
✅ Great work! You downloaded and opened a GeoDataFrame
➡ You earned 2 of 2 points for downloading data
In [9]:
# Selecting the infared image instance as our geodataframe
fire_url_gdf = fire_url_gdf.loc[[1]]
fire_url_gdf
Out[9]:
OBJECTID poly_SourceOID poly_IncidentName poly_FeatureCategory poly_MapMethod poly_GISAcres poly_CreateDate poly_DateCurrent poly_PolygonDateTime poly_IRWINID ... attr_Source attr_IsCpxChild attr_CpxName attr_CpxID attr_SourceGlobalID GlobalID Shape__Area Shape__Length attr_IncidentComplexityLevel geometry
1 14453 13272 Grizzly Creek Wildfire Final Fire Perimeter Infrared Image 32431.62 NaT 2023-03-14 15:13:42.810000+00:00 2020-09-04 00:37:00+00:00 {BC150C8C-D9C8-4C14-8725-2B84D7695302} ... FODR None None None {E4E21DCE-C8EE-4133-B9F7-5CA2E5FBF79C} c9133643-c537-4030-a059-04d80c6c3a96 0.013763 1.34652 None MULTIPOLYGON (((-107.19151 39.66880, -107.1914...

1 rows × 115 columns

In [10]:
# Plot the Grizzly Creek Fire boundary
fire_url_gdf.hvplot(
    tiles = 'EsriImagery',
    geo = True,
    title = 'Grizzly Creek Fire'
)
Out[10]:

Exploring the AppEEARS API for NASA Earthdata access¶

Over the next four cells, you will download MODIS NDVI data for the study period. MODIS is a multispectral instrument that measures Red and NIR data (and so can be used for NDVI). There are two MODIS sensors on two different platforms: satellites Terra and Aqua.

Read more

Learn more about MODIS datasets and the science they support

Since we’re asking for a special download that only covers our study area, we can’t just find a link to the data - we have to negotiate with the data server. We’re doing this using the APPEEARS API (Application Programming Interface). The API makes it possible for you to request data using code. You can use code from the earthpy library to handle the API request.

Your task

Often when we want to do something more complex in coding we find an example and modify it. This download code is already almost a working example. Your task will be:

  • Enter your NASA Earthdata username and password when prompted
  • Replace the start and end dates in the task parameters. Download data from July, when greenery is at its peak in the Northern Hemisphere.
  • Replace the year range. You should get 3 years before and after the fire so you can see the change!
  • Replace gdf with the name of your site geodataframe.

What would the product and layer name be if you were trying to download Landsat Surface Temperature Analysis Ready Data (ARD) instead of MODIS NDVI?

In [11]:
# Initialize AppeearsDownloader for MODIS NDVI data
ndvi_downloader = eaapp.AppeearsDownloader(
    download_key='gc-ndvi',
    ea_dir=data_dir,
    product='MOD13Q1.061', #satellite it is accessing.version
    layer='_250m_16_days_NDVI',
    start_date="08-01",
    end_date="09-15",
    recurring=True,
    year_range=[2017, 2023],
    polygon=fire_url_gdf
)
In [12]:
# Initialize AppeearsDownloader for MODIS NDVI data
ndvi_downloader.download_files(cache=True)
In [13]:
# Get a list of NDVI tif file paths
ndvi_paths = sorted(glob(os.path.join(data_dir, 'gc-ndvi', '*', '*NDVI*.tif')))
ndvi_paths
#len(ndvi_paths)
Out[13]:
['/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2017209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2017225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2017241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2017257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2018209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2018225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2018241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2018257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2019209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2019225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2019241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2019257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2020209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2020225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2020241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2020257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2021209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2021225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2021241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2021257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2022209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2022225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2022241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2022257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2023209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2023225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2023241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023258/MOD13Q1.061__250m_16_days_NDVI_doy2023257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2017209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2017225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2017241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2017257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2017273_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2017289_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2018209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2018225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2018241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2018257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2018273_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2018289_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2019209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2019225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2019241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2019257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2019273_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2019289_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2020209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2020225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2020241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2020257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2020273_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2020289_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2020305_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2021209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2021225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2021241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2021257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2021273_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2021289_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2022209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2022225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2022241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2022257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2022273_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2022289_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2023209_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2023225_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2023241_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2023257_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2023273_aid0001.tif',
 '/home/jovyan/grizzly-creek-fire/gc-ndvi/MOD13Q1.061_2017198_to_2023304/MOD13Q1.061__250m_16_days_NDVI_doy2023289_aid0001.tif']

Repeating tasks in Python¶

Now you should have a few dozen files! For each file, you need to:

  • Load the file in using the rioxarray library
  • Get the date from the file name
  • Add the date as a dimension coordinate
  • Give your data variable a name
  • Divide by the scale factor of 10000

You don’t want to write out the code for each file! That’s a recipe for copy pasta. Luckily, Python has tools for doing similar tasks repeatedly. In this case, you’ll use one called a for loop.

There’s some code below that uses a for loop in what is called an accumulation pattern to process each file. That means that you will save the results of your processing to a list each time you process the files, and then merge all the arrays in the list.

Your task

  • Look at the file names. How many characters from the end is the date?
  • Replace any required variable names with your chosen variable names
  • Change the scale_factor variable to be the correct scale factor for this NDVI dataset (HINT: NDVI should range between 0 and 1)
  • Using indices or regular
In [15]:
scale_factor = 10000
doy_start = -19
doy_end = -12
In [16]:
ndvi_das = []
for ndvi_path in ndvi_paths:
    # Get date from file name
    doy = ndvi_path[doy_start:doy_end]
    date = pd.to_datetime(doy, format='%Y%j')

    # Open dataset
    da = rxr.open_rasterio(ndvi_path, masked=True).squeeze()

    # Add date dimension and clean up metadata
    da = da.assign_coords({'date': date})
    da = da.expand_dims({'date': 1})
    da.name = 'NDVI'

    # Multiple by scale factor
    da = da / scale_factor

    # Prepare for concatenation
    ndvi_das.append(da)

len(ndvi_das)
Out[16]:
71

Next, stack your arrays by date into a time series using the xr.combine_by_coords() function. You will have to tell it which dimension you want to stack your data in.

Plot the change in NDVI spatially

Complete the following: * Select data from 2021 to 2023 (3 years after the fire) * Take the temporal mean (over the date, not spatially) * Get the NDVI variable (should be a DataArray, not a Dataset) * Repeat for the data from 2018 to 2020 (3 years before the fire) * Subtract the 2018-2020 time period from the 2021-2023 time period * Plot the result using a diverging color map like cmap=plt.cm.PiYG

There are different types of color maps for different types of data. In this case, we want decreases to be a different color from increases, so we should use a diverging color map. Check out available colormaps in the matplotlib documentation.

For an extra challenge, add the fire boundary to the plot

In [18]:
ndvi_da = xr.combine_by_coords(ndvi_das, coords=['date'])
ndvi_da
Out[18]:
<xarray.Dataset>
Dimensions:      (x: 94, y: 63, date: 43)
Coordinates:
    band         int64 1
  * x            (x) float64 -107.3 -107.3 -107.3 ... -107.1 -107.1 -107.1
  * y            (y) float64 39.67 39.67 39.67 39.66 ... 39.55 39.54 39.54 39.54
    spatial_ref  int64 0
  * date         (date) datetime64[ns] 2017-07-28 2017-08-13 ... 2023-10-16
Data variables:
    NDVI         (date, y, x) float32 0.6496 0.7459 0.7599 ... 0.4019 0.3721
xarray.Dataset
    • x: 94
    • y: 63
    • date: 43
    • band
      ()
      int64
      1
      array(1)
    • x
      (x)
      float64
      -107.3 -107.3 ... -107.1 -107.1
      array([-107.290625, -107.288542, -107.286458, -107.284375, -107.282292,
             -107.280208, -107.278125, -107.276042, -107.273958, -107.271875,
             -107.269792, -107.267708, -107.265625, -107.263542, -107.261458,
             -107.259375, -107.257292, -107.255208, -107.253125, -107.251042,
             -107.248958, -107.246875, -107.244792, -107.242708, -107.240625,
             -107.238542, -107.236458, -107.234375, -107.232292, -107.230208,
             -107.228125, -107.226042, -107.223958, -107.221875, -107.219792,
             -107.217708, -107.215625, -107.213542, -107.211458, -107.209375,
             -107.207292, -107.205208, -107.203125, -107.201042, -107.198958,
             -107.196875, -107.194792, -107.192708, -107.190625, -107.188542,
             -107.186458, -107.184375, -107.182292, -107.180208, -107.178125,
             -107.176042, -107.173958, -107.171875, -107.169792, -107.167708,
             -107.165625, -107.163542, -107.161458, -107.159375, -107.157292,
             -107.155208, -107.153125, -107.151042, -107.148958, -107.146875,
             -107.144792, -107.142708, -107.140625, -107.138542, -107.136458,
             -107.134375, -107.132292, -107.130208, -107.128125, -107.126042,
             -107.123958, -107.121875, -107.119792, -107.117708, -107.115625,
             -107.113542, -107.111458, -107.109375, -107.107292, -107.105208,
             -107.103125, -107.101042, -107.098958, -107.096875])
    • y
      (y)
      float64
      39.67 39.67 39.67 ... 39.54 39.54
      array([39.669792, 39.667708, 39.665625, 39.663542, 39.661458, 39.659375,
             39.657292, 39.655208, 39.653125, 39.651042, 39.648958, 39.646875,
             39.644792, 39.642708, 39.640625, 39.638542, 39.636458, 39.634375,
             39.632292, 39.630208, 39.628125, 39.626042, 39.623958, 39.621875,
             39.619792, 39.617708, 39.615625, 39.613542, 39.611458, 39.609375,
             39.607292, 39.605208, 39.603125, 39.601042, 39.598958, 39.596875,
             39.594792, 39.592708, 39.590625, 39.588542, 39.586458, 39.584375,
             39.582292, 39.580208, 39.578125, 39.576042, 39.573958, 39.571875,
             39.569792, 39.567708, 39.565625, 39.563542, 39.561458, 39.559375,
             39.557292, 39.555208, 39.553125, 39.551042, 39.548958, 39.546875,
             39.544792, 39.542708, 39.540625])
    • spatial_ref
      ()
      int64
      0
      crs_wkt :
      GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
      semi_major_axis :
      6378137.0
      semi_minor_axis :
      6356752.314245179
      inverse_flattening :
      298.257223563
      reference_ellipsoid_name :
      WGS 84
      longitude_of_prime_meridian :
      0.0
      prime_meridian_name :
      Greenwich
      geographic_crs_name :
      WGS 84
      horizontal_datum_name :
      World Geodetic System 1984
      grid_mapping_name :
      latitude_longitude
      spatial_ref :
      GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
      GeoTransform :
      -107.29166665668899 0.002083333333139592 0.0 39.67083332964411 0.0 -0.002083333333139592
      array(0)
    • date
      (date)
      datetime64[ns]
      2017-07-28 ... 2023-10-16
      array(['2017-07-28T00:00:00.000000000', '2017-08-13T00:00:00.000000000',
             '2017-08-29T00:00:00.000000000', '2017-09-14T00:00:00.000000000',
             '2017-09-30T00:00:00.000000000', '2017-10-16T00:00:00.000000000',
             '2018-07-28T00:00:00.000000000', '2018-08-13T00:00:00.000000000',
             '2018-08-29T00:00:00.000000000', '2018-09-14T00:00:00.000000000',
             '2018-09-30T00:00:00.000000000', '2018-10-16T00:00:00.000000000',
             '2019-07-28T00:00:00.000000000', '2019-08-13T00:00:00.000000000',
             '2019-08-29T00:00:00.000000000', '2019-09-14T00:00:00.000000000',
             '2019-09-30T00:00:00.000000000', '2019-10-16T00:00:00.000000000',
             '2020-07-27T00:00:00.000000000', '2020-08-12T00:00:00.000000000',
             '2020-08-28T00:00:00.000000000', '2020-09-13T00:00:00.000000000',
             '2020-09-29T00:00:00.000000000', '2020-10-15T00:00:00.000000000',
             '2020-10-31T00:00:00.000000000', '2021-07-28T00:00:00.000000000',
             '2021-08-13T00:00:00.000000000', '2021-08-29T00:00:00.000000000',
             '2021-09-14T00:00:00.000000000', '2021-09-30T00:00:00.000000000',
             '2021-10-16T00:00:00.000000000', '2022-07-28T00:00:00.000000000',
             '2022-08-13T00:00:00.000000000', '2022-08-29T00:00:00.000000000',
             '2022-09-14T00:00:00.000000000', '2022-09-30T00:00:00.000000000',
             '2022-10-16T00:00:00.000000000', '2023-07-28T00:00:00.000000000',
             '2023-08-13T00:00:00.000000000', '2023-08-29T00:00:00.000000000',
             '2023-09-14T00:00:00.000000000', '2023-09-30T00:00:00.000000000',
             '2023-10-16T00:00:00.000000000'], dtype='datetime64[ns]')
    • NDVI
      (date, y, x)
      float32
      0.6496 0.7459 ... 0.4019 0.3721
      array([[[0.6496, 0.7459, 0.7599, ..., 0.6255, 0.6255, 0.5293],
              [0.7566, 0.7352, 0.7433, ..., 0.6028, 0.6255, 0.6114],
              [0.7629, 0.7447, 0.784 , ..., 0.588 , 0.5446, 0.4406],
              ...,
              [0.7842, 0.7862, 0.7862, ..., 0.5565, 0.5333, 0.5344],
              [0.7144, 0.7144, 0.7415, ..., 0.5565, 0.4983, 0.4983],
              [0.7046, 0.7183, 0.7324, ..., 0.5124, 0.5124, 0.5   ]],
      
             [[0.5291, 0.7091, 0.7297, ..., 0.5781, 0.5781, 0.5153],
              [0.7042, 0.7091, 0.7276, ..., 0.5272, 0.5484, 0.5038],
              [0.7319, 0.657 , 0.6777, ..., 0.4725, 0.39  , 0.3235],
              ...,
              [0.8007, 0.7968, 0.7968, ..., 0.4808, 0.4727, 0.4869],
              [0.7304, 0.7304, 0.7304, ..., 0.4455, 0.4727, 0.4727],
              [0.7117, 0.7035, 0.6793, ..., 0.4455, 0.4455, 0.4455]],
      
             [[0.5632, 0.6754, 0.688 , ..., 0.5402, 0.5402, 0.4458],
              [0.6481, 0.6598, 0.688 , ..., 0.519 , 0.5402, 0.4992],
              [0.661 , 0.6488, 0.6332, ..., 0.519 , 0.4468, 0.3946],
              ...,
      ...
              [0.7345, 0.713 , 0.713 , ..., 0.453 , 0.4488, 0.4593],
              [0.6657, 0.6657, 0.713 , ..., 0.4524, 0.4331, 0.4331],
              [0.6398, 0.6657, 0.6567, ..., 0.4496, 0.4496, 0.4693]],
      
             [[0.4687, 0.4535, 0.5655, ..., 0.3834, 0.3834, 0.3834],
              [0.4687, 0.5548, 0.5462, ..., 0.3976, 0.3956, 0.3663],
              [0.5299, 0.4667, 0.4764, ..., 0.3623, 0.3956, 0.3409],
              ...,
              [0.5938, 0.5938, 0.5938, ..., 0.359 , 0.3562, 0.3709],
              [0.557 , 0.557 , 0.5276, ..., 0.3612, 0.3565, 0.3565],
              [0.5051, 0.5154, 0.5205, ..., 0.3661, 0.3661, 0.4046]],
      
             [[0.422 , 0.4969, 0.5601, ..., 0.3314, 0.3314, 0.3368],
              [0.4411, 0.4969, 0.516 , ..., 0.3609, 0.3697, 0.389 ],
              [0.4431, 0.4411, 0.4668, ..., 0.3609, 0.3179, 0.3045],
              ...,
              [0.5495, 0.4991, 0.4991, ..., 0.3768, 0.3678, 0.3653],
              [0.5495, 0.5495, 0.5316, ..., 0.3846, 0.378 , 0.378 ],
              [0.4733, 0.4844, 0.4692, ..., 0.4019, 0.4019, 0.3721]]],
            dtype=float32)
    • x
      PandasIndex
      PandasIndex(Index([-107.29062499002242, -107.28854165668929, -107.28645832335614,
               -107.284374990023, -107.28229165668986, -107.28020832335673,
             -107.27812499002358, -107.27604165669044,  -107.2739583233573,
             -107.27187499002417, -107.26979165669103, -107.26770832335788,
             -107.26562499002475, -107.26354165669161, -107.26145832335847,
             -107.25937499002532, -107.25729165669219, -107.25520832335906,
             -107.25312499002591, -107.25104165669276, -107.24895832335963,
              -107.2468749900265, -107.24479165669335,  -107.2427083233602,
             -107.24062499002707, -107.23854165669394,  -107.2364583233608,
             -107.23437499002765, -107.23229165669451, -107.23020832336138,
             -107.22812499002823, -107.22604165669509, -107.22395832336196,
             -107.22187499002882, -107.21979165669568, -107.21770832336253,
              -107.2156249900294, -107.21354165669626, -107.21145832336312,
             -107.20937499002997, -107.20729165669684,  -107.2052083233637,
             -107.20312499003056, -107.20104165669741, -107.19895832336428,
             -107.19687499003115,   -107.194791656698, -107.19270832336485,
             -107.19062499003172, -107.18854165669859, -107.18645832336544,
              -107.1843749900323, -107.18229165669916, -107.18020832336603,
             -107.17812499003288, -107.17604165669974,  -107.1739583233666,
             -107.17187499003347, -107.16979165670033, -107.16770832336718,
             -107.16562499003405, -107.16354165670091, -107.16145832336777,
             -107.15937499003462, -107.15729165670149, -107.15520832336836,
             -107.15312499003521, -107.15104165670206, -107.14895832336893,
              -107.1468749900358, -107.14479165670265,  -107.1427083233695,
             -107.14062499003637, -107.13854165670324, -107.13645832337009,
             -107.13437499003695, -107.13229165670381, -107.13020832337068,
             -107.12812499003753, -107.12604165670439, -107.12395832337126,
             -107.12187499003812, -107.11979165670498, -107.11770832337183,
              -107.1156249900387, -107.11354165670556, -107.11145832337242,
             -107.10937499003927, -107.10729165670614,   -107.105208323373,
             -107.10312499003986, -107.10104165670671, -107.09895832337358,
             -107.09687499004045],
            dtype='float64', name='x'))
    • y
      PandasIndex
      PandasIndex(Index([39.669791662977545, 39.667708329644405, 39.665624996311266,
             39.663541662978126,  39.66145832964499,  39.65937499631185,
              39.65729166297871,  39.65520832964557,  39.65312499631243,
              39.65104166297929,  39.64895832964615,  39.64687499631301,
              39.64479166297987,  39.64270832964673,  39.64062499631359,
              39.63854166298045,  39.63645832964731,  39.63437499631417,
              39.63229166298103,  39.63020832964789,  39.62812499631475,
              39.62604166298161, 39.623958329648474, 39.621874996315334,
             39.619791662982195, 39.617708329649055, 39.615624996315915,
             39.613541662982776, 39.611458329649636,   39.6093749963165,
              39.60729166298336,  39.60520832965022,  39.60312499631708,
              39.60104166298394,   39.5989583296508,  39.59687499631766,
              39.59479166298452,  39.59270832965138,  39.59062499631824,
               39.5885416629851,  39.58645832965196,  39.58437499631882,
              39.58229166298568,  39.58020832965254,   39.5781249963194,
              39.57604166298626, 39.573958329653124, 39.571874996319984,
             39.569791662986844, 39.567708329653705, 39.565624996320565,
             39.563541662987426, 39.561458329654286,  39.55937499632115,
              39.55729166298801,  39.55520832965487,  39.55312499632173,
              39.55104166298859,  39.54895832965545,  39.54687499632231,
              39.54479166298917,  39.54270832965603,  39.54062499632289],
            dtype='float64', name='y'))
    • date
      PandasIndex
      PandasIndex(DatetimeIndex(['2017-07-28', '2017-08-13', '2017-08-29', '2017-09-14',
                     '2017-09-30', '2017-10-16', '2018-07-28', '2018-08-13',
                     '2018-08-29', '2018-09-14', '2018-09-30', '2018-10-16',
                     '2019-07-28', '2019-08-13', '2019-08-29', '2019-09-14',
                     '2019-09-30', '2019-10-16', '2020-07-27', '2020-08-12',
                     '2020-08-28', '2020-09-13', '2020-09-29', '2020-10-15',
                     '2020-10-31', '2021-07-28', '2021-08-13', '2021-08-29',
                     '2021-09-14', '2021-09-30', '2021-10-16', '2022-07-28',
                     '2022-08-13', '2022-08-29', '2022-09-14', '2022-09-30',
                     '2022-10-16', '2023-07-28', '2023-08-13', '2023-08-29',
                     '2023-09-14', '2023-09-30', '2023-10-16'],
                    dtype='datetime64[ns]', name='date', freq=None))
In [25]:
# Compute the difference in NDVI before and after the fire
ndvi_diff = (
    ndvi_da
        .sel(date=slice('2021', '2023'))
        .mean('date')
        .NDVI 
   - ndvi_da
        .sel(date=slice('2018', '2020'))
        .mean('date')
        .NDVI
)


# Plot the difference
(
    ndvi_diff.hvplot(x='x', y='y', cmap='PiYG', geo=True)
    *
    fire_url_gdf.hvplot(geo=True, fill_color=None, line_color='black')
)
Out[25]:

Is the NDVI lower within the fire boundary after the fire?¶

You will compute the mean NDVI inside and outside the fire boundary. First, use the code below to get a GeoDataFrame of the area outside the Reservation. Your task: * Check the variable names - Make sure that the code uses your boundary GeoDataFrame * How could you test if the geometry was modified correctly? Add some code to take a look at the results.

In [26]:
out_gdf = (
    gpd.GeoDataFrame(geometry=fire_url_gdf.envelope)
    .overlay(fire_url_gdf, how='difference'))

# testing if geometry modified correctly
out_gdf.hvplot(geo=True, fill_color = 'blue', line_color='black')
Out[26]:

Next, clip your DataArray to the boundaries for both inside and outside the reservation. You will need to replace the GeoDataFrame name with your own. Check out the lesson on clipping data with the rioxarray library in the textbook.

GOTCHA ALERT

It’s important to use from_disk=True when clipping large arrays like this. It allows the computer to use less valuable memory resources when clipping - you will probably find that otherwise the cell below crashes the kernel

In [27]:
# Clip data to both inside and outside the boundary
ndvi_gc_da = ndvi_da.rio.clip(fire_url_gdf.geometry, from_disk=True)
ndvi_out_da = ndvi_da.rio.clip(out_gdf.geometry, from_disk=True)

Your Task

For both inside and outside the fire boundary:

  • Group the data by year
  • Take the mean. You always need to tell reducing methods in xarray what dimensions you want to reduce. When you want to summarize data across all dimensions, you can use the ... syntax, e.g. .mean(...) as a shorthand.
  • Select the NDVI variable
  • Convert to a DataFrame using the to_dataframe() method
  • Join the two DataFrames for plotting using the .join() method. You will need to rename the columns using the lsuffix= and rsuffix= parameters

GOTCHA ALERT

The DateIndex in pandas is a little different from the Datetime Dimension in xarray. You will need to use the .dt.year syntax to access information about the year, not just .year.

Finally, plot annual July means for both inside and outside the Reservation on the same plot.

:::

In [28]:
# Compute mean annual July NDVI
jul_ndvi_gc_df = (
    ndvi_gc_da
    .groupby(ndvi_gc_da.date.dt.year)
    .mean(...)
    .NDVI.to_dataframe())
jul_ndvi_out_df = (
    ndvi_out_da
    .groupby(ndvi_out_da.date.dt.year)
    .mean(...)
    .NDVI.to_dataframe())

# Plot inside and outside the reservation
jul_ndvi_df = (
    jul_ndvi_gc_df[['NDVI']]
    .join(
        jul_ndvi_out_df[['NDVI']], 
        lsuffix=' Burned Area', rsuffix=' Unburned Area')
)

jul_ndvi_df.hvplot(
    title='NDVI before and after the Grizzly Creek Fire'
)
Out[28]:

Now, take the difference between outside and inside the Reservation and plot that. What do you observe? Don’t forget to write a headline and description of your plot!

In [29]:
# Plot difference inside and outside the reservation
jul_ndvi_df['difference'] = (
    jul_ndvi_df['NDVI Burned Area']
    - jul_ndvi_df['NDVI Unburned Area'])
jul_ndvi_df.difference.hvplot(
    title='Difference between NDVI within and outside the Grizzly Creek Fire'
)
Out[29]:

Your turn! Repeat this workflow in a different time and place.¶

It’s not just fires that affect NDVI! You could look at:

  • Recovery after a national disaster, like a wildfire or hurricane
  • Drought
  • Deforestation
  • Irrigation
  • Beaver reintroduction
In [24]:
%%capture
%%bash
jupyter nbconvert *.ipynb --to html