This notebook uses changes in NDVI, EVI or Fractional Cover to identify land change. The algorithm identifies a "baseline" and "analysis" time period and then compares the spectral parameters in each of those time periods. Significant reductions in vegetation are coincident with land change. In some cases these changes could be deforestation. Users of this algorithm should not accept the accuracy of the results but should conduct ground validation testing to assess accuracy. In most cases, these algorithms can be used to identify clusters of pixels that have experienced change and allow targeted investigation of those areas by local or regional governments.
import warnings
# Supress Warning
warnings.filterwarnings('ignore')
# Load Data Cube Configuration
import datacube
dc = datacube.Datacube(app = 'my_app')#, config = '/home/localuser/.datacube.conf')
# Import Data Cube API
import utils.data_cube_utilities.data_access_api as dc_api
api = dc_api.DataAccessApi()#config = '/home/localuser/.datacube.conf')
dc = api.dc
## LS8 Caqueta
# Latitude: (0.000134747292617865, 1.077843593651382)
# Longitude: (-74.91935994831539, -73.30266193148462)
# '2013-04-13', '2018-03-26'
# Resolution: (-0.000269494585236, 0.000269494585236)
## LS8 Vietnam
# Latitude: (10.513927001104687, 12.611133863411238)
# Longitude: (106.79005909290998, 108.91906631627438)
# '2014-01-14', '2016-12-21'
# Resolution: (-0.000269494585236, 0.000269494585236)
## LS7 Caqueta
# Latitude: (0.000134747292617865, 1.077843593651382)
# Longitude: (-74.91935994831539, -73.30266193148462)
# '1999-08-21', '2018-03-25'
# Resolution: (-0.000269494585236, 0.000269494585236)
## LS7 Lake Baringo
# Latitude: (0.4997747685, 0.7495947795)
# Longitude: (35.9742163305, 36.473586859499996)
# '2005-01-08', '2016-12-24'
# Resolution: (-0.000269493, 0.000269493)
# CHANGE HERE >>>>>>>>>>>>>>>>>
# Select a Product and Platform
#product = ls7_collection1_AMA_ingest
#platform = "LANDSAT_7"
product = 'ls7_collection1_AMA_ingest'
platform = "LANDSAT_7"
output_crs = 'EPSG:4326'
resolution = (-0.000269494585236, 0.000269494585236)
# CHANGE HERE >>>>>>>>>>>>>>>>>
# Select an analysis region (Lat-Lon) within the extents listed above.
# HINT: Keep your region small (<0.5 deg square) to avoid memory overload issues
# Select a time period (Min-Max) within the extents listed above (Year-Month-Day)
# This region and time period will be used for the cloud assessment
#Sub-region selection
latitude = (1.0684, 0.8684)
longitude = (-74.8409, -74.6409)
## The code below renders a map that can be used to orient yourself with the region.
from utils.data_cube_utilities.dc_display_map import display_map
display_map(latitude = latitude, longitude = longitude)
from datetime import datetime
# CHANGE HERE >>>>>>>>>>>>>>>>>>>>
# Select the start and end periods for your analysis products
# The datetime function is (Year,Month,Day)
# These time windows will be used to make a mosaic, so typically pick a year length or more
# Be sure to evaluate the RGB mosaics to affirm they are not full of clouds
# Select the baseline time period (start and end)
baseline_time_period = (datetime(2000,1,1), datetime(2001,1,1))
# Select the analysis time period (start and end)
analysis_time_period = (datetime(2016,1,1), datetime(2017,6,1))
# CHANGE HERE >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Select the cloud-free mosaic type
# Options are: max_ndvi, min_ndvi, median, most_recent_pixel, geomedian
# If a geomedian is selected, it will take much longer to process
# It is most common to use the max_ndvi or median for these land change analyses
# HINT: Consider max_ndvi mosaics for NDVI analyses and median mosaics for EVI analyses
baseline_mosaic_function = "median"
analysis_mosaic_function = "median"
Load Data ( Baseline, Analysis)¶
import datacube
name = "land change"
version = 1
dc = datacube.Datacube(app = "{}_v{}".format(name, version))#, #config = '/home/localuser/.datacube.conf')
baseline_ds = dc.load(
latitude = latitude,
longitude = longitude,
time = baseline_time_period,
measurements = ["red", "green", "blue", "nir", "swir1", "swir2", "pixel_qa"],
product = product,
platform = platform,
output_crs = output_crs,
resolution = resolution
)
analysis_ds = dc.load(
latitude = latitude,
longitude = longitude,
time = analysis_time_period,
measurements = ["red", "green", "blue", "nir", "swir1", "swir2", "pixel_qa"],
product = product,
platform = platform,
output_crs = output_crs,
resolution = resolution
)
Check if loads are valid¶
import xarray as xr
def is_dataset_empty(ds:xr.Dataset) -> bool:
checks_for_empty = [
lambda x: len(x.dims) == 0, #Dataset has no dimensions
lambda x: len(x.data_vars) == 0 #Dataset no variables
]
for f in checks_for_empty:
if f(ds) == True:
return True
return False
if is_dataset_empty(baseline_ds): raise Exception("DataCube Load returned an empty Dataset." +
"Please check load parameters for Baseline Dataset!")
if is_dataset_empty(analysis_ds): raise Exception("DataCube Load returned an empty Dataset." +
"Please check load parameters for Analysis Dataset!")
Clean Data¶
Generating boolean masks that highlight valid pixels Pixels must be cloud-free over land or water to be considered
from utils.data_cube_utilities.dc_mosaic import ls8_unpack_qa, ls7_unpack_qa
unpack_function = {"LANDSAT_7": ls7_unpack_qa,
"LANDSAT_8": ls8_unpack_qa}
unpack = unpack_function[platform]
import numpy as np
def clean_mask(ds, unpacking_func, bands):
masks = [unpacking_func(ds, band) for band in bands]
return np.logical_or(*masks).values
baseline_clean_mask = clean_mask(baseline_ds.pixel_qa,unpack, ["clear", "water"])
analysis_clean_mask = clean_mask(analysis_ds.pixel_qa, unpack, ["clear", "water"])
baseline_ds = baseline_ds.where(baseline_clean_mask)
analysis_ds = analysis_ds.where(analysis_clean_mask)
Mosaic¶
Use clean masks in a time series composite
from utils.data_cube_utilities.dc_mosaic import create_max_ndvi_mosaic, create_min_ndvi_mosaic, create_median_mosaic, create_mosaic, create_hdmedians_multiple_band_mosaic
mosaic_function = {"median": create_median_mosaic,
"max_ndvi": create_max_ndvi_mosaic,
"min_ndvi": create_min_ndvi_mosaic,
"geomedian": create_hdmedians_multiple_band_mosaic,
"most_recent_pixel": create_mosaic}
baseline_compositor = mosaic_function[baseline_mosaic_function]
analysis_compositor = mosaic_function[analysis_mosaic_function]
baseline_composite = baseline_compositor(baseline_ds, clean_mask = baseline_clean_mask)
analysis_composite = analysis_compositor(analysis_ds, clean_mask = analysis_clean_mask)
from utils.data_cube_utilities.dc_rgb import rgb
rgb(baseline_composite, use_data_min=True, use_data_max=True)
(<Figure size 576x576 with 1 Axes>, <matplotlib.axes._subplots.AxesSubplot at 0x7f9561e8acf8>)