# Import modules
import numpy as np
import geopandas as gpd
@@ -891,12 +891,12 @@ 86
Read vector layer
-
+
# Read file with watersheds
= '../datasets/spatial/konza_watersheds.geojson'
vector_path = gpd.read_file(vector_path)
gdf 3) gdf.head(
-
+
@@ -958,7 +958,7 @@ Read vector layer
-
+
# Display coordiante reference system of vector layer
print(gdf.crs)
@@ -967,11 +967,11 @@ Read vector layer
The first step to perform spatial operations between vector and raster layers is to have both data sources in the same coordinate reference system. Our vector layer has geometries in projected coordinates (meters), but the raster datasets will be in geographic coordinates.
To perform operations that involve distance computations is best to have both datasets in projected coordiantes. To perform other operations like clipping layers, then both geographic and projected coordinates should perform similarly. In this example we will convert the vector layer to geographic coordinates.
-
+
# Change coordinate reference system from projected to geographic
=4326, inplace=True) gdf.to_crs(epsg
-
+
# Visualize vector map of watersheds
='None', edgecolor='k')
gdf.plot(facecolor'Konza Watersheds')
@@ -992,11 +992,11 @@ plt.title(Read raster layer
Band 3: Sentinel B08 (near infrared, 842 nm)
The image was downloaded using Sentinel Hub: https://www.sentinel-hub.com/explore/eobrowser/
-
+
# Load raster layer
= xr.open_dataarray('../datasets/spatial/2024-04-03_Sentinel-2_L2A_B03_B04_B08-16bit.tiff')
raster raster
-
+
-
+
# Display CRS for raster layer
print(raster.rio.crs)
EPSG:4326
-
+
# Create separate variables to easily keep track of each band
-= raster[1] # Red band
- B4 = raster[2] # NIR band B8
+= raster[1] # Red band
+ red = raster[2] # NIR band nir
-
+
# Visualize raster files (1 band each)
=(12,4))
plt.figure(figsize
1,2,1)
- plt.subplot('Band 4')
- plt.title(='Spectral')
+ B4.plot(cmap'Red band')
+ plt.title(='Spectral')
red.plot(cmap
1,2,2)
- plt.subplot('Band 8')
- plt.title(='Spectral')
+ B8.plot(cmap'NIR band')
+ plt.title(='Spectral')
nir.plot(cmap
plt.show()
@@ -1427,28 +1427,28 @@ Read raster layer
Compute NDVI
Normalized Difference Vegetation Index (NDVI) is a measure typically used in remote sensing to quantify and assess vegetation health and density. It is calculated using near-infrared (NIR) and red light reflectance values over an area as follows:
- NDVI = \frac{Red - NIR}{Red+NIR}
+ NDVI = \frac{NIR-Red}{NIR+Red}
NDVI values range from -1 to 1, where higher values indicate healthier and denser vegetation, typically falling between 0.2 and 0.8 for vegetation. Bare soil and rocks fall have values ranging from 0 to 0.2. Bodies of water like ponds, likes, rivers, and oceans typically have negative values close to -1.
-
+
# Calculate NDVI
-= (B8 - B4)/(B8 + B4) NDVI
+= (nir - red)/(nir + red) ndvi
Now that we have the NDVI raster layer, here a few additional commands that could be useful:
# Get NDVI values as Numpy array
-
+ NDVI.values
ndvi.values
# Get latitude values as Numpy array
-'y'].values
+ NDVI.coords['y'].values
ndvi.coords[
# Get a 3 by 3 slice
-0:3,0:3]
+ NDVI[0:3,0:3]
ndvi[
# Find NDVI value for nearest point
-=-96.664356, y=39.15406606, method='nearest') NDVI.sel(x
+=-96.664356, y=39.15406606, method='nearest') ndvi.sel(x
Define custom colormap
-
+
# Palette of colors NDVI
= ['#CE7E45', '#DF923D', '#F1B555', '#FCD163', '#99B718', '#74A901',
hex_palette '#66A000', '#529400', '#3E8601', '#207401', '#056201', '#004C00', '#023B01',
@@ -1459,17 +1459,17 @@ Define custom color
'#FEFEFE')
ndvi_cmap.set_bad('#0000FF')
ndvi_cmap.set_under( ndvi_cmap
-
+
from_list underbad over
-
+
# Add new colormap to list of Matplotlib's colormaps
=ndvi_cmap, name='ndvi') plt.colormaps.register(cmap
-
+
# Visualize NDVI for the entire area of the image
-='ndvi', vmin=0.0, vmax=1.0,
+ NDVI.plot(cmap='ndvi', vmin=0.0, vmax=1.0,
ndvi.plot(cmap={'label':'NDVI', 'shrink':0.5})
cbar_kwargs'NDVI')
plt.title('Longitude')
@@ -1482,18 +1482,18 @@ plt.xlabel(Define custom color
Clip NDVI raster to watersheds
-
+
# Define a function to clip the raster with each polygon and return a numpy array
= lambda polygon, R: R.rio.clip([polygon.geometry],
clip_fn =R.rio.crs,
crs=True)
all_touched
# Apply the function to each row in the GeoDataFrame to create a new 'clipped_raster' column
-'clipped_raster'] = gdf.apply(lambda row: clip_fn(row, NDVI), axis=1)
+ gdf['clipped_raster'] = gdf.apply(lambda row: clip_fn(row, ndvi), axis=1)
gdf[
# Inspect resulting GeoDataframe
3) gdf.head(
-
+
@@ -1563,7 +1563,7 @@ Clip NDVI r
Visualize a specific clipped watershed
We will choose the K1B watershed to show the NDVI for the entire area.
-
+
# Select watershed
= gdf['NAME_1'] == 'K1B'
idx = gdf[idx].index[0]
@@ -1572,7 +1572,7 @@ row Vis
31
-
+
# Create figure of selected watershed
= plt.subplots(figsize=(6, 6))
fig, ax 'geometry'].boundary.plot(ax=ax, edgecolor='k')
@@ -1592,7 +1592,7 @@ gdf.loc[ [row], Vis
Compute mean NDVI for each watershed
-
+
# Create empty list to append watershed NDVI values
= []
ndvi_avg
@@ -1605,7 +1605,7 @@ Compu
# Inspect results
3) gdf.head(
-
+
@@ -1678,7 +1678,7 @@ Compu
Create static map
-
+
# Create figure with mean NDVI values for each watershed
='ndvi_avg', edgecolor='k', cmap='ndvi',
gdf.plot(column=True, vmin=0.0, vmax=1.0, legend_kwds={'label':'NDVI', 'shrink':0.5})
@@ -1694,12 +1694,12 @@ legendCreate static map
Create interactive map
-
+
# Create interactive map
-1].explore(column='ndvi_avg', k=10, cmap='ndvi',
gdf.iloc[:,:={'stroke':True,'color':'black','width':1},
style_kwds={'caption':'NDVI'}) legend_kwds
-
+
Make this Notebook Trusted to load map: File -> Trust Notebook