Assignment 9

Image analysis in Google Earth Engine

This assignment will use Google Earth Engine to create an app displaying elevation and slope images. Users will be able to slide a divider back and forth to switch the visible layers, making it easy to compare elevation, slope, and a basemap.

You must have a Google account and request access to Earth Engine.

Table of Contents

Finished Example

https://geog4046.users.earthengine.app/view/highland-road-elevation-explorer

Code Editor

Go to the Google Earth Engine Code Editor. Familiarize yourself with the interface, especially the Code Editor panel in the center, the Map at the bottom, and the Inspector/Console/Tasks panel on the right. The Script panel to the top left is where you can create new files and folders and see a list of your saved scripts.
Earth Engine Code Editor
Labeled interface of the Earth Engine Code Editor. Source: Earth Engine documentation.

Data

We will use the USGS National Elevation Dataset hosted by Google in the Earth Engine Data Catalog. It is a digital elevation model (DEM) image. Each pixel is 1/3 arc-second, or about 10x10 meters, with pixel values in meters above sea level.

The slope layer that we will display is not a separate dataset; it will be calculated on the fly by Earth Engine, demonstrating the platform’s ability to run pixel-level analysis. Earth Engine calculates a pixel’s slope based on the elevation difference with neighboring pixels using trigonometry.

Preview the dataset before getting started. In the search bar at the top of the Google Earth Engine Code Editor, search for USGS National Elevation Dataset and click import. Then paste this line of JavaScript into the editor panel and click Run:

Map.addLayer(image, {min:0, max:3000});

Click the Inspector tab in the top right panel, then click a place on the map. The inspector will print the longitude, latitude, and elevation of the pixel you clicked. Adjust the min and max (elevation in meters) to see how it affects the display.

Click Reset > Clear Script to clear the code editor before starting the assignment.

Steps

The steps below build an app piece by piece, with short snippets of code gradually adding functionality to the app. The steps work for a Baton Rouge map but can be adapted for any area. Take a look at the finished app to see what we’re working towards.

Click Save to put a copy of your script into a repository, seen in the top left panel of the code editor. Save your work often while going through these steps.

Part 1: Display maps

  1. First, we will define some variables to make it easy to change our map later.

    Set minimum and maximum values for elevation in meters and slope in degrees for your area of interest. These Baton Rouge values would be very different for the Grand Canyon. Adjust accordingly.

    // Minimum and maximum elevation in meters and slope in degrees for your area (approx.). Affects color.
    var minElevation = 3;
    var maxElevation = 20;
    var minSlope = 0;
    var maxSlope = 30;
    
  2. Set the color scheme for the DEM image we’re about to import. In addition to basic color names, you can use HTML color codes such as 925314. Add colors to the list separated by commas and enclosed in quotes.
    // Elevation color gradient from lowest to highest. Add more colors for areas with more variation.
    var demColor = ['green', 'yellow', 'orange'];
    
  3. The wipe effect we’ll use to compare layers is achieved with two maps on top of each other. The slider bar hides one map and reveals the other when the user drags it back and forth. Therefore, create two empty maps to begin with. Because we have two maps, you’ll notice we often repeat commands.
    // Remove default map, create two maps for elevation and slope side by side
    ui.root.clear();
    var Map1 = ui.Map();
    var Map2 = ui.Map();
    ui.root.add(Map1);
    ui.root.add(Map2);
    
  4. Center the map on your location with appropriate zoom (4-17). Use the inspector to get lon/lat.
    // Set the map center and zoom level
    Map1.setCenter(-91.167275, 30.398996, 17);
    
  5. Use the Linker function to sync the movement of both maps.
    // Sync the pan and zoom of the two maps
    ui.Map.Linker([Map1, Map2]);
    
  6. Now we’re ready to add data. Import the DEM and add it with the min, max, and color options we specified in the first steps.
    // Load the digital elevation model (DEM) layer and add it to the default map.
    var dem = ee.Image("USGS/NED");
    Map1.addLayer(dem, {min: minElevation, max: maxElevation, palette: demColor}, 'Elevation');
    

    Now would be a good time to adjust your min, max, and color settings if needed.

  7. With the DEM added, we can use the slope function to calculate how steep/flat the land is at each pixel.
    // Calculate pixel slope based on the DEM and add to the 2nd map
    var slope = ee.Terrain.slope(dem);
    Map2.addLayer(slope, {min: minSlope, max: maxSlope}, 'Slope');
    
  8. Our maps are still side-by-side without a slider tool, so we need to add it, specifying the slide direction is horizontal.
    // Split the map panels so there is a swipe bar 
    var splitPanel = ui.SplitPanel({firstPanel: Map1, secondPanel: Map2, orientation: 'horizontal', wipe: true, style: {stretch: 'both'}});
    ui.root.widgets().reset([splitPanel]);
    

Part 2. Add a side panel

The code snippets in this section cannot be copied and pasted one after the other. You must modify one of the lines every time you add the additional snippets, so pay close attention to each step.

  1. To display information about our app and eventually print pixel values, we will create a side panel. For now, it will be a blank white space. We will modify these lines to add content in the following steps.
    // Create the side panel where info will be printed
    var sidePanel = ui.Panel([], 'flow', {width: '20%'}); // this line will be modified
    ui.root.add(sidePanel);
    
  2. Create a title label for the side panel. We must define the title before we add it to the side panel, so add this first line above the var sidePanel definition in the previous snippet and then modify the var sidePanel line. Do not add another copy of the lines from the previous step; modify the existing line. In the sidePanel definition, add titleLabel between the square brackets.

    var titleLabel = ui.Label({value: 'Highland Road Elevation Explorer', style: {fontSize: '2em'}});
     
    // Create the side panel where info will be printed
    var sidePanel = ui.Panel([titleLabel], 'flow', {width: '20%'});
    ui.root.add(sidePanel);
    
  3. Under the title line, add a variable for a label to give our audience basic info about the app. Then update the side panel variable to include the new variable between the square brackets.
    var titleLabel = ui.Label({value: 'Highland Road Elevation Explorer', style: {fontSize: '2em'}});
    var instructionsLabel = ui.Label('Explore elevation changes in an otherwise flat landscape along the historic Highland Road running along the natural floodplain of the Mississippi River. Slide the handle left and right to visually compare elevation and slope. Slide far right for elevation map controls.');
     
    // Create the side panel where info will be printed
    var sidePanel = ui.Panel([titleLabel,instructionsLabel], 'flow', {width: '20%'});
    ui.root.add(sidePanel);
    
  4. Next we will create labels where the elevation and slope values will be printed when the user clicks the map. They won’t work at this point; they are placeholders for now.
    var titleLabel = ui.Label({value: 'Highland Road Elevation Explorer', style: {fontSize: '2em'}});
    var instructionsLabel = ui.Label('Slide the handle left and right to visually compare elevation and slope. Slide far right for elevation map controls.');
    var valuesLabel = ui.Label('Elevation: (click map)\nSlope: (click map)', {whiteSpace: 'pre'});
     
    // Create the side panel where info will be printed
    var sidePanel = ui.Panel([titleLabel,instructionsLabel,valuesLabel], 'flow', {width: '20%'});
    ui.root.add(sidePanel);
    
  5. For the final addition to the side panel, link to the dataset details page for the DEM layer.
    var titleLabel = ui.Label({value: 'Highland Road Elevation Explorer', style: {fontSize: '2em'}});
    var instructionsLabel = ui.Label('Slide the handle left and right to visually compare elevation and slope. Slide far right for elevation map controls.');
    var valuesLabel = ui.Label('Elevation: (click map)\nSlope: (click map)', {whiteSpace: 'pre'});
    var sourceLink = ui.Label({
      value: 'Data source: USGS National Elevation Dataset', 
      targetUrl: 'https://developers.google.com/earth-engine/datasets/catalog/USGS_NED'
    });
     
    // Create the side panel where info will be printed
    var sidePanel = ui.Panel([titleLabel,instructionsLabel,valuesLabel,sourceLink], 'flow', {width: '20%'});
    ui.root.add(sidePanel);
    

    You could continue to add labels to the side panel by copying these examples, just be sure to update the var sidePanel line with the variable names between the square brackets. The order of the labels in the panel depends on the order of the variable names between the brackets, from top to bottom: [titleLabel,instructionsLabel,valuesLabel,sourceLink].

Part 3: Pixel values and finishing touches

The next step has a chunk of more complex code known as a function, whose lines all work together to perform a single task, so paste the whole snippet into your script instead of going line-by-line.

  1. Define a function that takes lat/lon coordinates of a pixel as input. The function sends the coordinates to Earth Engine to get the pixel’s elevation and slope values from the dataset. Then the function prints those values to our side panel in valuesLabel, replacing “(click map)” with the pixel values.
    // Query a clicked pixel for elevation and slope.
    var getPixelValues = function(coords) {
      // Print "Loading..." in the valuesLabel first in case query is slow. 
      valuesLabel.setValue('Elevation: Loading...\nSlope: Loading...').style({whiteSpace: 'pre'});
      // Send the clicked location to the server to request an elevation value
      var location = ee.Geometry.Point(coords.lon, coords.lat);
      dem.reduceRegion(ee.Reducer.first(), location, 10).evaluate(function(val){
     var demText = val.elevation.toFixed(2) + ' meters';
     // Request slope value from the server and print both elevation and slope to valuesLabel
     slope.reduceRegion(ee.Reducer.first(), location, 10).evaluate(function(val){
       var slopeText = val.slope.toFixed(0) + ' degrees';
       valuesLabel.setValue('Elevation: ' + demText + '\nSlope: ' + slopeText).style({whiteSpace: 'pre'});
     });
      });  
    }
    

    Note: the code above only defines a function, it does not “call” it, and we haven’t told the map to respond to a user click. Therefore, the code above doesn’t do anything yet.

  2. We can use the lines below to create something in JavaScript called a “listener”. Listeners wait for some kind of event to happen in the browser, such as a mouse click, scroll wheel, or completed page load. You can tell listeners what to do when those events happen. In our case, we want our app to listen for mouse clicks on either map, and then run our getPixelValues function, defined in the previous step. The lat/lon where the user clicked will be pass along to our function.
    // Tell the maps to listen for a click and run our getPixelValues function. 
    Map1.onClick(getPixelValues);
    Map2.onClick(getPixelValues);
    
  3. Finally, the cherry on top: change the user’s mouse pointer from a grabber hand to a crosshair. Now they can click those pixels with pinpoint accuracy!
    // Style the mouse pointer as a crosshair
    Map1.style().set('cursor', 'crosshair');
    Map2.style().set('cursor', 'crosshair');
    

Try It

  1. Choose a new area of interest that is not in East Baton Rouge Parish:
    a. Change map center and zoom.
    b. Change the min/max elevation and slope, appropriate for the new location.
    c. For a location outside of the United States, you can use the lower-resolution CGIAR/SRTM90_V4 dataset.
  2. Modify the DEM color palette (use colors that are distinguishable; see this thread for ideas):
    a. Change at least one of the existing colors.
    b. Add at least one color.
  3. Optional: add a color palette to the slope layer.
  4. Take a screenshot of your map and side panel.
  5. Turn your map into an app.
    a. Click Apps > New App.
    b. Give it an App Name.
    c. Create a new Google Cloud Project or select an existing project.
    d. Check Feature this app in your Public Apps Gallery.
    e. Add your screenshot.
    f. Provide a brief Description.
    g. Publish.

Checklist

  1. A publicly accessible app on earthengine.app.
  2. App shows elevation and slope layers.
  3. Map center not in Baton Rouge.
  4. Appropriate zoom level for app’s purpose (Highland Road close; Grand Canyon far).
  5. Color palette different from green-yellow-orange example, at least four colors.
  6. Swipe tool to compare elevation and slope on synced maps.
  7. Side panel with at least title, description, pixel values, and data source appropriate for your app.
  8. Clicking the map displays pixel values in side panel correctly.

Submit

  1. Example: https://yourname.users.earthengine.app/view/your-app
Top