Tour of the Earth Engine API
Background
Goodchild et al. (2012):
“The supply of geographic information from satellite-based and ground-based sensors has expanded rapidly, encouraging belief in a new, fourth, or “big data,” paradigm of science that emphasizes international collaboration, data-intensive analysis, huge computing resources, and high-end visualization.”
Source: NASA
Google Mission Statement
"To organize the world's information and make it universally accessible and useful."
-Jim Gray (1944-2007)
“Often it turns out to be more efficient to move the questions than to move the data.”
Objectives of Google Earth Engine
Make substantive progress on global challenges that involve large geospatial datasets. Do things at Google scale that no one else can do.
Surface water occurrence - Pekel et al., JRC
南昌
Mobile to Cloud pipeline
Fusion Table
Earth Engine
App Engine
ODK
Collect
ODK Aggregate
Who uses Earth Engine?
What can you do with Earth Engine?
What’s on tap?
Use the Docs!
Reducers
Reduce: Image Bands to Image
var max = image.reduce(ee.Reducer.max());
ee.Reducer
ee.Image
ee.Image
1
2
3
Reduce: ImageCollection to Image
ee.Reducer
var collection = ee.ImageCollection('LANDSAT/LT5_L1T_TOA');
var addNDVI = function(image) {
return image.addBands(
image.normalizedDifference(['B4', 'B3']));
};
// map
var ndviCollection = collection.map(addNDVI);
// reduce
var median = ndviCollection.reduce(ee.Reducer.median());
ee.ImageCollection
ee.Image
3
1
2
7
3
ImageCollection.reduce()
These reducers will NOT work in ImageCollection.reduce() since they do not have a numeric output:
ee.Reducer.frequencyHistogram()
ee.Reducer.histogram()
ee.Reducer.toList()
These reducers will NOT work in ImageCollection.reduce() without first converting each input to a 1-D Array (more on Arrays later):
ee.Reducer.covariance()
ee.Reducer.centeredCovariance()
Reduce: Image region to statistic
var mean = image.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: region.geometry(),
scale: 30,
maxPixels: 1e9,
bestEffort: true,
});
ee.Reducer
42
region
1
8
1
5
3
1
2
7
5
ee.Image
ee.Dictionary
Image.reduceRegion()
These reducers will NOT work in Image.reduceRegion() without first converting the image to an Array image (more on Arrays later):
ee.Reducer.centeredCovariance()
ee.Reducer.covariance()
Reduce: Neighborhood operations
var texture = naipNDVI.reduceNeighborhood({
reducer: ee.Reducer.stdDev(),
kernel: ee.Kernel.circle(7), // 7 is the kernel radius in pixels
});
kernel
ee.Reducer
Reduce: Raster to Vector
var vectors = zones.reduceToVectors({
geometry: japan,
scale: 1000,
geometryType: 'polygon',
labelProperty: 'zone',
reducer: ee.Reducer.mean()
});
Zones (red, green, blue)
ee.Image
Vectors
ee.FeatureCollection
Reducing a Table
| ‘p1’ | p2’ |
f1 | | |
f2 | | |
f3 | | |
... | | |
var sum = featureCollection.reduceColumns({
reducer: ee.Reducer.sum().repeat(2),
selectors: ['p1', 'p2'],
})
[sum1, sum2]
ee.List
ee.FeatureCollection
Reduce: Vector to Raster
var image = features.reduceToImage({
properties: ['Census 2000 Population'],
reducer: ee.Reducer.first()
});
ee.FeatureCollection
ee.Image
Grouping Reducer: Zonal Statistics
var zonalStats = image.reduceRegion({
reducer: ee.Reducer.mean().group({
groupField: 1,
groupName: 'code',
}),
geometry: region.geometry(),
scale: 1000,
maxPixels: 1e8
});
Returns a Dictionary
groups: List (17 elements)
0: Object (2 properties)
code: 0
mean: -0.111
1: Object (2 properties)
code: 1
mean: -0.105
2: Object (2 properties)
code: 2
mean: 0.531
…
ee.Image (band 0)
ee.Image (band 1)
Weighted Reductions
// band 0: image data
// band 1: weights
var weightedMean = image.reduceRegion({
reducer: ee.Reducer.mean().splitWeights(),
geometry: region,
scale: 30
});
Linear Regression
var linearRegression = collection.reduce({
reducer: ee.Reducer.linearRegression({
numX: 2,
numY: 2
})
});
var bandNames = [['constant', 'predictor'],
['response1', 'response2']];
var lrImage = linearRegression
.select(['coefficients'])
.arrayFlatten(bandNames);
| response1 | response2 |
constant | 42 | 13 |
predictor | 88 | 216 |
1-axis
0-axis
ee.Array
Reducers galore!
ee.Reducer.allNonZero()
ee.Reducer.anyNonZero()
ee.Reducer.centeredCovariance()
ee.Reducer.count()
ee.Reducer.countDistinct()
ee.Reducer.countEvery()
ee.Reducer.covariance()
ee.Reducer.first()
ee.Reducer.frequencyHistogram()
ee.Reducer.histogram()
ee.Reducer.intervalMean({
minPercentile: 0.5,
maxPercentile: 0.95
})
ee.Reducer.linearRegression({
numX: 4,
numY: 2
})
ee.Reducer.linearFit()
ee.Reducer.max()
ee.Reducer.mean()
ee.Reducer.median()
ee.Reducer.min()
ee.Reducer.minMax()
ee.Reducer.mode()
ee.Reducer.percentile([025, 0.75])
ee.Reducer.product()
ee.Reducer.robustLinearRegression({
numX: 4,
numY: 2
})
ee.Reducer.sampleStdDev()
ee.Reducer.sampleVariance()
ee.Reducer.stdDev()
ee.Reducer.sum()
ee.Reducer.toList()
ee.Reducer.variance()
Joins
Simple Join
var filter = ee.Filter.equals({leftField: 'p1', rightField: 'p3'});
var simpleJoin = ee.Join.simple();
var simpleJoined = simpleJoin.apply(primary, secondary, filter);
‘p1’ | ‘p2’ |
0 | a |
1 | b |
2 | c |
‘p3’ | ‘p4’ |
1 | d |
2 | e |
3 | f |
‘p1’ | ‘p2’ |
1 | b |
2 | c |
primary
collection
secondary
collection
Join
collection
Inner Join
var filter = ee.Filter.equals({leftField: 'p1', rightField: 'p3'});
var innerJoin = ee.Join.inner('primary', 'secondary');
var toyJoin = innerJoin.apply(primaryFeatures, secondaryFeatures, filter);
‘p1’ | ‘p2’ |
0 | a |
1 | b |
1 | c |
2 | d |
‘p3’ | ‘p4’ |
1 | e |
1 | f |
2 | g |
3 | h |
‘primary’ | |
1 | b |
1 | b |
1 | c |
1 | c |
2 | d |
‘secondary’ | |
1 | e |
1 | f |
1 | e |
1 | f |
2 | g |
primary
collection
secondary
collection
Join
ee.FeatureCollection
Save All Join
var filter = ee.Filter.equals({leftField: 'p1', rightField: 'p3'});
var saveAllJoin = ee.Join.saveAll('matches');
var toyJoin = saveAllJoin.apply(primary, secondary, filter);
‘p1’ | ‘p2’ |
0 | a |
1 | b |
2 | c |
‘p3’ | ‘p4’ |
1 | d |
1 | e |
2 | f |
3 | g |
Join
‘p1’ | ‘p2’ |
1 | b |
2 | c |
‘p3’ | ‘p4’ |
1 | d |
1 | e |
‘matches’
‘p3’ | ‘p4’ |
2 | f |
secondary
collection
primary
collection
collection
ee.List
ee.List
Save Best Join
var maxDiffFilter = ee.Filter.maxDifference({
difference: 2*24*60*60*1000, // two days
leftField: 'system:time_start',
rightField: 'system:time_start'
});
var saveBestJoin = ee.Join.saveBest({matchKey: 'bestImage', measureKey: 'timeDiff'});
var joined = saveBestJoin.apply(primary, secondary, maxDiffFilter);
ee.Image
ee.Image
‘bestImage’
ee.Number
‘timeDiff’
primary
collection
secondary
collection
Join
Spatial Join
var spatialFilter = ee.Filter.withinDistance({
distance: 100000,
leftField: '.geo',
rightField: '.geo',
maxError: 10
});
var saveAllJoin = ee.Join.saveAll({
matchesKey: 'scenes',
measureKey: 'distance'
});
var joined = saveAllJoin.apply(california, wrs, spatialFilter);
var list = ee.List(joined.first().get('scenes'));
var scenes = ee.FeatureCollection(list);
Charts
Time series in a region
ui.Chart.image.series(collection, region, ee.Reducer.mean(), 30);
region
ee.ImageCollection
Customizing a Chart
var options = {
title: 'Landsat 8 DN histogram, bands 2-5',
fontSize: 20,
hAxis: {title: 'DN'},
vAxis: {title: 'count of DN'},
series: {
0:{color: 'blue'},
1:{color: 'green'},
2:{color: 'red'},
3:{color: 'magenta'}}
};
var customChart = chart.setOptions(options);
Histogram in a region
var histogram = ui.Chart.image.histogram(image, region, 30)
.setSeriesNames(['blue', 'green', 'red', 'NIR'])
.setOptions(options);
region
ee.Image
Mean spectra in regions
var wavelengths = [0.44, 0.48, 0.56, 0.65, 0.86, 1.61, 2.2];
var spectraChart = ui.Chart.image.regions(
image, regions, ee.Reducer.mean(), 30, 'label', wavelengths)
.setChartType('LineChart')
.setOptions(options);
regions
Time series in regions
var bandName = 'B11';
var temperatureSeries = ui.Chart.image.seriesByRegion(
collection, regions, ee.Reducer.mean(), bandName, 200, 'system:time_start', 'label')
.setOptions(options);
Day-Of-Year (DOY) series in a region
ui.Chart.image.doySeries(collection, region, ee.Reducer.mean(), 500);
DOY series by year
ui.Chart.image.doySeriesByYear(
collection, bandName, region, ee.Reducer.mean(), 500);
DOY series by region
ui.Chart.image.doySeriesByRegion(
collection, bandName, regions, ee.Reducer.mean(), 500, ee.Reducer.mean(), 'label');
Spectra by class band
ui.Chart.image.byClass(
image, classBand, region, ee.Reducer.mean(), 500, classNames, wavelengths)
Scatter chart: band correlation
ui.Chart.array.values(yValues, 0, xValues).setSeriesNames(['B5', 'B6'])
Arrays
What is an Array?
| 1-axis | ||||||
0-axis | | 0 | 1 | 2 | 3 | 4 | 5 |
0 | 0.3037 | 0.2793 | 0.4743 | 0.5585 | 0.5082 | 0.1863 | |
1 | -0.2848 | -0.2435 | -0.5436 | 0.7243 | 0.0840 | -0.1800 | |
2 | 0.1509 | 0.1973 | 0.3279 | 0.3406 | -0.7112 | -0.4572 | |
3 | -0.8242 | 0.0849 | 0.4392 | -0.0580 | 0.2012 | -0.2768 | |
4 | -0.3280 | 0.0549 | 0.1075 | 0.1855 | -0.4357 | 0.8085 | |
5 | 0.1084 | -0.9022 | 0.4120 | 0.0573 | -0.0251 | 0.0238 | |
ee.Array([
[ 0.3037, 0.2793, 0.4743, 0.5585, 0.5082, 0.1863],
[-0.2848, -0.2435, -0.5436, 0.7243, 0.0840, -0.1800],
[ 0.1509, 0.1973, 0.3279, 0.3406, -0.7112, -0.4572],
[-0.8242, 0.0849, 0.4392, -0.0580, 0.2012, -0.2768],
[-0.3280, 0.0549, 0.1075, 0.1855, -0.4357, 0.8085],
[ 0.1084, -0.9022, 0.4120, 0.0573, -0.0251, 0.0238]
]
Array Dimensions
ee.Array(42); // 0-D (Scalar)
ee.Array([1, 2, 3]); // 1-D array, variation on the 0-axis
ee.Array([[1], [2], [3]]); // 2-D array (3x1), variation on the 0-axis
ee.Array([[1, 2, 3]]); // 2-D array (1x3), variation on the 1-axis
Array Concatenation
0-axis
1-axis
[1,
2,
3]
[1,
2,
3],
[1,
2,
3],
…
var array1D = ee.Array([1,2,3]);
ee.Array.cat([array1D], 1);
ee.Array.cat(
[array1D, array1D], 1);
Array Images
ee.Image
ee.Image
2
3
1
image.toArray()
ee.Image
image.toArray(1)
ee.Image
[1,2,3]
[1,2,3]
[[1], [2], [3]]
1-D
1-D
2-D
Example: tasseled cap transform
var coefficients = ee.Array([
[0.3037, 0.2793, 0.4743, 0.5585, 0.5082, 0.1863],
[-0.2848, -0.2435, -0.5436, 0.7243, 0.0840, -0.1800],
[0.1509, 0.1973, 0.3279, 0.3406, -0.7112, -0.4572],
[-0.8242, 0.0849, 0.4392, -0.0580, 0.2012, -0.2768],
[-0.3280, 0.0549, 0.1075, 0.1855, -0.4357, 0.8085],
[0.1084, -0.9022, 0.4120, 0.0573, -0.0251, 0.0238]
]);
var image = ee.Image('LT5_L1T_TOA/LT50440342008285PAC01')
.select(['B1', 'B2', 'B3', 'B4', 'B5', 'B7']);
var arrayImage1D = image.toArray(); // []
var arrayImage2D = arrayImage1D.toArray(1); // [[],[],...,[]]
var componentsImage = ee.Image(coefficients)
.matrixMultiply(arrayImage2D) // [[],[],...,[]]
.arrayProject([0]) // []
.arrayFlatten([['brightness', 'greenness', 'wetness', 'fourth', 'fifth', 'sixth']]);
Covariance Arrays
var arrayCollection = collection.map(function(image) {
return image.toArray();
});
var covarianceImage = arrayCollection.reduce(ee.Reducer.covariance());
// 0-axis
var bandNames = [[ 'B2',
'B3'],
['B2','B3']]; // 1-axis
var collCovImage = collCovariance.arrayFlatten(bandNames);
collCovImage: Image (4 bands)
B2_B2: 364.4705810546875
B2_B3: 430.2426452636719
B3_B2: 430.2426452636719
B3_B3: 510.5294189453125
Pixel
| 1-axis | ||
0-axis | | B2 | B3 |
B2 | 364 | 430 | |
B3 | 430 | 510 | |
Array Image Collections
ee.Image
[ [b1,...,bp],
⋮
[b1,...,bp] ]
2-D
imageCollection
.toArray()
Image axis (0)
Band axis (1)
Array based linear modeling
1 | t1 | sin(t1) | cos(t1) | NDVI1 |
1 | t2 | sin(t2) | cos(t2) | NDVI2 |
1 | t3 | sin(t3) | cos(t3) | NDVI3 |
⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
1 | tT | sin(tT) | cos(tT) | NDVIT |
𝛽0 + 𝛽1t + 𝛽2sin(t) + 𝛽2cos(t) = NDVI
PTx4B4x1 = RTx1
Image axis (0)
0 | 1 | 2 | 3 | 4 |
Band axis (1)
slice
Array based linear modeling
var array = collection.toArray();
var imageAxis = 0;
var bandAxis = 1;
var predictors = array.arraySlice(bandAxis, 0, 4);
var response = array.arraySlice(bandAxis, 4);
var coefficients3 = predictors.matrixSolve(response);
code.earthengine.google.com
What will you create with Earth Engine?