Earth Engine Workshop

Remote Sensing from the Cloud

SEFS 521

Today's Objectives

1. Opening and Navigating the API

2. Loading & Exploring Feature Collections

3. Loading & Visualizing Images

4. Dealing with Feature Collections

5. Charts & Figures

The Background

We are going to find the location in Seattle with the highest elevation and the least rainfall. We are also going to consider the slope around that area (for riding bikes). We are going to do all of this in Earth Engine.

Opening and Navigating the API

Use the following link to access the Google Earth Engine Javascript API:

    https://code.earthengine.google.com/

In the API click on the "Docs" tab on the left. This is a searchable wiki for all the algorithms.

Go up to the "Help" button on the right. This is important. I use this all the time to go to the developers forum, the real help documentation, the dataset request form, and more.

Now go to "Scripts" and click "New Script". Title your script "My First Earth Engine Script Ever" or whatever.

In the coding console, type the following

    // I love donuts. 

This is how you comment code. Now change the comment to read:

    // SEFS 521 Earth Engine Demo Script

and go to the top and click "Run". Your code will run -even though it is basically empty. Now click "Save". You can tell when a script hasn't been saved because it will have a * next to the title. Now that you have saved the script, click "Get Link". If you look at your browser, you will see a new unique URL gets generated. You can copy this link and use it to share scripts with people.

Loading & Exploring Feature Collections

Someone else was kind enough to upload the NHD watershed boundaries using Fusion Tables. Load a Feature Collection from Fusion Tables using the following command:

var watersheds = ee.FeatureCollection("ft:1IXfrLpTHX4dtdj1LcNXjJADBB-d93rkdJ9acSEWK")
Map.addLayer(watersheds, null, 'watersheds')

A map should appear in the mapping window that looks sort of like this:

You can go down to the mapping window and play with the zoom, the background and the transparency.

Let's explore this feature collection a little bit. First, type this into your coding console:

print(watersheds)

Click around. How many features, or HUC6 watersheds are there? What kind of information or properties does each feature have?

Now try this.

print(watersheds, 'watersheds')

What changed?

We only care about Seattle, so we want to isolate our watershed name. To do this, click on the "Inspector" tab up on the right next to the Console. Then, scroll over to Seattle, click on the map and see what pops up in the "Inspector" window. What is the name of the HUC 6 that Seattle is located in?

In order to filter the feature collection down to just that region, use the following:

var pugetSound= watersheds.filter(ee.Filter.contains('name', 'Puget Sound'));
Map.addLayer(pugetSound, null, 'Puget Sound')    

Loading and Visualizing Images

Elevation

Let's load a couple different interesting images into our window.

var elevation = ee.Image("USGS/NED")
Map.addLayer(elevation, {min:0, max:3000, palette:['000000',"ffffff"]},"elevation");

Whoa. Look at the power! This is a digital elevation model for the whole United States with a resolution of 10 meters! While cool, we only want the elevation in our study area.

Map.addLayer(elevation.clip(pugetSound), {min:0, max:3000, palette:['000000',"ffffff"]},"elevation2");

Notice your screen is entirely blank. What do you suspect happened?

You can fix this by adjusting the min and the max. Try this for example:

Map.addLayer(elevation.clip(pugetSound), {min:0, max:300, palette:['000000',"ffffff"]},"elevation2");

Slope

Let's calculate slope, just because it is so easy.

var slope = ee.Terrain.slope(elevation); 
var visslope = {min:0, max:10, palette:['00ff00',"ff0000"]};
Map.addLayer(slope.clip(pugetSound),visslope,"slope");

Dealing with Image Collections

Precipitation

Precipitation comes as an Image Collection where each image in the collection represents 1 day of data. These images are all stacked up in a collection. Let's get just the precipitation data for a specific range of dates.

var annualPrecip = ee.Image(ee.ImageCollection('IDAHO_EPSCOR/GRIDMET').filterDate
('2015-01-01', '2015-12-30').select("pr").sum())  
print('annualPrecip', annualPrecip)

Earth Engine has many tools for reducing the dimensionality of image collections. Some of these tools are reducers. If we want to take this image and just turn it into an object, we can use a Reducer.

// Reduce precip down to one number. The region parameter is the Feature geometry.
var meanPrecip = annualPrecip.reduceRegion({
  reducer: ee.Reducer.mean(),
  geometry: pugetSound.geometry(),
  scale: 100,
  maxPixels: 1e9
});

print(meanPrecip, 'mean annual precip')

This can be handy if we want to extract numbers or statistics from images and image collections.

Now we want to visualize our new layer. Let's define a lovely palette and map our image.

//define vis parameters
var palette=  ["66ffff","00ff00", "ffff00", "ff3300", "ff0000"];  

//add precip to the map
Map.addLayer(annualPrecip, {min:900, max: 2000, palette: palette}, "Annual Precip"); 




You should see something like this:

Filtering Individual Storm Events

You can even use this method to try do detect individual storms. Using the code above, try to isolate the big storm that happened between August 29-September 1st of this past fall. Hint: Change the dates and the image visualization parameters.

Charts & Figures

I am curious about the elevation variability in Seattle because it feels hecka hilly when I ride my bike around. Let's make a histogram to take a peak at the distribution of different elevation classes.

// Manually compute a histogram.
var hist = ee.Reducer.histogram({minBucketWidth: 300});
var dict = elevation.reduceRegion(hist, pugetSound, 200);

// Use the custom helper function below to get a data table.
var dt = getDataTable(dict.get('elevation').getInfo());

// Fancy options.
var options = {
  title: 'Histogram of elevation',
  vAxis: {
    title: 'Frequency'
  },
  bar: {  
    groupWidth: '100%'  // Eliminate horizontal space between bars.
  },
  legend: {
    position: 'none'
  },
  hAxis: {
    ticks: getTicks(dt.rows),
    baselineColor: 'transparent'  // Hide off-zero vertical axes.
  }
};
print(Chart(dt, 'ColumnChart', options));

function getTicks(rows) {
  // Infer min from difference between first two entries.
  var bucketWidth = rows[1].c[0].v - rows[0].c[0].v;
  return rows.map(function(row) {
    var min = parseInt(row.c[0].v);
    // Use bucket mins as tick values and position to the
    // left of each bucket column.
    return {'v': min - bucketWidth / 2, 'f': min}; 
  });
}

// Generates a Vizualization API data table from histogram output.
function getDataTable(histDict) {
  var rows = histDict.histogram.map(function(v, i) {
    var min = histDict.bucketMin + histDict.bucketWidth * i;
    return {c: [{v: min}, {v: v}]}
  });
  var cols = [
      {id: 'bucketMin', label: 'Bucket Min', type: 'number'},
      {id: 'count', label: 'Count', type: 'number'}
  ];
  return {cols: cols, rows: rows};
}

Final Thoughts

If we finish early and have time, take some time to poke around the API a bit more. You could explore the other datasets that are available or walk through any of the other tutorials on the Earth Engine User Guide.

https://developers.google.com/earth-engine/tutorials

Here is a bailout copy of my script if you get totally stuck https://code.earthengine.google.com/9d84eeaf3399bbc169582a19db6971a1