🌎 Simple and fast watershed delineation in python
View the Project on GitHub mdbartos/pysheds
The grid.view method returns a copy of a dataset cropped to the grid’s current view. The grid’s current view is defined by its viewfinder attribute, which contains five properties that fully define the spatial reference system:
affine: An affine transformation matrix.shape: The desired shape (rows, columns).crs : The coordinate reference system.mask : A boolean array indicating which cells are masked.nodata : A sentinel value indicating ‘no data’.The grid’s view will be populated automatically upon reading the first dataset.
grid = Grid.from_raster('./data/dem.tif')
grid.affine
Affine(0.0008333333333333, 0.0, -97.4849999999961,
0.0, -0.0008333333333333, 32.82166666666536)
grid.crs
Proj('+proj=longlat +datum=WGS84 +no_defs', preserve_units=True)
grid.shape
(359, 367)
grid.mask
array([[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True],
...,
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True]])
We can verify that the spatial reference system is the same as that of the originating dataset:
dem = grid.read_raster('./data/dem.tif')
grid.affine == dem.affine
True
grid.crs == dem.crs
True
grid.shape == dem.shape
True
(grid.mask == dem.mask).all()
True
First, let’s delineate a watershed and use the grid.view method to get the results.
# Resolve flats
inflated_dem = grid.resolve_flats(dem)
# Compute flow directions
fdir = grid.flowdir(inflated_dem)
# Specify pour point
x, y = -97.294167, 32.73750
# Delineate the catchment
catch = grid.catchment(x=x, y=y, fdir=fdir, xytype='coordinate')
# Get the current view and plot
catch_view = grid.view(catch)
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(catch_view, cmap='Greys_r', zorder=1)
plt.title('Catchment', size=14)
plt.tight_layout()

Note that in this case, the original raster and its view are the same:
(catch == catch_view).all()
True
The grid.clip_to method clips the grid’s current view to nonzero elements in a given dataset. This is especially useful for clipping the view to an irregular feature like a delineated watershed.
# Clip the grid's view to the catchment dataset
grid.clip_to(catch)
# Get the current view and plot
catch_view = grid.view(catch)
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(catch_view, cmap='Greys_r', zorder=1)
plt.title('Clipped catchment', size=14)
plt.tight_layout()

We can also now use the view method to view other datasets within the current catchment boundaries:
# Get the current view of flow directions
fdir_view = grid.view(fdir)
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(fdir_view, cmap='viridis', zorder=1)
plt.title('Clipped flow directions', size=14)
plt.tight_layout()

The “no data” value in the output array can be specified using the nodata keyword argument. This is often useful for visualization.
dem_view = grid.view(dem, nodata=np.nan)
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(dem_view, cmap='terrain', zorder=1)
plt.title('Clipped DEM with mask', size=14)
plt.tight_layout()

The mask can be turned off by setting apply_output_mask=False.
dem_view = grid.view(dem, nodata=np.nan,
apply_output_mask=False)
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(dem_view, cmap='terrain', zorder=1)
plt.title('Clipped DEM without mask', size=14)
plt.tight_layout()

By default, the view method uses a nearest neighbors approach for interpolation. However, this can be changed using the interpolation keyword argument.
# Load a dataset with a different spatial reference system
terrain = grid.read_raster('./data/impervious_area.tiff', window=grid.bbox,
window_crs=grid.crs)
# View the new dataset with nearest neighbor interpolation
nn_interpolation = grid.view(terrain, nodata=np.nan)
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(nn_interpolation, cmap='bone', zorder=1)
plt.title('Nearest neighbor interpolation', size=14)
plt.tight_layout()

# View the new dataset with linear interpolation
lin_interpolation = grid.view(terrain, nodata=np.nan, interpolation='linear')
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(lin_interpolation, cmap='bone', zorder=1)
plt.title('Linear interpolation', size=14)
plt.tight_layout()

The grid.viewfinder attribute can also be set manually.
# Reset the view to the dataset's original view
grid.viewfinder = dem.viewfinder
# Plot the new view
dem_view = grid.view(dem)
fig, ax = plt.subplots(figsize=(8,6))
fig.patch.set_alpha(0)
plt.imshow(dem_view, cmap='terrain', zorder=1)
plt.title('DEM with original view restored', size=14)
plt.tight_layout()
