Csci 210 Lab: Flat areas on terrains

(Laura Toma, developed with the support of NSF award no. 0728780)

Overview

Terrain elevation models are used to model and understand a wealth of natural processes in geosciences and beyond. Like in the previous labs, in this lab you will work with grid terrains. You will extend your GridGIS by adding functionality to compute the flow direction grid, and to compute and classify the flat areas of the terrain.

Flow direction (FD)

First, let's talk (again) about flow direction. We talked about flow direction implicitly in the last lab, as part of modeling flow. We said that we model the flow of water as following the direction of steepest descent, and you wrote a method flowsInto(i,j,k,l) that returns true if point (i,j) flows into point (k,l); in other words, the flow direction of (i,j) is towards (k,l).

In this lab you'll build on the previous methods and write code to compute the flow direction of each point in a grid. Naturally, this defines a grid called the FD grid. The FD at a point is defined as the direction of its steepest downslope neighbor. The neighbors are coded as follows:

6 7 8
5 x 1
4 3 2

That is, if the FD of the cell marked with an x is towards its East neighbor, its flow direction should be assigned to 1; if it's toward its SE neighbor, its FD is 2; and so on.

When all neighbors of p have elevation greater or equal to the elevation of p, the FD of p cannot be set. This includes two cases: when p is part of a pit, and when p is part of a plateau area.

To summarize, the FD of a point p in the grid is defined as follows:

Your first task is to write a method computeFlowDirection() which computes and returns a FD grid.

Grid computeFlowDirection()
As a helper to this function, you will write a function that computes the FD for a specified position (i,j), and you will call it in a loop for all i and j.

For example, for test2.asc:

The grid is: 
9 9 9 9 9
8 7 6 7 8
7 6 5 6 7
6 5 4 5 6
5 4 3 4 5

The FD grid is:
2 2 3 4 4
2 2 3 4 4
2 2 3 4 4
...

Once you compute the FD grid, try to visualize it using your method that renders elevation grids.

Flat areas

You may have noticed, looking at the flow grid from your previous lab, that you can see many small, scattered streams instead of a few long rivers that collected the water from the entire terrain. This is because many points in the terrain have undefined flow directions, and the water "stops" at those points and is not routed further. To improve this, the idea is to identify the points where the FD is undefined (i.e. has value 0), and somehow assign them FD.

Note that the 0 values in the FD grid represent the flat areas in the terrain. Remember the definition of blobs from class --- in this case we are interested in blobs of 0's, not of 1's. The first step is to identify the flat areas, and classify them.

We distinguish two types of flat areas: plateaus and sinks.

The figure below shows a grid with the elevation values shown in green and the flow directions shown with black arrows. The blob at elevation 12 defines a flat area which is a plateau, because some of the points on its boundary have downslope neighbors (and thus flow outside). The blob at elevation 9 is a sink, because all points surrounding the flat area are higher. The area shown in gray represents the points whose FD is 0 (undefined). The function labelFlat() should re-set all these 0's to either PLATEAU or SINK, as explained below.

Labeling flat areas

Your second task is to classify each flat area as being either a plateau or sink, and re-label each point with FD=0 accordingly.

final static int SINK = -1;
final static int PLATEAU = -2;

/* this function re-labels all the points with FD=0 to FD=SINK or FD=PLATEAU, depending on whether the respective blob (flat area) is a sink or a plateau
*/ 
void labelFlat(Grid fd)

You'll break this into a couple of smaller functions. The first one is findBlobType.

/*
When this function is called, the point (i,j) must be inside the grid
and it must be part of a blob, i.e. its FD=0.  This function finds the
type of flat area and returns its type (SINK or PLATEAU). As a side-effect, part of blob may be marked. 
*/

int findBlobType(Grid fd, Grid elevation,  i,j) {
    
   if (i,j) is outside the grid or if it's FD is not 0 ==> error
   
   //the blob containing (i,j) is a sink if ALL the neighbors of the
   //blob which are not part of the blob (i.e. their FD is a value 1 through 8) have
   //elevations higher than the blob. Otherwise it's a plateau.

   mark (i,j)  to avoid infinite recursion
   for each neighbor (i+k, j+l) of (i,j)
        if the neighbor is outside the grid, skip it
        if the neighbor has FD = NODATA, skip it
        if the neighbor has a valid FD (a value 1 through 8)
                 then we found a neighbor of this blob; check its elevation,
                 and  if elevation(i+k, j+l) is not higher than elevation(i,j),
                 then return PLATEAU
        if the neighhbor has FD=0 and is marked, skip it 
        if the neighbot has FD=0 and is unmarked
                 call findBlobTYpe recursively on it
                 if this returns PLATEAU, return PLATEAU immediately 


   //if we are here, all neighbors are higher or none of the recursive
   //calls returned a plateau. so return SINK
   
}
The second one is labelBlob.

/*

The blob containing (i,j) is partially  marked. This function labels all the
points in the blob at position (i,j) with the type given as argument.

*/

void labelBlob(Grid fd, int i, int j, int type) {

    //recursive formulation
    basecases:  if (i,j) is ouside grid or fd (i,j) is not 0 or marked, return

    if fd(i,j) is 0 or marked,  set it as type and recurse on
    all its neighbors
}
So far, you have findBlobType and labelBlob which must be called on a point (i,j). Put everything together in a function that re-labels the points with FD=0 in the FD grid, as follows:

void labelFlat(Grid fd) {
   for i = 0;  i< nrows; i++)
     for (j=0; j< ncols; i++)
         if FD(i,j)=0 then findBlobType(fd, i,j) labelBlob(fd, i, j) with the resulting type. As a result, the entire blob containing (i,j) will be labeled. 
         else: this point is either not a flat area, or its been
already labeled, so continue

}
Example
The grid is: 
  ...

The FD grid:
  ...

The re-labeled FD grid is:
 ... 

Test grids


Have fun!