Lab 20 - Design UI/UX and Deploying Google Earth Engine Apps

Objective: Create and publish an interactive Earth Engine App with a split-panel map for visualizing land cover change.

What You'll Learn

  • Design user interfaces using the Earth Engine UI API
  • Create interactive widgets (selectors, panels, legends)
  • Build a split-panel map for comparing images
  • Link maps for synchronized pan/zoom
  • Publish and share Earth Engine Apps

Why This Matters

Earth Engine Apps enable you to:

  • Share your work: Anyone with a web browser can use your analysis
  • Reach stakeholders: No coding knowledge required for users
  • Create decision tools: Interactive maps for planning and management
  • Showcase skills: Build a portfolio of geospatial applications

Before You Start

  • Prerequisites: Complete Lab 19 and understand JavaScript basics.
  • Estimated time: 90 minutes
  • Materials: Earth Engine account (Gmail required for publishing).

Important: Gmail Account Required

Publishing apps requires a Gmail account. University (UFL) accounts may have publishing restrictions. If you've been using your university email, please create a free Gmail account for this lab.

Key Terms

UI API
Earth Engine's User Interface API for creating interactive widgets and layouts.
Widget
A UI element like a button, slider, dropdown, or label that users can interact with.
Split Panel
A layout showing two maps side-by-side with a draggable divider for comparison.
NLCD
National Land Cover Database - a Landsat-based land cover classification for the US.

Earth Engine UI Components

Component Purpose Example Use
ui.Label Display text Titles, instructions
ui.Select Dropdown menu Year selection
ui.Button Clickable action Run analysis
ui.Slider Value selection Threshold adjustment
ui.Panel Container/layout Group widgets
ui.SplitPanel Side-by-side maps Before/after comparison
ui.Map Map display Visualization

Learn more: UI Widgets Guide | UI Panels Guide

Part 1: Get NLCD Image by Year

Create a function to get NLCD land cover for a specific 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);
};

NLCD Epochs: Available years are 2001, 2004, 2006, 2008, 2011, 2013, 2016, and 2019. The 1992 data uses different methodology and is not directly comparable.

Part 2: Create Image Dictionary

Build a dictionary mapping years to their NLCD layers:

// Create a dictionary with each year and its corresponding NLCD layer
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 Split Panel Maps

Create left and right maps with dropdown selectors:

// Create the left map (shows first year by default)
var leftMap = ui.Map();
leftMap.setControlVisibility(false);
var leftSelector = addLayerSelector(leftMap, 0, 'top-left');

// Create the right map (shows last year by default)
var rightMap = ui.Map();
rightMap.setControlVisibility(true);
var rightSelector = addLayerSelector(rightMap, 7, 'top-right');

// Function to add layer selection widget to a map
function addLayerSelector(mapToChange, defaultValue, position) {
    var label = ui.Label('Select a year:');
    
    // Function to update the map layer
    function updateMap(selection) {
        mapToChange.layers().set(0, images[selection]);
    }
    
    // Create dropdown selector
    var select = ui.Select({
        items: Object.keys(images),
        onChange: updateMap
    });
    select.setValue(Object.keys(images)[defaultValue], true);
    
    // Create control panel
    var controlPanel = ui.Panel({
        widgets: [label, select],
        style: {position: position}
    });
    
    mapToChange.add(controlPanel);
}

Part 4: Create the Legend

Define NLCD land cover classes and colors:

// Legend configuration
var title = 'NLCD Land Cover Classification';
var position = 'bottom-right';

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', '41 Deciduous Forest', '42 Evergreen Forest',
        '43 Mixed Forest', '52 Shrub/Scrub', '71 Grassland/Herbaceous',
        '81 Pasture/Hay', '82 Cultivated Crops',
        '90 Woody Wetlands', '95 Emergent Herbaceous Wetlands'
    ],
    'colors': [
        '#466b9f', '#d1def8', '#dec5c5', '#d99282', '#eb0000', '#ab0000',
        '#b3ac9f', '#68ab5f', '#1c5f2c', '#b5c58f', '#ccb879', '#dfdfc2',
        '#dcd939', '#ab6c28', '#b8d9eb', '#6c9fb8'
    ]
};

Part 5: Assemble the App

Tie everything together with a SplitPanel:

// Create legend panel
var legend = ui.Panel({
    style: {position: position, padding: '8px 15px'}
});

// Add legend to map (function definition in full code)
addCategoricalLegend(legend, dict, title);

// Create split panel with linked maps
var splitPanel = ui.SplitPanel({
    firstPanel: leftMap,
    secondPanel: rightMap,
    wipe: true,
    style: {stretch: 'both'}
});

// Set the SplitPanel as the root UI
ui.root.widgets().reset([splitPanel]);

// Link the maps for synchronized pan/zoom
var linker = ui.Map.Linker([leftMap, rightMap]);
leftMap.setCenter(-100, 40, 4);

Expected Result

Your app should look like this - a split panel with synchronized maps:

Split panel land cover app

Part 6: Publish Your App

  1. Click the Apps button above the script panel
  2. Click New App
  3. Name your app (e.g., "NLCD Land Cover Change")
  4. Select a Google Cloud Project (use defaults if first app)
  5. Configure source code and appearance
  6. Click Publish

Check Your Understanding

  1. What is the purpose of the ui.Map.Linker function?
  2. Why do we set control visibility differently for left and right maps?
  3. How does the ui.Select widget know which years to show?
  4. What happens when you drag the split panel divider?

Troubleshooting

Problem: "Unable to publish" error

Solution: Make sure you're using a Gmail account, not a university account. Create a new Gmail if needed.

Problem: Dropdowns don't appear

Solution: Minimize the Code Editor and maximize the map view. Check that addLayerSelector is called before the panels are used.

Problem: Maps don't sync when panning

Solution: Ensure ui.Map.Linker is created with both maps in the array.

Problem: App URL doesn't work

Solution: Wait a few minutes for the app to deploy. Use the Manage Apps panel to check status or update configuration.

Pro Tips

  • Add branding: Upload a logo and customize colors in the publish settings
  • Test thoroughly: Apps behave differently than the Code Editor - test in a new browser tab
  • Version control: Save your script before publishing so you can update later
  • User feedback: Add a ui.Textbox or link for users to provide feedback

Key Takeaways

  • The UI API enables creation of interactive applications without web development skills
  • Split panels are effective for before/after comparisons
  • Publishing creates a shareable URL accessible to anyone
  • Apps can be updated or deleted from the Manage Apps panel

Challenge: Extend Your App

Try adding these features to your app:

  • Add a location search box
  • Include additional datasets for comparison
  • Add an info panel that appears when clicking on the map
  • Create a time slider instead of dropdown menus

📋 Lab Submission

Subject: Lab 20 - GEE Apps - [Your Name]

Submit:

  1. The URL to your published Earth Engine App
  2. A shareable URL to your GEE script
  3. A brief description of your app's purpose