FormalPara Overview

The purpose of this chapter is to showcase Earth Engine’s application in fluvial hydrology and geomorphology. Specifically, we show examples demonstrating how to use Earth Engine to extract a river’s centerline and width, and how to calculate the bank erosion rate. At the end of this chapter, you will be able to distinguish rivers from other water bodies, perform basic morphological analyses, and detect changes in river form over time.

FormalPara Learning Outcomes
  • Working with Landsat surface water products.

  • Calculating river centerline location and width.

  • Quantifying river bank erosion.

Helps if you know how to

  • Import images and image collections, filter, and visualize (Part I).

  • Perform basic image analysis: select bands, compute indices, create masks (Part II).

  • Perform image morphological operations (Chap. 10).

  • Write a function and map it over an ImageCollection (Chap. 12).

  • Use reduceRegions to summarize an image with zonal statistics in irregular shapes (Chaps. 22 and 24).

  • Work with vector data (Chap. 23)

1 Introduction to Theory

The shape of a river viewed from above, known as its “planview geometry,” can reveal many things about the river, including its morphological evolution and the flow of water and sediment within its channel. For example, hydraulic geometry establishes that a river’s width and its discharge satisfy a power-law relation (rating curve). Thus, one can use such a relationship to monitor a river’s discharge from river widths derived from remote sensing images (Smith et al. 1996). Similarly, in addition to the natural variability of river size, rivers also adjust their courses on the landscape as water flows from the headwaters toward lowland downstream regions. These adjustments result in meandering, lateral variations in a river’s course resulting from the erosion and accretion of sediment along the banks. Many tools have been developed to study morphological changes of rivers using remotely sensed images.

Early remote sensing of river form was done by manual interpretation of aerial imagery, but advances in computing power have facilitated and the volume of imagery from satellites has necessitated automated processing. The RivWidth software (Pavelsky and Smith 2008) first presented an automated method for river width extraction, and RivWidthCloud (RWC) (Yang et al. 2020) later applied and expanded these methods to Earth Engine. Similarly, methods for detecting changes in river form have evolved from simple tools that track hand-drawn river centerlines (Shields et al. 2000) to automated methods that can process entire basins (Constantine et al. 2014; Rowland et al. 2016). This progression toward automated software for studies in fluvial geomorphology has paired well with the capabilities of Earth Engine, and as a result, many tools are being built for large-scale analysis in the cloud (Boothroyd et al. 2021).

2 Practicum

2.1 Creating and Analyzing a Single River Mask

In this section, we will prepare an image and calculate some simple morphological attributes of a river. To do this, we will use a pre-classified image of surface water occurrence, identify which pixels represent the river and channel bars, and finally calculate the centerline, width, and bank characteristics.

2.1.1 Isolate River from Water Surface Occurrence Map

Code Checkpoint A24a. The book’s repository contains a script to use to begin this section. You will need to start with that script and paste code below into it.

The script includes our example area of interest (in the variable aoi) and two helper functions for reprojecting data to the local UTM coordinates. We force this projection and scale for many of our map layers because we are trying to observe and measure the river morphology. As data is viewed at different zoom levels, the shapes and apparent connectivity of many water bodies will change. To allow a given dataset to be viewed with the same detail at multiple scales, we can force the data to be reprojected, as we do here.

The Joint Research Centre’s surface water occurrence dataset (Pekel et al. 2016) classified the entire Landsat 5, 7, and 8 history and produced annual maps that identify seasonal and permanent water classes. Here, we will include both seasonal and permanent water classes (represented by pixel values of ≥ 2) as water pixels (with value = 1) and the rest as non-water pixels (with value = 0). In this section, we will look at only one image at a time by choosing the image from the year 2000 (Fig. 1a). In the code below, bg serves as a dark background layer for other map layers to be seen easily.

Fig. 43.1
A group of four curved lines depicts a water mask, a water mask with a dotted line across the center, a channel mask, and a river mask.

a Water mask; b filled water mask with prior centerline points; c channel mask; d river mask

A code for the import and visualize surface water tasks has 13 steps. The steps include surface water occurrence data set from J R C and selection of the seasonal and permanent pixels image .

Next, we clean up the water mask by filling in small gaps by performing a closing operation (dilation followed by erosion). Areas of non-water pixels inside surface water bodies in the water mask may represent small channel bars, which we will fill in to create a simplified water mask. We identify these bars using a vectorization; however, you could do a similar operation with the connectedPixelCount method for bars up to 256 pixels in size (Fig. 1b). Filling in these small bars in the river mask improves the creation of a new centerline later in the lab.

A code for the removal of noise and small islands to simplify the topology. It has 2 parts labeled a and b. Part A is the image closure operation to fill small holes and part B identifies small bars and fills them in to create a filled water mask.

Note here that we forced reprojection of the map layer using the helper function rpj. This means we have to be careful to keep our domain small enough to be processed at the set scale when doing the calculation on the fly in the Code Editor; otherwise, we will run out of memory. The reprojection may not be necessary when exporting the output using a task.

In the following step, we extract water bodies in the water mask that correspond to rivers. We will define a river mask (Fig. 1d) to be pixels that are connected to the river centerline according to the filled water mask. The channel mask (Fig. 1c) is defined also by connectivity but excludes the small bars, which will give us more accurate widths and areas for change detection in Sects. 43.2.1.2 and 43.2.1.3.

We can extract the river mask by checking the water pixels’ connectivity to a provided river location database. Specifically, we use the Earth Engine method cumulativeCost to identify connectivity between the filled water mask and the pixels corresponding to the river dataset. By inverting the filled mask, the cost to traverse water pixels is 0, and the cost over land pixels is 1. Pixels in the cost map with a value of 0 are entirely connected to the Surface Water and Ocean Topography (SWOT) Mission River Database (SWORD) centerline points by water, and pixels with values greater than 0 are separated from SWORD by land. The SWORD data, which were loaded as assets in the starter script, have some points located on land, either because the channel bifurcates or because the channel has migrated, so we must exclude those from our cumulative cost parameter source, or they will appear as single pixels of 0 in our cost map.

The maxDistance parameter must be set to capture maximum distance between centerline points and river pixels. In a single-threaded river with an accurate centerline, the ideal maxDistance value would be about half the river width. However, in reality, the centerlines are not perfect, and large islands may separate pixels from their nearest centerline. Unfortunately, increasing maxDistance has a large computational penalty, so some tweaking is required to get an optimal value. We can set geodeticDistance to false to regain some computational efficiency, because we are not worried about the accuracy of the distances.

A code for identifying rivers from other types of water bodies. The step includes cumulative cost mapping to find pixels connected to a reference centerline, followed by 15 steps.

Code Checkpoint A24b. The book’s repository contains a script that shows what your code should look like at this point.

2.1.2 Obtain River Centerline and Width

After processing the image to create a river mask, we will use existing functions from RivWidthCloud to process the image further to obtain river centerlines and widths. Here, we will call RivWidthCloud functions directly, taking advantage of the ability to use functions from another Earth Engine script (using the require functionality to load another script as a module). We will explain the usage and purpose of the RivWidthCloud functions used here.

There are three major steps involved in obtaining river widths from a given river mask:

  1. 1.

    Calculate one-pixel-width river centerlines.

  2. 2.

    Estimate the direction orthogonal to the flow direction for each centerline pixel.

  3. 3.

    Quantify river width on the channel mask along the orthogonal directions.

Extract River Centerline

We rely on morphological image analysis techniques to extract a river centerline. This process involves three steps:

  1. 1.

    Using distance transform to enhance pixels near the centerline of the river.

  2. 2.

    Using gradient to further isolate the centerline pixel having local minimal gradient values.

  3. 3.

    Cleaning the raw centerline by removing spurious centerlines.

First, a distance transform is applied to the river mask, resulting in a raster image where the value of each water pixel in the river mask is replaced by the closest distance to the shore. This step is done by using the CalcDistanceMap function from RWC. From Fig. 2a, we can see that, in the distance transform, the center of the river has the highest values.

Fig. 43.2
An illustration of information on a river mask depicts 2 water masks, one blurred and another with a dotted line across the center, with two zoomed-in views of the centerline after skeletonization and pruning.

Steps extracting river centerline: a distance transform of a river mask; b gradient of the distance map (a); c raw centerline after skeletonization; d centerline after pruning

A code for importing existing functions from Rivwidthcloud has 10 lines. The steps include the calculation distance from the shoreline using distance transform.

Second, to isolate the centerline of the river, we apply a gradient calculation to the distance raster. If we treat the distance raster as a digital elevation model (DEM), then the locations of the river centerline can be visualized as ridgelines. They will thus have minimal gradient value. The gradient calculation is important, as it converts a local property of the centerline (local maximum distance) to a global property (global minimal gradient) to allow extraction of the centerline with a fixed gradient threshold (Fig. 2b). We use a 0.9 threshold (recommended for RivWidth (Pavelsky and Smith 2008) and RWC) to extract the centerline pixels from the gradient image. However, the resulting initial centerline is not always one pixel wide. To ensure a one-pixel-wide centerline, iterative image skeletonization is applied to thin the initial centerline (Fig. 2c).

A code to calculate the gradient of distance raster. The steps include three different ways to calculate the gradient, by default the function uses the second approach, for details on kernels see the source code, and threshold the gradient raster and derive 1 p x centerline using skeletonization.

Third, the centerline from the previous step will have noise along the shoreline and will have spurious branches resulting from side channels or irregular channel forms that need to be pruned. The pruning function in RWC, CleanCenterline, works by first identifying end pixels of the centerline (i.e., centerline pixels with only one neighboring pixel) and then erasing pixels along the centerline pixels starting from the end pixels for a distance specified by MAXDISTANCE_BRANCH_REMOVAL. It will stop if the specified distance is reached or the erasing encounters a joint pixel (i.e., pixels having more than two neighboring pixels). After pruning, the final centerline should look like Fig. 2d.

A code to prune the centerline to remove spurious branches. A note on the top reads, the last argument of the CleanCenterline function enables the removal of pixels so that the resulting centerline will have 1 p x width in an 8-connected way. Once it is done, it doesn't need to be done a second time.

Estimate Cross-Sectional Direction

Now we will use the centerline we obtained from the previous step to help us measure the widths of the river. River width is often measured along the direction perpendicular to the flow, which we will approximate using the course of its centerline. To estimate cross-sectional directions, we convolve the centerline image with a customized kernel. The square 9 × 9 kernel has been designed so that each pixel on its rim has the radian value of the angle between the line connecting the rim pixel and the center of the kernel and the horizontal x-axis (radian angle 0). The convolution works by overlapping the center of the kernel with the centerline and calculating the average of the values of the rim pixels that overlap the centerline pixels, which corresponds to the cross-sectional direction of the particular centerline point under consideration. Here, we use the function CalculateAngle to estimate the cross-sectional angles. The resulting raster will replace each centerline pixel with the value of the cross-sectional directions in degrees.

A code to calculate the perpendicular direction for the cleaned centerline has 7 lines. The functions include calculating angle, minimum 0, and maximum 360.

Quantify River Widths

To estimate river width, we will be using the RWC function rwGen_waterMask. This function can take any binary water mask image as input to calculate river widths, so long as the band name is “waterMask” and contains the following three properties: (1) crs—UTM projection code, (2) scale—native spatial resolution, and (3) image_id—acting as an identifier for the output widths. This function works by first processing the input water mask to create all the intermediate images mentioned before (channel mask, river mask, centerline, and angle image). Then, it creates a FeatureCollection of cross-sectional lines, each centered on one centerline pixel (from the centerline raster) along the direction estimated in the “Estimate Cross-Sectional Direction” section (from the angle raster) and with a length three times longer than the distance from the centerline point to the closest shoreline pixel (obtained from the distance raster). This FeatureCollection is then used in the Image.reduceRegions method as the FeatureCollection input. With a mean reducer, the result denotes the ratio between the actual river width and the length of the line segment (which is known). Thus, the final river width can be estimated by multiplying the ratio with the length of each line segment in the FeatureCollection. However, the scaling factor of 3 is chosen empirically, and can over- or underestimate the maximum extent of river width. This is because the width, scaled by 3, is the minimal distance from centerline pixels to the nearest shoreline pixels. When aligning line segments along the directions orthogonal to the river centerline, we might encounter situations when the length of these segments is too short to cover the width of the river (underestimation) or too long that they overlap with neighboring river reaches (overestimation). In both cases, the end(s) of the line segment overlaps with a pixel identified as “water” in the channel mask. Thus, additional steps are taken to flag these measurements.

The rwGen_waterMask takes four arguments—maximum search distance (unit: meter) to label river pixels, maximum size of islands (unit: pixel) to be filled in to calculate river mask, distance (unit: meter) to be pruned to clean the raw centerline, and the area of interest to carry out the width calculation. The output of the rwc function is a FeatureCollection with each feature having the properties listed in Table 43.1.

Table 43.1 Output variables from the rwc function
A code to estimate width has 9 lines. The steps include var r w c function equal requires, var r w c function. r w Gen watermark, scale 30, image i d as a o i.

2.1.3 Bank Morphology

In addition to a river’s centerline and width, we can also extract information about the banks of the river, such as their aspect and total length. To identify the banks, we simply dilate the channel mask and compare it to the original channel mask. The difference in these images represents the land pixels adjacent to the channel mask.

An algorithm that reads var bankMask equals channel mask dot focal underscore max left parenthesis 1 right parenthesis dot n e q left parenthesis channel mask colon.

Next, we will calculate the aspect, or compass direction, of the bank faces. We use the Image.cumulativeCost method with the entire river channel as our source to create a new image (bankDistance) with increasing values away from the river channel, similar to an elevation map of river banks. In this image, the banks will “slope” toward the river channel and we can take advantage of the terrain methods in EE. We will call the Terrain.aspect method on the bank distance and select the bank pixels by applying the bank mask. In the end, our bank aspect data will give us the direction from each bank pixel toward the center of the channel. These data could be useful for interpreting any directional preferences in erosion as a result of geological features or thawed permafrost soils from solar radiation.

An algorithm has 7 lines. The commands include var bankDistance equals channel mask dot not parenthesis dot cumulative cost, source channel mask, max distance 1 E 2, geodetic distance false, and var equals e e dot terrain dot aspect of bank distance.

Last, we calculate the length represented by each bank pixel by convolving the bank mask with a Euclidean distance kernel. Sections of bank oriented along the pixel edges will have a value of 30 m per pixel, whereas a diagonal section will have a value of \(\sqrt 2\) * 30 m per pixel.

An algorithm to calculate the distance and length represented by each bank pixel has 15 lines. The functions include distance kernel equals e e dot kernel dot Euclidean, radius 30, units meters, and magnitude 0.5.

Code Checkpoint A24c. The book’s repository contains a script that shows what your code should look like at this point.

2.2 Multitemporal River Width

Refresh the Code Editor to begin with a new script for this section.

In Sect. 43.2.1.2, we walked through the process of extracting the river centerline and width from a given water mask. In that section, we intentionally unpacked the different steps used to extract river centerline and width so that readers can: (1) get an intuitive idea of how the image processes work step by step and see the resulting images at each stage; (2) combine these functions to answer different questions (e.g., readers might only be interested in river centerlines instead of getting all the way to widths). In this section, we will walk you through how to use some high-level functions in RivWidthCloud to more efficiently implement these steps across multiple water mask images to extract time series of widths at a given location. To do this, we need to provide two inputs: a point of interest (longitude, latitude) and a collection of binary water masks. The code below re-introduces a helper function to convert between projections, then accesses other data and functionality.

An algorithm to extract river width. The steps include giving longitude and latitude in decimal degrees and returning E P S G string for the corresponding U T M projection. The output includes import and visualization of surface water mask and surface water occurrence dataset from the J R C.

Remember that the widths from Sect. 43.2.1.2 are stored in a FeatureCollection with multiple width values from different locations along a centerline. To extract the multitemporal river width for a particular location along a river, we only need one width measurement from each water mask. Here, we choose the width for the centerline pixel that is nearest to the given point of interest using the function getNearestCl. This function takes the width FeatureCollection from Sect. 43.2.1.2 as input and returns a feature corresponding to the width closest to the point of interest.

An algorithm for the function to identify the nearest river width to a given location has 8 lines. The functions include var get nearest c l g e n equals function o p o i, var temp equals function of width, width equals width dot map left parenthesis function of f left curly bracket.

Then, we will need to use the map method on the input collection of water masks to apply the rwc to all the water mask images. This will result in a FeatureCollection, each feature of which will contain the width quantified from one image (Fig. 43.3).

Fig. 43.3
A line graph titled river width times series upstream of the Three Gorges Dam plots width versus year. The values are estimated. Width, (1990, 375), (2000, 410), (2010, 470), (2020, 485).

River width time series upstream of the Three Gorges Dam in China. The series shows the abrupt increase in river width around the year 2003, when the dam was completed

An algorithm for multitemporal width extraction. The functions include var polygon equals p o i buffer of 2000, var coords equals p o i dot centroid dot coordinates parenthesis, var l o n equals coords dot get of 0, and var l a t equals coords dot get of 1.
An algorithm for multitemporal width extraction. The functions include var polygon equals p o i buffer of 2000, var coords equals p o i dot centroid dot coordinates parenthesis, var l o n equals coords dot get of 0, and var l a t equals coords dot get of 1.

Code Checkpoint A24d. The book’s repository contains a script that shows what your code should look like at this point.

2.3 Riverbank Erosion

In this section, we will apply the methods we developed in Sect. 43.2.1 to multiple images, calculate the amount of bank erosion, and summarize our results back onto our centerline. Before doing so, we will create a new script that wraps the masking and morphology code in Sects. 43.2.1.1 and 43.2.1.3 into a function called makeChannelmask that has one argument for the year. We return an image with bands for all of the masks and bank calculations, plus a property named ‘year’ that contains the year argument. If you have time, you could try to create this function on your own and then compare with our implementation of it, in the next code checkpoint. Note that we would not expect that your code would look the same, but it should ideally have the same functionality.

Code Checkpoint A24e. The book’s repository contains a script to use to begin this section. You will need to start with that script and paste code below into it.

Change Detection

We will use a section of the Madre de Dios River as our study area for this example because it migrates very quickly, more than 30 m per year in some locations. Our methods will work best if the two channel masks partially overlap everywhere along the length of the river; if there is a gap between the two masks, we will underestimate the amount of change and not be able to calculate the direction of change. As such, we will pick the years 2015 and 2020 for our example. However, in other locations, you may want to increase the time span in order to observe more change. We first create these two sets of channel masks and add them to the map (Fig. 4a).

Fig. 43.4
Two illustrations of two-channel masks from the years 2015 and 2020 are labeled as A and B, respectively. Image B represents the eroded area with contractions in the river's path.

Single meander bend of the Madre de Dios River in Bolivia, showing areas of erosion and accretion: a channel mask from 2015 in blue and channel mask from 2020 in red at 50% transparency; b pixels that represent erosion between 2015 and 2020

An algorithm has 11 lines. The functions include var mask S 1 equals make channel mask of 2015, var mask S 2 equals make channel mask of 2020, and map dot center object of a o i, 13.

Next, we create an image to represent the eroded area (Fig. 4b). We can quickly calculate this by comparing the channel mask in year 2 to the inverse water mask from year 1. In alluvial river systems, avulsions and meander cutoffs can leave fragments of old channels near the river. If the river meanders back into these water bodies, we want to be careful not to count these as fully eroded, which is why we need to compare our river pixels in year 2 (channel mask) to the land pixels in year 1 (inverse water mask). If you were to compare only the channel masks from year to year, water in the floodplains that is captured by the channel migration would be falsely counted as erosion.

An algorithm for pixels that are now the river channel but were previously land. The steps include, var erosion equals masks 2 dot select of channel mask dot and of mask 1 dot select of water mask dot not parenthesis dot rename of erosion semicolon.

Now we are going to approximate the direction of erosion. We will define the direction of erosion by the shortest path through the eroded area from each bank pixel in year 1 to any of the bank pixels in year 2. In reality, meandering rivers often translate their shape downvalley, which breaks our definition of the shortest path between banks. However, the shortest path produces a reasonable approximation in most cases and is easy to calculate. We will again use Image.cumulativeCost to measure the distance using the erosion image as our cost surface. The erosion image has to be dilated by 1 pixel to compensate for the missing edge pixels in the gradient calculations and masked in order to limit the cost paths to within the eroded area.

A code for erosion distance assuming the shortest distance between banks has 13 lines. The function marks max distance as 1 E 3, geodetic distance as true, and erosion distance as false.

Now we can use the same Terrain.aspect method that we used for the bank aspect to calculate the direction of the shortest path along our cost surface. You could also calculate this direction (and the bank aspect in Sect. 43.2.1.3) using the Image.gradient method and then calculating the tangent of the resulting x and y components.

A code for the direction of erosion following the slope of distance has 10 lines. The functions include var erosion direction equals e e dot terrain dot aspect of erosion distance, min as 0, max as math P I, and erosion direction as false.

Connecting to the Centerline

We now have all of our change metrics calculated as images in Earth Engine. We could export these and make maps and figures using these data. However, when analyzing a lot of river data, we often want to look at long profiles of a river or tributary networks in a watershed. In order to do this, we will use reducers to summarize our raster data back onto our vector centerline. The first step is to identify which pixels should be assigned to which centerline points. We will start by calculating a single image representing the distance to any SWORD centerline point with the FeatureCollection.distance method. Next, we will use a convolution with the Laplacian kernel (Chap. 10) as an edge detection method on our distance raster. By convolving the distance to the nearest SWORD node with the Laplacian kernel, we are calculating the second derivative of distance and can find the locations where the distance surface starts sloping toward another SWORD point.

A code to convert the distance to the nearest S W O R D centerline point has 7 codes. The steps include getting second derivatives of the distance and finding the zeros identifies boundaries between centerline points.

Next, we need to create an image where each pixel’s value is set to the unique node identifier of the nearest SWORD centerline point. We will create a two-band image, where the first band is the concavity boundaries found in the last step, and the second band has the unique node identifiers painted on their location. When we reduce this image using the Image.reduceConnectedComponents method, we set all pixels in each region with the corresponding node ID. Last, we need to dilate these pixels to fill in the boundary gaps using a call to the Image.focalMode method (Fig. 43.5).

Fig. 43.5
An inverted U-shaped section of a river has multiple sections in different colors with a dotted line that connects the centerline of the U-shape.

Section of the Madre de Dios River where each pixel is assigned to its closest centerline node

A code to reduce the pixels according to the concavity boundaries and set the value to S W O R D node I D has 10 codes. A note on the top reads that focal mode is used to fill in the empty pixels that were the boundaries.

Summarizing the Data

The final step in this section is to apply a reducer that uses our nodePixels image from the previous step to group our raster data. We will combine the reducer.forEach and reducer.group methods into our own custom function that we can use with different reducers to get our final results. The reducer.forEach method sets up a different reducer and output for each band in our image, which is necessary when we use the reducer.group method. The reducer.group method is conceptually similar to reducer.reduceRegions, except our regions are defined by an image band instead of by polygons. In some cases, the group method is much faster than the reducer.reduceRegions method, particularly if you were to have to convert your regions to polygons in order to provide the input to reducer.reduceRegions. The grouped reducers in our function return a list of dictionaries. However, it is much easier to work with feature collections, so we will map over the list and create a FeatureCollection before returning from the function.

A code to set up a custom reducing function to summarize the data. The steps include create a grouped reducer for each band in the data image, apply the grouped reducer, and convert a list of dictionaries to feature collection.

For some variables—such as the erosion, the channel mask, or the bank length—we want the total number of pixels or bank length, so we will use the Reducer.sum method with our grouped reducer function. For our aspect and directional variables, we need to use the Reducer.circularMean method to find the mean direction. The returned variables sumStats and angleStats are feature collections with properties for our reduced data and the corresponding node ID.

A code has 12 lines. It starts with var data mask equals mask 1 dot add bands of mask 2 dot reducce of e e dot reducer dot any non zero. var sum bands is equal to the watermask, channel mask, bank length.

Finally, we will join these two new feature collections to our original centerline data and print the results (Fig. 43.6).

Fig. 43.6
A list of 32 object properties has entries in 2 columns and 32 rows with 3 highlighted sections. They include bank aspect, bank length, channel mask, channel mask underscore 1 on the top, erosion, erosion direction in the middle, and water mask, water mask underscore 1 on the bottom.

Updated list of properties in our centerline dataset; new properties are outlined in black. The erosion and mask fields are in units of pixels, but you could convert to area using the Image.pixelArea method on the masks

A code has 7 lines. It starts with var vector data equals sword dot filler bounds of a o i dot map of function of feat and ends with map dot add layer left parentheses vector data curly brackets final data .

Code Checkpoint A24f. The book’s repository contains a script that shows what your code should look like at this point.

This workflow can be used to add many new properties to the river centerlines based on raster calculations. For example, we calculated the amount of erosion between these two years, but you could use very similar code to calculate the amount of accretion that occurred. Other interesting properties of the river, like the slope of the banks from a DEM, could be calculated and added to our centerline dataset.

3 Synthesis

Assignment 1. RivWidthCloud can estimate individual channel width in the case of multichannel rivers. Change the AOI to a multichannel river and observe the resulting centerline and width data. Note down things you think are different from the single-channel case.

Assignment 2. Answer the following question. When rivers experience both variable width over time and bank migration, how can we apply the methods in this chapter to distinguish these two types of changes?

4 Conclusion

In this chapter, we provide ways in which Earth Engine can be used to aid river planview morphological studies. In the first half of the chapter, we show how to distinguish river pixels from other types of water bodies, as well as how to extract river centerline, river width, bank aspect, and length. In the second half of the chapter, we give examples of how to apply these methods to multitemporal image collections to estimate changes in river widths for rivers that have stable channels and to estimate bank erosion for rivers that tend to meander quickly. The analysis makes use of both raster- and vector-based methods provided by Earth Engine to help quantify river morphology. More importantly, these methods can be applied at scale.