Note: This lab should be done with a Gmail account for GEE. Having a Gmail account is necessary to finish this lab with GEE successfully. Unfortunately, due to certain limitations imposed by the University on Google, publishing apps may not be possible if you have used your UFL email. If you used UFL in the past, please create a Gmail account for this lab.
Introduction
The main objective of this lab is to teach you how to design and publish Earth Engine Apps using JavaScript. By the end of the lab, you will have the knowledge and skills to create an Earth Engine App with a split-panel map that visualizes land cover change.
The lab covers the following learning outcomes:
- Designing a user interface for an Earth Engine App using JavaScript
- Publishing an Earth Engine App for visualizing land cover change.
Background
The JavaScript Code Editor in Earth Engine provides a user interface API that allows users to create and publish interactive web apps directly from their code. We are familiar with the ui.Chart function, here, we will look further into the UI API offers many more interface options. Users can use the UI functions to build a complete graphical user interface (GUI) for their Earth Engine script. This GUI can include various widgets, such as labels, buttons, checkboxes, sliders, and text boxes, as well as more complex widgets like charts, maps, and panels to control the GUI layout. A list of all available UI widgets and more panel information can be found at the links below. Once a GUI is constructed, users can easily publish the app from the JavaScript Code Editor by clicking the "Apps" button above the script panel.
You can read more here:
Widgets: https://developers.google.com/earth-engine/guides/ui_widgets
Panels: https://developers.google.com/earth-engine/guides/ui_panels
Lab
Now you will be introduced to designing a user interface for an Earth Engine App using JavaScript and the Earth Engine User Interface API. By the end of this lab, you will have created an Earth Engine App with a split-panel map for visualizing land cover change using the Landsat-based United States Geological Survey National Land Cover Database (NLCD).
Go to https://code.earthengine.google.com/ and sign in to bring up the code editor.
Part 1: Get an NLCD image by year
First, we will define a function for filtering the NLCD ImageCollection by year and selecting the landcover band. The function returns an Earth Engine ui.Map.Layer of the landcover band of the selected NLCD image. It is important to note that as of this writing, the NLCD spans nine epochs: 1992, 2001, 2004, 2006, 2008, 2011, 2013, 2016, and 2019. The 1992 data are primarily based on the unsupervised classification of Landsat data. The rest of the images rely on the imperviousness data layer for the urban classes and a decision-tree classification for the rest. However, the 1992 image is not directly comparable to any later editions of NLCD. Therefore, we will only use the eight epochs after 2000 in this lab. More details about the NLCD can be found in the Earth Engine Data Catalog.
https://developers.google.com/earth-engine/datasets/catalog/USGS_NLCD_RELEASES_2019_REL_NLCD
The following code defines a function called getNLCD that retrieves an image from the USGS National Land Cover Database (NLCD) for a given year. The function takes the year as an input and returns a map layer containing the NLCD land cover classification image for that year.
// Get an NLCD image by year.
var getNLCD = function(year) {
// Import the NLCD collection.
var dataset = ee.ImageCollection(
'USGS/NLCD_RELEASES/2019_REL/NLCD');
// Filter the collection by year.
var nlcd = dataset.filter(ee.Filter.eq('system:index', year))
.first();
// Select the land cover band.
var landcover = nlcd.select('landcover');
return ui.Map.Layer(landcover, {}, year);
};
Part 2: Create a dictionary of NLCD images for different years
We intend to create a dropdown list so that when a particular epoch is selected, the corresponding NLCD image layer will be displayed on the map. We’ll define a dictionary with each NLCD epoch as the key and its corresponding NLCD image layer as the value. The keys of the dictionary (i.e., the eight NLCD epochs) will be used as the input to the dropdown lists (ui.Select) on the split-level map.
The following code creates a dictionary called images that maps each year to its corresponding NLCD image layer.
// Create a dictionary with each year as the key
// and its corresponding NLCD image layer as the value.
var images = {
'2001': getNLCD('2001'),
'2004': getNLCD('2004'),
'2006': getNLCD('2006'),
'2008': getNLCD('2008'),
'2011': getNLCD('2011'),
'2013': getNLCD('2013'),
'2016': getNLCD('2016'),
'2019': getNLCD('2019'),
};
Part 3: Create left and right maps with layer selection widgets
The split-panel map is composed of two individual maps, leftMap and rightMap. The map controls (e.g., zoomControl, scaleControl, mapTypeControl) will be shown only on rightMap. A control panel (ui.Panel) composed of a label (ui.Label) and a dropdown list (ui.Select) is added to each map. When an NLCD epoch is selected from a dropdown list, the function updateMap will be called to show the corresponding image layer of the selected epoch.
The following code creates two maps, leftMap and rightMap, and adds layer selection widgets to each map that allow the user to change which NLCD image is displayed.
// Create the left map, and have it display the first layer.
var leftMap = ui.Map();
leftMap.setControlVisibility(false);
var leftSelector = addLayerSelector(leftMap, 0, 'top-left');
// Create the right map, and have it display the last layer.
var rightMap = ui.Map();
rightMap.setControlVisibility(true);
var rightSelector = addLayerSelector(rightMap, 7, 'top-right');
// Adds a layer selection widget to the given map, to allow users to
// change which image is displayed in the associated map.
function addLayerSelector(mapToChange, defaultValue, position) {
var label = ui.Label('Select a year:');
// This function changes the given map to show the selected image.
function updateMap(selection) {
mapToChange.layers().set(0, images[selection]);
}
// Configure a selection dropdown to allow the user to choose
// between images, and set the map to update when a user
// makes a selection.
var select = ui.Select({
items: Object.keys(images),
onChange: updateMap
});
select.setValue(Object.keys(images)[defaultValue], true);
var controlPanel =
ui.Panel({
widgets: [label, select],
style: {
position: position
}
});
mapToChange.add(controlPanel);
}
Part 4: Create a legend for the NLCD land cover classification
When displaying a land cover classification image on the Map, it would be useful to add a legend to make it easier for users to interpret the land cover type associated with each color. Let’s define a dictionary that will be used to construct the legend. The dictionary contains two keys: names (a list of land cover types) and colors (a list of colors associated with each land cover type). The legend will be placed in the bottom right of the Map.
The following code defines a function called addCategoricalLegend that generates a categorical legend for the NLCD land cover classification. The function takes as inputs a panel to hold the legend widget, a dictionary containing the class names and corresponding colors, and a title for the legend.
// Set the legend title.
var title = 'NLCD Land Cover Classification';
// Set the legend position.
var position = 'bottom-right';
// Define a dictionary that will be used to make a legend
var dict = {
'names': [
'11 Open Water',
'12 Perennial Ice/Snow',
'21 Developed, Open Space',
'22 Developed, Low Intensity',
'23 Developed, Medium Intensity',
'24 Developed, High Intensity',
'31 Barren Land (Rock/Sand/Clay)',
'41 Deciduous Forest',
'42 Evergreen Forest',
'43 Mixed Forest',
'51 Dwarf Scrub',
'52 Shrub/Scrub',
'71 Grassland/Herbaceous',
'72 Sedge/Herbaceous',
'73 Lichens',
'74 Moss',
'81 Pasture/Hay',
'82 Cultivated Crops',
'90 Woody Wetlands',
'95 Emergent Herbaceous Wetlands',
],
'colors': [
'#466b9f', '#d1def8', '#dec5c5', '#d99282', '#eb0000',
'#ab0000',
'#b3ac9f', '#68ab5f', '#1c5f2c', '#b5c58f', '#af963c',
'#ccb879',
'#dfdfc2', '#d1d182', '#a3cc51', '#82ba9e', '#dcd939',
'#ab6c28',
'#b8d9eb', '#6c9fb8',
]
};
Part 5: Set up the maps and control widgets
With the legend dictionary defined above, we can now create a panel to hold the legend widget and add it to the Map. Each row on the legend widget is composed of a color box followed by its corresponding land cover type.
The following code ties everything together by creating a split-panel map that displays the left and right maps side-by-side, with the left map showing the first NLCD image and the right map showing the last NLCD image. The code also creates a legend for the NLCD land cover classification and sets the legend position.
// Create a panel to hold the legend widget.
var legend = ui.Panel({
style: {
position: position,
padding: '8px 15px'
}
});
// Function to generate the legend.
function addCategoricalLegend(panel, dict, title) {
// Create and add the legend title.
var legendTitle = ui.Label({
value: title,
style: {
fontWeight: 'bold',
fontSize: '18px',
margin: '0 0 4px 0',
padding: '0'
}
});
panel.add(legendTitle);
var loading = ui.Label('Loading legend...', {
margin: '2px 0 4px 0'
});
panel.add(loading);
// Creates and styles 1 row of the legend.
var makeRow = function(color, name) {
// Create the label that is actually the colored box.
var colorBox = ui.Label({
style: {
backgroundColor: color,
// Use padding to give the box height and width.
padding: '8px',
margin: '0 0 4px 0'
}
});
// Create the label filled with the description text.
var description = ui.Label({
value: name,
style: {
margin: '0 0 4px 6px'
}
});
return ui.Panel({
widgets: [colorBox, description],
layout: ui.Panel.Layout.Flow('horizontal')
});
};
// Get the list of palette colors and class names from the image.
var palette = dict.colors;
var names = dict.names;
loading.style().set('shown', false);
for (var i = 0; i < names.length; i++) {
panel.add(makeRow(palette[i], names[i]));
}
rightMap.add(panel);
}
The last step is to create a split-panel map to hold the linked maps (leftMap and rightMap) and tie everything together. When users pan and zoom one map, the other map will also be panned and zoomed to the same extent automatically. When users select a year from a dropdown list, the image layer will be updated accordingly. Users can use the slider to swipe through and visualize land cover change easily. Please make sure you minimize the Code Editor and maximize the Map so that you can see the dropdown widget in the upper-right corner of the map.
addCategoricalLegend(legend, dict, title);
// Create a SplitPanel to hold the adjacent, linked maps.
var splitPanel = ui.SplitPanel({
firstPanel: leftMap,
secondPanel: rightMap,
wipe: true,
style: {
stretch: 'both'
}
});
// Set the SplitPanel as the only thing in the UI root.
ui.root.widgets().reset([splitPanel]);
var linker = ui.Map.Linker([leftMap, rightMap]);
leftMap.setCenter(-100, 40, 4);
Part 6: Troubleshoot
Run and check your Code, and troubleshoot any problems. If there are no problems, it should look like this.

Part 7: Deploy App - Publishing an Earth Engine App from the Code Editor
The goal now is to publish the Earth Engine App that we just created. Open the Manage Apps panel by clicking the Apps button above the script panel in the Code Editor. Then click on the New App button.

In the Publish New App dialog, choose a name for the App (e.g., NLCD Land Cover Change)

Select a Google Cloud Project, here you can go with defaults since, most likely this is your first app.

For the source code, retake the default.
Next, you can customize how the app is published. Feel free to play with the setting like logos, etc., but not necessary, and you can take defaults. When done with the wizard, push publish.
Here is mine: https://sounny.users.earthengine.app/view/nlcd
Part 8: Test the URL
Test the URL of your app. If it is working, submit it to Canvas for the lab. If not, time to troubleshoot. Note: To manage an App from the Code Editor, open the Manage Apps panel by clicking the Apps button above the script panel in the Code Editor. There, you can update your App’s configuration or delete the App.
Lab Submission
Submit lab via email.
Subject: Lab 20 - Design UI/UX and Deploying Google Earth Engine Apps - [Your Name]