CVPR 2025
An Introduction to Cloud-Based Geospatial Analysis with Earth Engine and Geemap
- Notebook: https://geemap.org/workshops/CVPR_2025
- Earth Engine: https://earthengine.google.com
- Geemap: https://geemap.org
Introduction¶
This notebook is for the workshop (Computer Vision and Artificial Intelligence for Large-Scale Earth Observation Data) presented at the IEEE / CVF Computer Vision and Pattern Recognition Conference (CVPR) .
Participants will dive into Google Earth Engine and Geemap to create interactive maps and analyze large-scale geospatial datasets. Topics include:
- Building interactive maps
- Visualizing and analyzing Earth Engine datasets
- Exploring Earth Engine Data Catalogs
- Exporting data for further use
- Creating timelapse animations
Prerequisites¶
To use geemap and the Earth Engine Python API, you must register for an Earth Engine account and follow the instructions here to create a Cloud Project. Earth Engine is free for noncommercial and research use. To test whether you can use authenticate the Earth Engine Python API, please run this notebook on Google Colab.
It is recommended that attendees have a basic understanding of Python and Jupyter Notebook.
Familiarity with the Earth Engine JavaScript API is not required but will be helpful.
Attendees can use Google Colab to follow this workshop without installing anything on their computer.
Introduction to Google Earth Engine¶
Google Earth Engine Overview¶
Google Earth Engine is a cloud-computing platform that enables scientific analysis and visualization of large-scale geospatial datasets. It provides a rich data catalog and processing capabilities, allowing users to analyze satellite imagery and geospatial data at planetary scale.
Earth Engine is free for noncommercial and research use. Nonprofit organizations, research institutions, educators, Indigenous governments, and government researchers can continue using Earth Engine for free, as they have for more than a decade. However, commercial users may require a paid license.
Installing Geemap¶
The geemap package simplifies the use of Google Earth Engine in Python, offering an intuitive API for visualization and analysis in Jupyter notebooks. In Google Colab, geemap is pre-installed, but you may need to install additional dependencies for certain features. To install the latest version with optional dependencies, use the following command:
# %pip install -U "geemap[workshop]" leafmap
Import Libraries¶
To start, import the necessary libraries for working with Google Earth Engine (GEE) and geemap.
import ee
import geemap
Authenticate and Initialize Earth Engine¶
To authenticate and initialize your Earth Engine environment, you’ll need to create a Google Cloud Project and enable the Earth Engine API for the project if you have not done so already. You can find detailed setup instructions here.
ee.Authenticate()
Replace YOUR-PROJECT-ID
with your actual Google Cloud Project ID. You can get the project ID from the Earth Engine JavaScript Code Editor.
ee.Initialize(project="Your_Project_ID")
Creating Interactive Maps¶
Let’s create an interactive map using the ipyleaflet
plotting backend. The geemap.Map
class inherits from the ipyleaflet.Map
class, so the syntax is similar to creating an interactive map with ipyleaflet.Map
.
m = geemap.Map()
To display the map in a Jupyter notebook, simply enter the map object:
m
To customize the map’s display, you can set various keyword arguments, such as center
(latitude and longitude), zoom
, width
, and height
. By default, the width
is 100%
, filling the entire width of the Jupyter notebook cell. The height
parameter can be a number (in pixels) or a string with a pixel format, e.g., 600px
.
Map of the Contiguous United States
To center the map on the contiguous United States, use:
m = geemap.Map(center=[40, -100], zoom=4, height="600px")
m
Map of the state of Tennessee
m = geemap.Map(center=[35.746512, -86.209818], zoom=8)
m
Adding Basemaps¶
There are several ways to add basemaps to a map in geemap. You can specify a basemap in the basemap
keyword argument when creating the map, or you can add additional basemap layers using the add_basemap
method. Geemap
provides access to hundreds of built-in basemaps, making it easy to add layers to a map with a single line of code.
To create a map with a specific basemap, use the basemap
argument as shown below. For example, Esri.WorldImagery
provides an Esri world imagery basemap.
m = geemap.Map(basemap="Esri.WorldImagery")
m
m = geemap.Map()
m.add("basemap_selector")
m
Layer Manager¶
The layer manager provides control over layer visibility and transparency. It enables toggling layers on and off and adjusting transparency with a slider, making it easy to customize map visuals.
m = geemap.Map(center=(40, -100), zoom=4)
dem = ee.Image("USGS/SRTMGL1_003")
states = ee.FeatureCollection("TIGER/2018/States")
vis_params = {
"min": 0,
"max": 4000,
"palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"],
}
m.add_layer(dem, vis_params, "SRTM DEM")
m.add_layer(states, {}, "US States")
m.add("layer_manager")
m
Inspector Tool¶
The inspector tool allows you to click on the map to query Earth Engine data at specific locations. This is helpful for examining the properties of datasets directly on the map.
m = geemap.Map(center=(40, -100), zoom=4)
dem = ee.Image("USGS/SRTMGL1_003")
landsat7 = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003")
states = ee.FeatureCollection("TIGER/2018/States")
vis_params = {
"min": 0,
"max": 4000,
"palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"],
}
m.add_layer(dem, vis_params, "SRTM DEM")
m.add_layer(
landsat7,
{"bands": ["B4", "B3", "B2"], "min": 20, "max": 200, "gamma": 2.0},
"Landsat 7",
)
m.add_layer(states, {}, "US States")
m.add("inspector")
m
m = geemap.Map(center=(40, -100), zoom=4)
dem = ee.Image("USGS/SRTMGL1_003")
vis_params = {
"min": 0,
"max": 4000,
"palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"],
}
m.add_layer(dem, vis_params, "SRTM DEM")
m.add("layer_editor", layer_dict=m.ee_layers["SRTM DEM"])
m
Multi-Band image¶
m = geemap.Map(center=(40, -100), zoom=4)
landsat7 = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003")
m.add_layer(
landsat7,
{"bands": ["B4", "B3", "B2"], "min": 20, "max": 200, "gamma": 2.0},
"Landsat 7",
)
m.add("layer_editor", layer_dict=m.ee_layers["Landsat 7"])
m
Feature Collection¶
m = geemap.Map(center=(40, -100), zoom=4)
states = ee.FeatureCollection("TIGER/2018/States")
m.add_layer(states, {}, "US States")
m.add("layer_editor", layer_dict=m.ee_layers["US States"])
m
Draw Control¶
The draw control feature allows you to draw shapes directly on the map, converting them automatically into Earth Engine objects. Access the drawn features as follows:
- To return the last drawn feature as an
ee.Geometry()
, usem.user_roi
- To return all drawn features as an
ee.FeatureCollection()
, usem.user_rois
m = geemap.Map(center=(40, -100), zoom=4)
dem = ee.Image("USGS/SRTMGL1_003")
vis_params = {
"min": 0,
"max": 4000,
"palette": "terrain",
}
m.add_layer(dem, vis_params, "SRTM DEM")
m.add("layer_manager")
m
if m.user_roi is not None:
image = dem.clip(m.user_roi)
m.layers[1].visible = False
m.add_layer(image, vis_params, "Clipped DEM")
The Earth Engine Data Catalog¶
The Earth Engine Data Catalog hosts an extensive collection of geospatial datasets. Currently, he catalog includes over 1,000 datasets with a combined size exceeding 100 petabytes. Notable datasets include Landsat, Sentinel, MODIS, and NAIP. For a comprehensive list of datasets in CSV or JSON format, refer to the Earth Engine Datasets List.
Searching Datasets on the Earth Engine Website¶
To browse datasets directly on the Earth Engine website:
- View the full catalog: https://developers.google.com/earth-engine/datasets/catalog
- Search by tags: https://developers.google.com/earth-engine/datasets/tags
Searching Datasets Within Geemap¶
The Earth Engine Data Catalog can also be searched directly from geemap
. Use keywords to filter datasets by name, tag, or description. For instance, searching for "elevation" will display only datasets containing "elevation" in their metadata, returning 52 datasets. You can scroll through the results to locate the NASA SRTM Digital Elevation 30m dataset.
m = geemap.Map()
m
Each dataset page contains detailed information such as availability, provider, Earth Engine snippet, tags, description, and example code. The Image, ImageCollection, or FeatureCollection ID is essential for accessing the dataset in Earth Engine’s JavaScript or Python APIs.
Earth Engine Data Types¶
Earth Engine objects are server-side entities, meaning they are stored and processed on Earth Engine’s servers rather than locally. This setup is comparable to streaming services (e.g., YouTube or Netflix) where the data remains on the provider’s servers, allowing us to access and process geospatial data in real time without downloading it to our local devices.
- Image: The core raster data type in Earth Engine, representing single images or scenes.
- ImageCollection: A collection or sequence of images, often used for time series analysis.
- Geometry: The fundamental vector data type, including shapes like points, lines, and polygons.
- Feature: A Geometry with associated attributes, used to add descriptive data to vector shapes.
- FeatureCollection: A collection of Features, similar to a shapefile with attribute data.
Earth Engine Raster Data¶
Image¶
In Earth Engine, raster data is represented as Image objects. Each Image is composed of bands, with each band having its own name, data type, scale, mask, and projection. Metadata for each image is stored as a set of properties.
Loading Earth Engine Images¶
To load images, use the Earth Engine asset ID within the ee.Image
constructor. Asset IDs can be found in the Earth Engine Data Catalog. For example, to load the NASA SRTM Digital Elevation dataset:
image = ee.Image("USGS/SRTMGL1_003")
image
Visualizing Earth Engine Images¶
To visualize an image, you can specify visualization parameters such as minimum and maximum values and color palettes.
m = geemap.Map(center=[21.79, 70.87], zoom=3)
image = ee.Image("USGS/SRTMGL1_003")
vis_params = {
"min": 0,
"max": 6000,
"palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"], # 'terrain'
}
m.add_layer(image, vis_params, "SRTM")
m
ImageCollection¶
An ImageCollection represents a sequence of images, often used for temporal data like satellite image time series. ImageCollections are created by passing an Earth Engine asset ID into the ImageCollection
constructor. Asset IDs are available in the Earth Engine Data Catalog.
Loading Image Collections¶
To load an ImageCollection, such as the Sentinel-2 surface reflectance collection:
collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
Filtering Image Collections¶
You can filter ImageCollections by location and time. For example, to filter images covering a specific location with low cloud cover:
geometry = ee.Geometry.Point([-86.781999, 36.155265])
images = collection.filterBounds(geometry)
images.size()
images.first()
images = (
collection.filterBounds(geometry)
.filterDate("2025-03-01", "2025-06-10")
.filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 5))
)
images.size()
To view the filtered collection on a map:
m = geemap.Map()
image = images.first()
vis = {
"min": 0.0,
"max": 3000,
"bands": ["B4", "B3", "B2"],
}
m.add_layer(image, vis, "Sentinel-2")
m.centerObject(image, 8)
m
Visualizing Image Collections¶
To visualize an ImageCollection as a single composite image, you need to aggregate the collection. For example, using the collection.median()
method creates an image representing the median value across the collection.
m = geemap.Map()
image = images.median()
vis = {
"min": 0.0,
"max": 3000,
"bands": ["B8", "B4", "B3"],
}
m.add_layer(image, vis, "Sentinel-2")
m.centerObject(geometry, 8)
m
Earth Engine Vector Data¶
A FeatureCollection is a collection of Features. It functions similarly to a GeoJSON FeatureCollection, where features include associated properties or attributes. For example, a shapefile’s data can be represented as a FeatureCollection.
Loading Feature Collections¶
The Earth Engine Data Catalog includes various vector datasets, such as U.S. Census data and country boundaries, as feature collections. Feature Collection IDs are accessible by searching the data catalog. For example, to load the TIGER roads data by the U.S. Census Bureau:
m = geemap.Map()
fc = ee.FeatureCollection("TIGER/2016/Roads")
m.set_center(-83.909463, 35.959111, 12)
m.add_layer(fc, {}, "Census roads")
m
Filtering Feature Collections¶
The filter
method allows you to filter a FeatureCollection based on certain attribute values. For instance, we can filter for specific states using the eq
filter to select "Tennessee":
m = geemap.Map()
states = ee.FeatureCollection("TIGER/2018/States")
fc = states.filter(ee.Filter.eq("NAME", "Tennessee"))
m.add_layer(fc, {}, "Tennessee")
m.center_object(fc, 7)
m
To retrieve properties of the first feature in the collection, use:
feat = fc.first()
feat.toDictionary()
You can also convert a FeatureCollection to a Pandas DataFrame for easier analysis with:
geemap.ee_to_df(fc)
m = geemap.Map()
states = ee.FeatureCollection("TIGER/2018/States")
fc = states.filter(ee.Filter.inList("NAME", ["California", "Oregon", "Washington"]))
m.add_layer(fc, {}, "West Coast")
m.center_object(fc, 5)
m
region = m.user_roi
if region is None:
region = ee.Geometry.BBox(-88.40, 29.88, -77.90, 35.39)
fc = ee.FeatureCollection("TIGER/2018/States").filterBounds(region)
m.add_layer(fc, {}, "Southeastern U.S.")
m.center_object(fc, 6)
A FeatureCollection can be used to clip an image. The clipToCollection
method clips an image to the geometry of a feature collection. For example, to clip a Landsat image to the boundary of France:
m = geemap.Map(center=(40, -100), zoom=4)
landsat7 = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003")
countries = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level0")
fc = countries.filter(ee.Filter.eq("ADM0_NAME", "Germany"))
image = landsat7.clipToCollection(fc)
m.add_layer(
image,
{"bands": ["B4", "B3", "B2"], "min": 20, "max": 200, "gamma": 2.0},
"Landsat 7",
)
m.center_object(fc, 6)
m
Visualizing Feature Collections¶
Once loaded, feature collections can be visualized on an interactive map. For example, visualizing U.S. state boundaries from the census data:
m = geemap.Map(center=[40, -100], zoom=4)
states = ee.FeatureCollection("TIGER/2018/States")
m.add_layer(states, {}, "US States")
m
Feature collections can also be styled with additional parameters. To apply a custom style, specify options like color and line width:
m = geemap.Map(center=[40, -100], zoom=4)
states = ee.FeatureCollection("TIGER/2018/States")
style = {"color": "0000ffff", "width": 2, "lineType": "solid", "fillColor": "FF000080"}
m.add_layer(states.style(**style), {}, "US States")
m
Exporting Earth Engine Data¶
Exporting Images¶
This section demonstrates exporting Earth Engine images. First, a Landsat image is added to the map for visualization, and a rectangular region of interest (ROI) is specified. Exporting options include saving the image locally with specified scale and region or exporting to Google Drive. It’s possible to define custom CRS and transformation settings when saving the image.
Add a Landsat image to the map.
m = geemap.Map()
image = ee.Image("LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318").select(
["B5", "B4", "B3"]
)
vis_params = {"min": 0, "max": 0.5, "gamma": [0.95, 1.1, 1]}
m.center_object(image)
m.add_layer(image, vis_params, "Landsat")
m
Add a rectangle to the map.
region = ee.Geometry.BBox(-122.5955, 37.5339, -122.0982, 37.8252)
fc = ee.FeatureCollection(region)
style = {"color": "ffff00ff", "fillColor": "00000000"}
m.add_layer(fc.style(**style), {}, "ROI")
To local drive
geemap.ee_export_image(image, filename="landsat.tif", scale=30, region=region)
Check image projection.
projection = image.select(0).projection().getInfo()
projection
crs = projection["crs"]
crs_transform = projection["transform"]
Specify region, crs, and crs_transform.
geemap.ee_export_image(
image,
filename="landsat_crs.tif",
crs=crs,
crs_transform=crs_transform,
region=region,
)
To Google Drive
geemap.ee_export_image_to_drive(
image, description="landsat", folder="export", region=region, scale=30
)
geemap.download_ee_image(image, "landsat.tif", scale=90)
Exporting Image Collections¶
Image collections, like time series data, can be filtered and exported as multiple images. Here, a National Agriculture Imagery Program (NAIP) collection is filtered by date and location. The collection can then be saved locally or sent to Google Drive, making it easier to handle large datasets.
point = ee.Geometry.Point(-99.2222, 46.7816)
collection = (
ee.ImageCollection("USDA/NAIP/DOQQ")
.filterBounds(point)
.filterDate("2008-01-01", "2018-01-01")
.filter(ee.Filter.listContains("system:band_names", "N"))
)
collection.aggregate_array("system:index")
To local drive
geemap.ee_export_image_collection(collection, out_dir=".", scale=10)
To Google Drive
geemap.ee_export_image_collection_to_drive(collection, folder="export", scale=10)
Exporting Feature Collections¶
Feature collections, such as state boundaries, are exportable in multiple formats (e.g., Shapefile, GeoJSON, and CSV). This example exports the Alaska state boundary as different vector formats both locally and to Google Drive. Additionally, exported data can be directly loaded into a GeoDataFrame for further manipulation in Python.
m = geemap.Map()
states = ee.FeatureCollection("TIGER/2018/States")
fc = states.filter(ee.Filter.eq("NAME", "Alaska"))
m.add_layer(fc, {}, "Alaska")
m.center_object(fc, 4)
m
To local drive
geemap.ee_to_shp(fc, filename="Alaska.shp")
geemap.ee_export_vector(fc, filename="Alaska.shp")
geemap.ee_to_geojson(fc, filename="Alaska.geojson")
geemap.ee_to_csv(fc, filename="Alaska.csv")
gdf = geemap.ee_to_gdf(fc)
gdf
df = geemap.ee_to_df(fc)
df
To Google Drive
geemap.ee_export_vector_to_drive(
fc, description="Alaska", fileFormat="SHP", folder="export"
)
m = geemap.Map()
roi = ee.Geometry.BBox(-115.5541, 35.8044, -113.9035, 36.5581)
m.add_layer(roi)
m.center_object(roi)
m
timelapse = geemap.landsat_timelapse(
roi,
out_gif="las_vegas.gif",
start_year=1984,
end_year=2023,
bands=["NIR", "Red", "Green"],
frames_per_second=5,
title="Las Vegas, NV",
font_color="blue",
)
geemap.show_image(timelapse)
m = geemap.Map()
roi = ee.Geometry.BBox(113.8252, 22.1988, 114.0851, 22.3497)
m.add_layer(roi)
m.center_object(roi)
m
timelapse = geemap.landsat_timelapse(
roi,
out_gif="hong_kong.gif",
start_year=1990,
end_year=2022,
start_date="01-01",
end_date="12-31",
bands=["SWIR1", "NIR", "Red"],
frames_per_second=3,
title="Hong Kong",
)
geemap.show_image(timelapse)
Sentinel-2 Timelapse¶
This example generates a timelapse of Sentinel-2 data for a selected ROI, with options to draw a custom ROI on the map. The timelapse spans 2017-2023, focusing on the June to September period each year.
m = geemap.Map()
m
Pan and zoom the map to an area of interest. Use the drawing tools to draw a rectangle on the map. If no rectangle is drawn, the default rectangle shown below will be used.
roi = m.user_roi
if roi is None:
roi = ee.Geometry.BBox(-74.7222, -8.5867, -74.1596, -8.2824)
m.add_layer(roi)
m.center_object(roi)
timelapse = geemap.sentinel2_timelapse(
roi,
out_gif="sentinel2.gif",
start_year=2017,
end_year=2023,
start_date="01-01",
end_date="12-31",
frequency="year",
bands=["SWIR1", "NIR", "Red"],
frames_per_second=3,
title="Sentinel-2 Timelapse",
)
geemap.show_image(timelapse)
MODIS Timelapse¶
The MODIS timelapse can showcase NDVI (vegetation index) or ocean color data over time. In the vegetation example, we visualize NDVI from 2000-2022 with country overlays. The temperature example animates Aqua satellite data for monthly temperature trends from 2018-2020 with continent overlays.
m = geemap.Map()
m
roi = m.user_roi
if roi is None:
roi = ee.Geometry.BBox(-18.6983, -36.1630, 52.2293, 38.1446)
m.add_layer(roi)
m.center_object(roi)
timelapse = geemap.modis_ndvi_timelapse(
roi,
out_gif="ndvi.gif",
data="Terra",
band="NDVI",
start_date="2000-01-01",
end_date="2022-12-31",
frames_per_second=3,
title="MODIS NDVI Timelapse",
overlay_data="countries",
)
geemap.show_image(timelapse)
MODIS temperature
m = geemap.Map()
m
roi = m.user_roi
if roi is None:
roi = ee.Geometry.BBox(-171.21, -57.13, 177.53, 79.99)
m.add_layer(roi)
m.center_object(roi)
timelapse = geemap.modis_ocean_color_timelapse(
satellite="Aqua",
start_date="2018-01-01",
end_date="2020-12-31",
roi=roi,
frequency="month",
out_gif="temperature.gif",
overlay_data="continents",
overlay_color="yellow",
overlay_opacity=0.5,
)
geemap.show_image(timelapse)
GOES Timelapse¶
GOES timelapse generation can animate atmospheric phenomena in near real-time. We create animations for different ROIs, including hurricane tracking and fire events using GOES-17 data with custom time windows and frame rates.
roi = ee.Geometry.BBox(167.1898, -28.5757, 202.6258, -12.4411)
start_date = "2022-01-15T03:00:00"
end_date = "2022-01-15T07:00:00"
data = "GOES-17"
scan = "full_disk"
timelapse = geemap.goes_timelapse(
roi, "goes.gif", start_date, end_date, data, scan, framesPerSecond=5
)
geemap.show_image(timelapse)
roi = ee.Geometry.BBox(-159.5954, 24.5178, -114.2438, 60.4088)
start_date = "2021-10-24T14:00:00"
end_date = "2021-10-25T01:00:00"
data = "GOES-17"
scan = "full_disk"
timelapse = geemap.goes_timelapse(
roi, "hurricane.gif", start_date, end_date, data, scan, framesPerSecond=5
)
geemap.show_image(timelapse)
roi = ee.Geometry.BBox(-121.0034, 36.8488, -117.9052, 39.0490)
start_date = "2020-09-05T15:00:00"
end_date = "2020-09-06T02:00:00"
data = "GOES-17"
scan = "full_disk"
timelapse = geemap.goes_fire_timelapse(
roi, "fire.gif", start_date, end_date, data, scan, framesPerSecond=5
)
geemap.show_image(timelapse)
Data Visualization in 3D¶
import leafmap.maplibregl as leafmap
m = leafmap.Map(style="3d-terrain", projection="globe")
dataset = ee.ImageCollection("ESA/WorldCover/v200").first()
vis_params = {"bands": ["Map"]}
m.add_ee_layer(dataset, vis_params, name="ESA Worldcover", opacity=0.5)
m.add_legend(builtin_legend="ESA_WorldCover", title="ESA Landcover")
m
m = leafmap.Map(style="darkmatter", projection="globe")
dataset = ee.ImageCollection("NOAA/VIIRS/DNB/ANNUAL_V22").filter(
ee.Filter.date("2022-01-01", "2023-01-01")
)
nighttime = dataset.select("maximum")
nighttimeVis = {"min": 0.0, "max": 60.0}
m.add_ee_layer(nighttime, nighttimeVis, name="Nighttime")
countries = ee.FeatureCollection("USDOS/LSIB_SIMPLE/2017")
styleParams = {
"fillColor": "00000000",
"color": "ff0000",
"width": 1.0,
}
countries = countries.style(**styleParams)
m.add_ee_layer(countries, {}, name="Country Boundaries")
m