The Earth Engine App resulting from this tutorial. Shown is an NDVI time series chart for the drawn polygon around Carmel Valley, California.
Functionality
The app has two main functions: 1) allow a user to draw a geometry and; 2) render a region reduction time series chart for the drawn geometry. Clicking a "draw" button will allow a user to draw a geometry, and once the drawing is complete, the chart will render. These two events (button click to draw and drawing finished) need associated functions to complete the event-callback cycle.
Custom drawing tools
Before defining callback functions, setup drawingTools
for custom use.
1. Get the drawing tools widget object; define it as a variable for convenience in recalling it later.
var drawingTools = Map.drawingTools();
2. Hide the default drawing tools so you can add your own. You can use the default drawing tools for interactive region reduction, but they provide more functionality than is needed when simplicity is the goal.
drawingTools.setShown(false);
3. Setup a while loop to clear all existing geometries that have been added as imports from drawing tools (from previously running the script). The design of the app is to handle charting a time series for a single geometry, so remove any that exist.
while (drawingTools.layers().length() > 0) {
var layer = drawingTools.layers().get(0);
drawingTools.layers().remove(layer);
}
4. Initialize a dummy GeometryLayer
with null
geometry to act as a
placeholder for drawn geometries.
var dummyGeometry =
ui.Map.GeometryLayer({geometries: null, name: 'geometry', color: '23cba7'});
drawingTools.layers().add(dummyGeometry);
Event callback functions
Define callback functions to enable drawing and chart rendering, they will be attached to event listeners in the following sections.
Drawing buttons
Define a series of functions that are called when geometry
drawing buttons are clicked: one for clearing the previous geometry from
the GeometryLayer
and one for each drawing mode button
(rectangle, polygon, and point).
1. Define the geometry clearing function.
function clearGeometry() {
var layers = drawingTools.layers();
layers.get(0).geometries().remove(layers.get(0).geometries().get(0));
}
2. Define functions that will be called when each respective drawing
button is clicked. Each function will clear previous drawings using the
clearGeometry
function and then initialize drawing for the particular drawing
mode.
function drawRectangle() {
clearGeometry();
drawingTools.setShape('rectangle');
drawingTools.draw();
}
function drawPolygon() {
clearGeometry();
drawingTools.setShape('polygon');
drawingTools.draw();
}
function drawPoint() {
clearGeometry();
drawingTools.setShape('point');
drawingTools.draw();
}
Regional time series chart
1. Define a panel to hold the time series chart. Set the shown
style
parameter to false
to initially hide the panel until the first chart is
rendered.
var chartPanel = ui.Panel({
style:
{height: '235px', width: '600px', position: 'bottom-right', shown: false}
});
2. Add the panel to the Map
.
Map.add(chartPanel);
3. Define a function that gets called on geometry drawing completion and
editing events to generate an
NDVI time series
chart for the drawn region. See code comments for an explanation of each step.
In summary, the function shows the chart panel on the first drawing event,
clears previously rendered charts, gets the drawn geometry, calculates the
region reduction scale based on the Map
scale, and renders a chart in the
chart panel.
function chartNdviTimeSeries() {
// Make the chart panel visible the first time a geometry is drawn.
if (!chartPanel.style().get('shown')) {
chartPanel.style().set('shown', true);
}
// Get the drawn geometry; it will define the reduction region.
var aoi = drawingTools.layers().get(0).getEeObject();
// Set the drawing mode back to null; turns drawing off.
drawingTools.setShape(null);
// Reduction scale is based on map scale to avoid memory/timeout errors.
var mapScale = Map.getScale();
var scale = mapScale > 5000 ? mapScale * 2 : 5000;
// Chart NDVI time series for the selected area of interest.
var chart = ui.Chart.image
.seriesByRegion({
imageCollection: ee.ImageCollection('MODIS/006/MOD13A2'),
regions: aoi,
reducer: ee.Reducer.mean(),
band: 'NDVI',
scale: scale,
xProperty: 'system:time_start'
})
.setOptions({
titlePostion: 'none',
legend: {position: 'none'},
hAxis: {title: 'Date'},
vAxis: {title: 'NDVI (x1e4)'},
series: {0: {color: '23cba7'}}
});
// Replace the existing chart in the chart panel with the new chart.
chartPanel.widgets().reset([chart]);
}
4. Set the drawing tools widget to listen for geometry drawing and editing
events and respond with the chartNdviTimeSeries
function.
Note that ui.util.debounce
wraps the chartNdviTimeSeries
function to
reduce the frequency of it being invoked while drawing and editing a
geometry. Here, the delay is set to 500 milliseconds or 0.5 seconds.
drawingTools.onDraw(ui.util.debounce(chartNdviTimeSeries, 500));
drawingTools.onEdit(ui.util.debounce(chartNdviTimeSeries, 500));
User interface
This section defines the drawing control panel, which contains instructions and drawing tool buttons.
1. Define a dictionary of symbols to augment the text label for each of the geometry buttons defined in the following step. The symbols are kept separate from the text to avoid unexpected cursor behavior when mixing symbols and text.
var symbol = {
rectangle: '⬛',
polygon: '🔺',
point: '📍',
};
2. Define a ui.Panel
to hold app instructions and the geometry drawing
buttons. Use a ui.Label
for each instruction line and a ui.Button
for
each of the three geometry drawing options. Button labels are the
concatenation of the symbols defined in the previous step and text. Set the
onClick
parameter to each respective drawing mode callback function defined
above.
var controlPanel = ui.Panel({
widgets: [
ui.Label('1. Select a drawing mode.'),
ui.Button({
label: symbol.rectangle + ' Rectangle',
onClick: drawRectangle,
style: {stretch: 'horizontal'}
}),
ui.Button({
label: symbol.polygon + ' Polygon',
onClick: drawPolygon,
style: {stretch: 'horizontal'}
}),
ui.Button({
label: symbol.point + ' Point',
onClick: drawPoint,
style: {stretch: 'horizontal'}
}),
ui.Label('2. Draw a geometry.'),
ui.Label('3. Wait for chart to render.'),
ui.Label(
'4. Repeat 1-3 or edit/move\ngeometry for a new chart.',
{whiteSpace: 'pre'})
],
style: {position: 'bottom-left'},
layout: null,
});
3. Add the panel to the Map
.
Map.add(controlPanel);
Result
The following animation is the result of putting the entire script together and publishing it as an Earth Engine App. The simplicity of this custom drawing tool interface and automatic clearing of the previous drawing and plot will allow you and your app users to focus on exploring data instead of geometry management.
Learn more about the drawing tools API in the Getting Started with Drawing Tools tutorial.