NCSU GIS/MEA582:
Geospatial Modeling and Analysis

Geospatial Analysis I: global, zonal and focal operations, map algebra

Resources:

To run r.mapcalc expressions, you can either run the entire command in the GUI Console, or in case of any problems, type or copy the expression (without the r.mapcalc) in the GRASS GIS Raster Map Calculator which can be launched from Layer Manager toolbar.

Text files with color rules:

Start GRASS GIS

In startup pannel set GRASS GIS Database Directory to path to datasets, for example on MS Windows, C:\Users\myname\grassdata. For GRASS Location select nc_spm_08_grass7 (North Carolina, State Plane, meters) and for GRASS Mapset create a new mapset (called e.g. HW_map_algebra) and click Start GRASS session.
grass83

Change working directory:
Settings > GRASS working environment > Change working directory > select/create any directory
or type cd (stands for change directory) into the GUI Console and hit Enter:

cd

Download all text files with color rules (see above) to the selected directory. Now you can use the commands from the assignment requiring the text file without the need to specify the full path to the file.

Compute summaries

Compute areas for each category at two different resolutions.

Are results equal? Explain in detail why (see also Lecture 1). Copy and paste the report from the output window or save the report in a text file: Output window > Save. Use fixed width font (e.g., Courier, Andale Mono in your report to preserve formatting).

g.region raster=landuse96_28m res=12 -ap
r.report landuse96_28m unit=c,h,p
g.region raster=landuse96_28m res=30 -ap
r.report landuse96_28m unit=c,h,p

Compute areas for each category of land use for each zipcode. Compare 27601 Raleigh with 27511 Cary. Include only the relevant part of the table in your report.

r.report zipcodes,landuse96_28m unit=h,p

Compute zonal statistics maps representing average slope for each basin.
Add legends using Add map elements in Map Display toolbar. Reminder: d.out.file means Save to graphics file for your report.

g.region raster=slope -p
r.stats.zonal base=basin_50K cover=slope method=average output=slope_avgbasin
r.colors slope_avgbasin color=gyr
d.rast slope_avgbasin
d.legend slope_avgbasin at=90,50,5,8
d.vect streams color=15:25:110
d.out.file slope_avgbasin

Compute zonal statistics maps representing most common land use for each basin.
To get the best result, make sure you use all the flags from the example below.

g.region raster=landuse96_28m -p
r.mode base=basin_50K cover=landuse96_28m output=landuse96_modebasin
d.rast landuse96_modebasin
d.vect streams
d.legend landuse96_modebasin at=40,20,2,5 -n -f -c
d.out.file landuse96_modezip

Apply neighborhood operators

Use neighborhood operator to compute land use diversity map and create a map of locations with the most homogeneous (single category) landuse.
Which land use covers the largest area with a single landuse category(most homogeneous areas)?

First, remove all previously added map layers from the Layer Manager, then compute and display the land use diversity map. Use map algebra to extract the single category areas and find out the area totals for each category using the report tool. Adjust the legend size and placement as needed using right click and mouse.

d.erase
r.info -g landuse96_28m
g.region raster=landuse96_28m -p
r.neighbors landuse96_28m output=lu_divers method=diversity size=7
d.rast lu_divers
d.legend lu_divers at=70,15,5,10 -v
d.vect streets_wake co=white
r.report lu_divers unit=p
d.out.file lu_diversity_map
r.mapcalc "landuse_1cat = if(lu_divers == 1, landuse96_28m, null())"
r.colors landuse_1cat raster=landuse96_28m
r.category landuse_1cat raster=landuse96_28m
r.report landuse_1cat unit=h,p -n
d.rast landuse_1cat
d.legend -c landuse_1cat
d.out.file lu_signle_map

Visualy compare the SRTM elevation map "elev_srtm_30m" with lidar-based elevation map "elevation". Use neighborhood operator to smooth the SRTM elevation map and compare the summary statistical measures for the original and smoothed SRTM DEM. How does the size of the neighborhood influence the result? (You can test different sizes yourself.)

d.erase
g.region raster=elev_srtm_30m -p
d.rast elevation
d.legend elevation
r.colors elev_srtm_30m raster=elevation
d.rast elev_srtm_30m
d.legend elev_srtm_30m
d.out.file srtm_dem_original
r.neighbors elev_srtm_30m output=elev_srtm30m_sm5 method=average size=5
d.rast elev_srtm30m_sm5
d.out.file srtm_dem_smoothed
r.univar elev_srtm_30m
r.univar elev_srtm30m_sm5

Patch multiple raster layers into a single raster

Patch raster tiles for lidar based, 6m res. DEM for Centennial Campus.
d.erase
g.region raster=el_D793_6m,el_D783_6m,el_D782_6m,el_D792_6m -p
r.patch input=el_D793_6m,el_D783_6m,el_D782_6m,el_D792_6m output=elevlidD_6m
r.colors elevlidD_6m raster=elevation
d.rast elevlidD_6m

Map Algebra

See r.mapcalc manual page for syntax and functions. If you are getting en error when running r.mapcalc in GUI Console or the system command line, launch the GUI version from Layer Manager toolbar.

NDVI

Compute Normalized Difference Vegetation Index (NDVI).
Explain the difference between floating point and integer handling in ndvi1, ndvi2 and ndvi3 result. Make sure to zoom into computational area after changing the region.
d.erase
g.region raster=lsat7_2002_40 -p
r.mapcalc "ndvi1 = (lsat7_2002_40 - lsat7_2002_30) / (lsat7_2002_40 + lsat7_2002_30)"
r.mapcalc "ndvi2 = 1.0 * (lsat7_2002_40 - lsat7_2002_30) / (lsat7_2002_40 + lsat7_2002_30)"
r.mapcalc "ndvi3 = float(lsat7_2002_40 - lsat7_2002_30) / float(lsat7_2002_40 + lsat7_2002_30)"
r.info -r ndvi1
r.info -r ndvi2
r.info -r ndvi3
r.colors ndvi3 color=ndvi
d.rast ndvi3
d.legend ndvi3
d.out.file ndvi3
Note that this is a simplified, map algebra example, for computing various vegetation indices in GRASS GIS, we would use the i.vi module after performing atmospheric corrections.

Difference between DEM and DSM

Explore the difference between the SRTM DSM "elev_srtm_30m" and lidar-based NED DEM "elev_ned_30m". First, compute the map of elevation differences:
g.region raster=elev_ned_30m -p
r.mapcalc "srtm_ned30_dif = elev_srtm_30m - elev_ned_30m"

Find the range of differences and create a divergent color table to distinguish the negative and positive values:

r.info -r srtm_ned30_dif

Assign custom color table srtmneddiff_color.txt.
GUI: Right click on layer > Properties > Set color table > Colors > Path to rules file.

r.colors srtm_ned30_dif rules=srtmneddiff_color.txt

Zoom to computational region and switch off previous map layers. Display the difference map layer:

d.erase
d.rast srtm_ned30_dif
d.legend srtm_ned30_dif at=70,15,5,10
d.out.file srtm_ned30_dif
r.univar elev_srtm_30m
r.univar elev_ned_30m
d.histogram srtm_ned30_dif

Are the elevations in "elev_srtm_30m" mostly higher or mostly lower than in "elev_ned_30m"? Which result will you use to answer the above question - the "srtm_ned30_dif" map or the numbers provided by r.univar or the histogram? Are there any values in the maps used or generated in this section that are not realistic?

Working with if statements

Create map of urban areas (high density and low density class) with 0 class elsewhere.
g.region raster=landuse96_28m -p
r.mapcalc "urban1_30m = if(landuse96_28m == 1,1,0) + if(landuse96_28m == 2,2,0)"
d.rast urban1_30m
d.legend urban1_30m at=10,30,5,8

Alternatively with logical 'or' operator and null elsewhere:

r.mapcalc "urban2_30m = if(landuse96_28m == 1 || landuse96_28m == 2,landuse96_28m,null())"
d.rast urban2_30m

Handling null values

Create mask for low lying developed areas (e.g. vulnerable to flooding) with elevation between 60 and 100m and land use 1 or 2.
Set the region, display the input maps and create a MASK.
Before you start new computations, remove or switch off previous map layers in the Layer Manger. You may also zoom to computational region in Map Display once you set a new one.

g.region raster=elevation -p
d.erase
d.rast elevation
d.rast landuse96_28m
r.mapcalc "low_elev_developed = if((elevation < 100 && elevation > 60) && (landuse96_28m == 1 || landuse96_28m == 2),1,null())"
r.mask raster=low_elev_developed
Command r.mask creates a raster map "MASK" in your mapset.

Remove "low_elev_developed" layer if it was added.

Display watersheds to see the mask effect:

d.rast basin_50K
d.out.file basin_masked

Disable mask, and display basin_50K again to show that the mask was removed.

r.mask -r
d.rast basin_50K

Using coordinates of moving window in map algebra

Replace section of SRTM DSM with NED DEM elevation.
Try to explain how this r.mapcalc expression works.
r.mapcalc "elev_combined = if(y() < 224274. && x() > 637455., elevation, elev_srtm_30m)"

Optional - various additional useful tasks

Tilted plane

Create a raster map representing tilted plane (e.g., geologic fault):
g.region rural_1m -p
r.mapcalc "tiltplane = 0.2*(0.1*row()+col())+50"
r.mapcalc "tiltpl_under = if(tiltplane < elev_lid792_1m + 2,tiltplane,null())"

View the elevation surface and subsurface plane in 3D. Switch off all layers in the layer manager except for elev_lid792_1m and tiltpl_under. Change display to 3D view, adjust viewing position to a view from South. Save an image for your report.

Map subsets

Use map algebra to create map subsets.
Change region to the airphoto tile 792 and resolution 10m. Since we will work in different area, it is a good idea to remove all previously used map layers from Layers in the Layer Manager.
g.region raster=ortho_2001_t792_1m res=10 -ap
d.erase
d.rast ortho_2001_t792_1m

Create a subset of the map elevation for this subregion.

r.mapcalc "elevation_792_10m = elevation"
d.rast elevation_792_10m

Zoom out to see that it is a subset.

Work with NULL and MASK

Set the mask and check its effect.
d.rast elevation
d.vect streets_wake
r.mask raster=urban maskcats=55
d.rast elevation

Remove mask:

r.mask -r

Zonal statistics

First, set the computational region:
g.region raster=urban2_30m
Then, compute % area for each category in each zipcode.
r.stats -pl zipcodes,urban2_30m

Working with relative coordinates

Enter the expression on a single line without \
Again, it is a good idea to remove the previously used map layers before we start to work on a new task.
g.region raster=elev_srtm_30m -p
d.erase
r.mapcalc "elev_srtm30m_smooth = (elev_srtm_30m[-1,-1]   \
           + elev_srtm_30m[-1,0] + elev_srtm_30m[-1,1] \
           + elev_srtm_30m[0,-1] + elev_srtm_30m[0,0]  \
           + elev_srtm_30m[0,1]  + elev_srtm_30m[1,-1] \
           + elev_srtm_30m[1,0]  + elev_srtm_30m[1,1])/9."

Assign the resulting map the same color table as the original. Compare global statistics

r.colors elev_srtm30m_smooth raster=elev_srtm_30m
r.univar elev_srtm_30m
r.univar elev_srtm30m_smooth
d.rast elev_srtm_30m
d.rast elev_srtm30m_smooth