diff --git a/book/geospatial/geemap.ipynb b/book/geospatial/geemap.ipynb index 2d4a93b..9b5f004 100644 --- a/book/geospatial/geemap.ipynb +++ b/book/geospatial/geemap.ipynb @@ -9,53 +9,39 @@ "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/giswqs/geog-312/blob/main/book/geospatial/geemap.ipynb)\n", "\n", - "## Introduction\n", + "## Overview\n", "\n", - "This notebook is designed for users who are new to Google Earth Engine and geemap. It provides an introduction to Earth Engine and geemap, and demonstrates how to use geemap to explore and analyze Earth Engine data.\n", - "\n", - "### Prerequisites\n", - "\n", - "- To use geemap and the Earth Engine Python API, you must [register](https://code.earthengine.google.com/register) for an Earth Engine account and follow the instructions [here](https://docs.google.com/document/d/1ZGSmrNm6_baqd8CHt33kIBWOlvkh-HLr46bODgJN1h0/edit?usp=sharing) to create a Cloud Project. Earth Engine is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). To test whether you can use authenticate the Earth Engine Python API, please run [this notebook](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/notebooks/geemap_colab.ipynb) on Google Colab.\n", - "\n", - "- It is recommended that attendees have a basic understanding of Python and Jupyter Notebook.\n", - "- Familiarity with the Earth Engine JavaScript API is not required but will be helpful.\n", - "- Attendees can use Google Colab to follow this short course without installing anything on their computer.\n", - "\n", - "\n", - "### Agenda\n", - "\n", - "The main topics to be covered in this workshop include:\n", - "\n", - "* Create interactive maps\n", - "* Visualize Earth Engine data\n", - "* Explore Earth Engine Data Catalogs\n", - "* Analyze Earth Engine data\n", - "* Export Earth Engine data\n", - "* Create timelapse animations\n", + "This lecture introduces cloud-based geospatial analysis using the [Google Earth Engine](https://earthengine.google.com) (GEE) API in combination with the [geemap](https://geemap.org) Python package. We will cover core concepts of Earth Engine, visualization techniques, and practical workflows to perform analyses within a Jupyter environment. \n", "\n", + "## Learning Objectives\n", "\n", - "## Introduction to Earth Engine and geemap\n", + "By the end of this lecture, you will be able to:\n", + "* Explain the fundamentals of the Google Earth Engine platform, including its data types and cloud-based capabilities\n", + "* Set up and configure the geemap library for conducting geospatial analyses in the cloud\n", + "* Perform essential geospatial operations, including filtering, visualizing, and exporting data from Earth Engine\n", + "* Access and manipulate both raster and vector data using Earth Engine\n", + "* Create timelapse animations from satellite imagery\n", + "* Create interactive charts from Earth Engine data\n", "\n", - "Earth Engine is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). For more than a decade, Earth Engine has enabled planetary-scale Earth data science and analysis by nonprofit organizations, research scientists, and other impact users.\n", - "\n", - "With the launch of Earth Engine for [commercial use](https://earthengine.google.com/commercial), commercial customers will be charged for Earth Engine services. However, Earth Engine will remain free of charge for noncommercial use and research projects. Nonprofit organizations, academic institutions, educators, news media, Indigenous governments, and government researchers are eligible to use Earth Engine free of charge, just as they have done for over a decade.\n", + "### Prerequisites\n", "\n", - "The geemap Python package is built upon the Earth Engine Python API and open-source mapping libraries. It allows Earth Engine users to interactively manipulate, analyze, and visualize geospatial big data in a Jupyter environment. Since its creation in April 2020, geemap has received over 3,300 GitHub stars and is being used by over 2,700 projects on GitHub.\n", + "Before beginning with geemap and Google Earth Engine, ensure the following steps are completed:\n", "\n", - "## Google Colab and Earth Engine Python API authentication\n", + "- **Register for a Google Earth Engine account**: Go to [https://code.earthengine.google.com/register](https://code.earthengine.google.com/register) to sign up for a GEE account. After registering, you will need to set up a Google Cloud Project and enable the Earth Engine API by following the instructions [here](https://docs.google.com/document/d/1ZGSmrNm6_baqd8CHt33kIBWOlvkh-HLr46bODgJN1h0/edit?usp=sharing). GEE is free for [noncommercial and research use](https://earthengine.google.com/noncommercial).\n", "\n", - "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/workshops/Taiwan_2024.ipynb)\n", + "- **Verify API Authentication**: To confirm that your Earth Engine account is set up correctly, try running [this test notebook](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/notebooks/geemap_colab.ipynb) in Google Colab.\n", "\n", - "### Change Colab dark theme\n", + "## Introduction to Google Earth Engine\n", "\n", - "Currently, ipywidgets does not work well with Colab dark theme. Some of the geemap widgets may not display properly in Colab dark theme.It is recommended that you change Colab to the light theme.\n", + "### Google Earth Engine Overview\n", "\n", - "![](https://i.imgur.com/EJ0GDP8.png)\n", + "[Google Earth Engine](https://earthengine.google.com) 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.\n", "\n", + "Earth Engine is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). 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](https://earthengine.google.com/commercial) may require a paid license.\n", "\n", - "### Install geemap\n", + "### Installing Geemap\n", "\n", - "The geemap package is pre-installed in Google Colab and is updated to the latest minor or major release every few weeks. Some optional dependencies of geemap being used by this notebook are not pre-installed in Colab. Uncomment the following code block to install geemap and some optional dependencies." + "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:" ] }, { @@ -73,30 +59,15 @@ "id": "2", "metadata": {}, "source": [ - "Note that some geemap features may not work properly in the Google Colab environmennt. If you are familiar with [Anaconda](https://www.anaconda.com/download) or [Miniconda](https://docs.anaconda.com/free/miniconda), it is recommended to create a new conda environment to install geemap and its optional dependencies on your local computer. \n", - "\n", - "```bash\n", - "conda create -n gee python=3.11\n", - "conda activate gee\n", - "conda install -c conda-forge mamba\n", - "mamba install -c conda-forge geemap pygis\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "3", - "metadata": {}, - "source": [ - "### Import libraries\n", + "### Import Libraries\n", "\n", - "Import the earthengine-api and geemap." + "To start, import the necessary libraries for working with Google Earth Engine (GEE) and geemap." ] }, { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -106,22 +77,30 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "4", "metadata": {}, "source": [ - "### Authenticate and initialize Earth Engine\n", + "### Authenticate and Initialize Earth Engine\n", "\n", - "You will need to create a [Google Cloud Project](https://console.cloud.google.com/projectcreate) and enable the [Earth Engine API](https://console.cloud.google.com/apis/api/earthengine.googleapis.com) for the project. You can find detailed instructions [here](https://book.geemap.org/chapters/01_introduction.html#earth-engine-authentication)." + "To authenticate and initialize your Earth Engine environment, you’ll need to create a Google Cloud Project and enable the [Earth Engine API](https://console.cloud.google.com/apis/api/earthengine.googleapis.com) for the project if you have not done so already. You can find detailed setup instructions [here](https://developers.google.com/earth-engine/guides/access#a-role-in-a-cloud-project)." ] }, { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "5", "metadata": {}, "outputs": [], "source": [ - "ee.Authenticate()" + "geemap.ee_initialize()" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "Running the code above will prompt you to authenticate your Earth Engine account. Follow the instructions to complete the authentication process on Colab. To avoid this step in the future, you can set up a Colab secret with the name `EARTHENGINE_TOKEN` and your Earth Engine token as the value, which can be obtained by running `geemap.get_ee_token()`." ] }, { @@ -131,7 +110,7 @@ "metadata": {}, "outputs": [], "source": [ - "ee.Initialize(project=\"YOUR-PROJECT-ID\")" + "# geemap.get_ee_token()" ] }, { @@ -139,9 +118,9 @@ "id": "8", "metadata": {}, "source": [ - "## Creating interactive maps\n", + "### Creating Interactive Maps\n", "\n", - "Let's create an interactive map using the `ipyleaflet` plotting backend. The [`geemap.Map`](https://geemap.org/geemap/#geemap.geemap.m) class inherits the [`ipyleaflet.Map`](https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/map.html) class. Therefore, you can use the same syntax to create an interactive map as you would with `ipyleaflet.Map`." + "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`." ] }, { @@ -159,7 +138,7 @@ "id": "10", "metadata": {}, "source": [ - "To display it in a Jupyter notebook, simply ask for the object representation:" + "To display the map in a Jupyter notebook, simply enter the map object:" ] }, { @@ -177,7 +156,13 @@ "id": "12", "metadata": {}, "source": [ - "To customize the map, you can specify various keyword arguments, such as `center` ([lat, lon]), `zoom`, `width`, and `height`. The default `width` is `100%`, which takes up the entire cell width of the Jupyter notebook. The `height` argument accepts a number or a string. If a number is provided, it represents the height of the map in pixels. If a string is provided, the string must be in the format of a number followed by `px`, e.g., `600px`." + "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`.\n", + "\n", + "#### Example Maps\n", + "\n", + "**Map of the Contiguous United States** \n", + "\n", + " To center the map on the contiguous United States, use:" ] }, { @@ -187,7 +172,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map(center=[40, -100], zoom=4, height=\"600xp\")\n", + "m = geemap.Map(center=[40, -100], zoom=4, height=\"600px\")\n", "m" ] }, @@ -196,7 +181,7 @@ "id": "14", "metadata": {}, "source": [ - "To hide a control, set `control_name` to `False`, e.g., `draw_ctrl=False`." + "**Map of the state of Tennessee**" ] }, { @@ -206,7 +191,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map(data_ctrl=False, toolbar_ctrl=False, draw_ctrl=False)\n", + "m = geemap.Map(center=[35.746512, -86.209818], zoom=8)\n", "m" ] }, @@ -215,11 +200,7 @@ "id": "16", "metadata": {}, "source": [ - "### Adding basemaps\n", - "\n", - "There are several ways to add basemaps to a map. You can specify the basemap to use in the `basemap` keyword argument when creating the map. Alternatively, you can add basemap layers to the map using the `add_basemap` method. Geemap has hundreds of built-in basemaps available that can be easily added to the map with only one line of code.\n", - "\n", - "Create a map by specifying the basemap to use as follows. For example, the `Esri.WorldImagery` basemap represents the Esri world imagery basemap." + "**Map of the city of Knoxville, TN**" ] }, { @@ -229,7 +210,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map(basemap=\"Esri.WorldImagery\")\n", + "m = geemap.Map(center=[35.959111, -83.909463], zoom=13)\n", "m" ] }, @@ -238,7 +219,7 @@ "id": "18", "metadata": {}, "source": [ - "You can add as many basemaps as you like to the map. For example, the following code adds the `OpenTopoMap` basemap to the map above:" + "To hide specific map controls, set the corresponding control argument to `False`, such as `draw_ctrl=False` to hide the drawing control." ] }, { @@ -248,7 +229,8 @@ "metadata": {}, "outputs": [], "source": [ - "m.add_basemap(\"OpenTopoMap\")" + "m = geemap.Map(data_ctrl=False, toolbar_ctrl=False, draw_ctrl=False)\n", + "m" ] }, { @@ -256,7 +238,11 @@ "id": "20", "metadata": {}, "source": [ - "You can also change basemaps interactively using the basemap GUI." + "### Adding Basemaps\n", + "\n", + "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.\n", + "\n", + "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." ] }, { @@ -266,8 +252,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "m.add(\"basemap_selector\")\n", + "m = geemap.Map(basemap=\"Esri.WorldImagery\")\n", "m" ] }, @@ -276,23 +261,9 @@ "id": "22", "metadata": {}, "source": [ - "## Using Earth Engine data\n", - "\n", - "### Earth Engine data types\n", - "\n", - "Earth Engine objects are server-side objects rather than client-side objects, which means that they are not stored locally on your computer. Similar to video streaming services (e.g., YouTube, Netflix, and Hulu), which store videos/movies on their servers, Earth Engine data are stored on the Earth Engine servers. We can stream geospatial data from Earth Engine on-the-fly without having to download the data just like we can watch videos from streaming services using a web browser without having to download the entire video to your computer.\n", + "#### Adding Multiple Basemaps\n", "\n", - "- **Image**: the fundamental raster data type in Earth Engine.\n", - "- **ImageCollection**: a stack or time-series of images.\n", - "- **Geometry**: the fundamental vector data type in Earth Engine.\n", - "- **Feature**: a Geometry with attributes.\n", - "- **FeatureCollection**: a set of features.\n", - "\n", - "### Image\n", - "\n", - "Raster data in Earth Engine are represented as **Image** objects. Images are composed of one or more bands and each band has its own name, data type, scale, mask and projection. Each image has metadata stored as a set of properties.\n", - "\n", - "#### Loading Earth Engine images" + "You can add multiple basemaps to a map by calling `add_basemap` multiple times. For example, the following code adds the `Esri.WorldTopoMap` and `OpenTopoMap` basemaps to the existing map:" ] }, { @@ -302,48 +273,38 @@ "metadata": {}, "outputs": [], "source": [ - "image = ee.Image(\"USGS/SRTMGL1_003\")\n", - "image" + "m.add_basemap(\"Esri.WorldTopoMap\")" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "24", "metadata": {}, + "outputs": [], "source": [ - "#### Visualizing Earth Engine images" + "m.add_basemap(\"OpenTopoMap\")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "25", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map(center=[23.5790, 121.4181], zoom=8)\n", - "image = ee.Image(\"USGS/SRTMGL1_003\")\n", - "vis_params = {\n", - " \"min\": 0,\n", - " \"max\": 6000,\n", - " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"], # 'terrain'\n", - "}\n", - "m.add_layer(image, vis_params, \"SRTM\")\n", - "m" + "#### Listing Available Basemaps\n", + "\n", + "To view the first 10 available basemaps, use:" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "26", "metadata": {}, + "outputs": [], "source": [ - "### ImageCollection\n", - "\n", - "An `ImageCollection` is a stack or sequence of images. An `ImageCollection` can be loaded by passing an Earth Engine asset ID into the `ImageCollection` constructor. You can find `ImageCollection` IDs in the [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets).\n", - "\n", - "#### Loading image collections\n", - "\n", - "For example, to load the image collection of the [Sentinel-2 surface reflectance](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR):" + "basemaps = list(geemap.basemaps.keys())\n", + "len(geemap.basemaps)" ] }, { @@ -353,7 +314,7 @@ "metadata": {}, "outputs": [], "source": [ - "collection = ee.ImageCollection(\"COPERNICUS/S2_SR\")" + "basemaps[:10]" ] }, { @@ -361,9 +322,18 @@ "id": "28", "metadata": {}, "source": [ - "#### Visualizing image collections\n", + "### Google Basemaps\n", "\n", - "To visualize an Earth Engine **ImageCollection**, we need to convert an **ImageCollection** to an **Image** by compositing all the images in the collection to a single image representing, for example, the min, max, median, mean or standard deviation of the images. For example, to create a median value image from a collection, use the `collection.median()` method. Let's create a median image from the Sentinel-2 surface reflectance collection:" + "Due to licensing restrictions, Google basemaps are not included in geemap by default. However, users can add Google basemaps manually at their own discretion using the following URLs:\n", + "\n", + "```text\n", + " ROADMAP: https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}\n", + " SATELLITE: https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}\n", + " TERRAIN: https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}\n", + " HYBRID: https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}\n", + "```\n", + "\n", + "For example, to add Google Satellite as a tile layer:" ] }, { @@ -374,21 +344,8 @@ "outputs": [], "source": [ "m = geemap.Map()\n", - "collection = (\n", - " ee.ImageCollection(\"COPERNICUS/S2_SR\")\n", - " .filterDate(\"2023-01-01\", \"2024-01-01\")\n", - " .filter(ee.Filter.lt(\"CLOUDY_PIXEL_PERCENTAGE\", 5))\n", - ")\n", - "image = collection.median()\n", - "\n", - "vis = {\n", - " \"min\": 0.0,\n", - " \"max\": 3000,\n", - " \"bands\": [\"B4\", \"B3\", \"B2\"],\n", - "}\n", - "\n", - "m.set_center(121.4181, 23.5790, 8)\n", - "m.add_layer(image, vis, \"Sentinel-2\")\n", + "url = \"https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}\"\n", + "m.add_tile_layer(url, name=\"Google Satellite\", attribution=\"Google\")\n", "m" ] }, @@ -397,13 +354,11 @@ "id": "30", "metadata": {}, "source": [ - "### FeatureCollection\n", - "\n", - "A **FeatureCollection** is a collection of Features. A FeatureCollection is analogous to a GeoJSON FeatureCollection object, i.e., a collection of features with associated properties/attributes. Data contained in a shapefile can be represented as a FeatureCollection.\n", + "## Introduction to Interactive Maps and Tools\n", "\n", - "#### Loading feature collections\n", + "### Basemap Selector\n", "\n", - "The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) hosts a variety of vector datasets (e.g,, US Census data, country boundaries, and more) as feature collections. You can find feature collection IDs by searching the data catalog. For example, to load the [TIGER roads data](https://developers.google.com/earth-engine/datasets/catalog/TIGER_2016_Roads) by the U.S. Census Bureau:" + "The basemap selector allows you to choose from various basemaps via a dropdown menu. Adding it to your map provides easy access to different map backgrounds." ] }, { @@ -414,9 +369,7 @@ "outputs": [], "source": [ "m = geemap.Map()\n", - "fc = ee.FeatureCollection(\"TIGER/2016/Roads\")\n", - "m.set_center(-73.9596, 40.7688, 12)\n", - "m.add_layer(fc, {}, \"Census roads\")\n", + "m.add(\"basemap_selector\")\n", "m" ] }, @@ -425,10 +378,9 @@ "id": "32", "metadata": {}, "source": [ - "#### Filtering feature collections\n", + "### Layer Manager\n", "\n", - "* [geoBoundaries: Political administrative boundaries at Country level (ADM0)](https://developers.google.com/earth-engine/datasets/catalog/WM_geoLab_geoBoundaries_600_ADM0)\n", - "* [geoBoundaries: Political administrative boundaries at District level (ADM1)](https://developers.google.com/earth-engine/datasets/catalog/WM_geoLab_geoBoundaries_600_ADM1)\n" + "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." ] }, { @@ -438,27 +390,28 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM0\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeName\", \"Taiwan\"))\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.center_object(fc, 8)\n", + "m = geemap.Map(center=(40, -100), zoom=4)\n", + "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "vis_params = {\n", + " \"min\": 0,\n", + " \"max\": 4000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "}\n", + "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", + "m.add_layer(states, {}, \"US States\")\n", + "m.add(\"layer_manager\")\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "34", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM1\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeGroup\", \"TWN\"))\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.center_object(fc, 8)\n", - "m" + "### Inspector Tool\n", + "\n", + "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." ] }, { @@ -468,13 +421,24 @@ "metadata": {}, "outputs": [], "source": [ - "region = m.user_roi\n", - "if region is None:\n", - " region = ee.Geometry.BBox(119.1687, 21.7799, 122.981, 25.4234)\n", - "\n", - "fc = fc.filterBounds(region)\n", - "m.add_layer(fc, {}, \"Taiwan 2\")\n", - "m.center_object(fc, 8)" + "m = geemap.Map(center=(40, -100), zoom=4)\n", + "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", + "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\")\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "vis_params = {\n", + " \"min\": 0,\n", + " \"max\": 4000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "}\n", + "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", + "m.add_layer(\n", + " landsat7,\n", + " {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 20, \"max\": 200, \"gamma\": 2.0},\n", + " \"Landsat 7\",\n", + ")\n", + "m.add_layer(states, {}, \"US States\")\n", + "m.add(\"inspector\")\n", + "m" ] }, { @@ -482,7 +446,11 @@ "id": "36", "metadata": {}, "source": [ - "#### Visualizing feature collections" + "### Layer Editor\n", + "\n", + "With the layer editor, you can adjust visualization parameters of Earth Engine data for better clarity and focus. It supports single-band images, multi-band images, and feature collections.\n", + "\n", + "#### Single-Band image" ] }, { @@ -492,28 +460,24 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM1\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeGroup\", \"TWN\"))\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.center_object(fc, 8)\n", + "m = geemap.Map(center=(40, -100), zoom=4)\n", + "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", + "vis_params = {\n", + " \"min\": 0,\n", + " \"max\": 4000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "}\n", + "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", + "m.add(\"layer_editor\", layer_dict=m.ee_layers[\"SRTM DEM\"])\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "38", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM1\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeGroup\", \"TWN\"))\n", - "style = {\"color\": \"000000ff\", \"width\": 2, \"lineType\": \"solid\", \"fillColor\": \"FF000000\"}\n", - "m.add_layer(fc.style(**style), {}, \"Taiwan\")\n", - "m.center_object(fc, 8)\n", - "m" + "#### Multi-Band image" ] }, { @@ -523,12 +487,14 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM1\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeGroup\", \"TWN\"))\n", - "style = {\"color\": \"0000ffff\", \"width\": 2, \"lineType\": \"solid\", \"fillColor\": \"FF000080\"}\n", - "m.add_layer(fc.style(**style), {}, \"Taiwan\")\n", - "m.center_object(fc, 8)\n", + "m = geemap.Map(center=(40, -100), zoom=4)\n", + "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\")\n", + "m.add_layer(\n", + " landsat7,\n", + " {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 20, \"max\": 200, \"gamma\": 2.0},\n", + " \"Landsat 7\",\n", + ")\n", + "m.add(\"layer_editor\", layer_dict=m.ee_layers[\"Landsat 7\"])\n", "m" ] }, @@ -537,15 +503,7 @@ "id": "40", "metadata": {}, "source": [ - "### Earth Engine Data Catalog\n", - "\n", - "The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) hosts a variety of geospatial datasets. As of July 2024, the catalog contains over [1,100 datasets](https://github.com/opengeos/Earth-Engine-Catalog/blob/master/gee_catalog.tsv) with a total size of over 100 petabytes. Some notable datasets include: Landsat, Sentinel, MODIS, NAIP, etc. For a complete list of datasets in CSV or JSON formats, see the [Earth Engine Datasets List](https://github.com/giswqs/Earth-Engine-Catalog/blob/master/gee_catalog.tsv).\n", - "\n", - "#### Searching for datasets\n", - "\n", - "The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets/catalog) is searchable. You can search datasets by name, keyword, or tag. For example, enter \"elevation\" in the search box will filter the catalog to show only datasets containing \"elevation\" in their name, description, or tags. 52 datasets are returned for this search query. Scroll down the list to find the [NASA SRTM Digital Elevation 30m](https://developers.google.com/earth-engine/datasets/catalog/USGS_SRTMGL1_003#description) dataset. On each dataset page, you can find the following information, including Dataset Availability, Dataset Provider, Earth Engine Snippet, Tags, Description, Code Example, and more. One important piece of information is the Image/ImageCollection/FeatureCollection ID of each dataset, which is essential for accessing the dataset through the Earth Engine JavaScript or Python APIs.\n", - "\n", - "![](https://i.imgur.com/B3rf4QN.jpg)" + "#### Feature Collection" ] }, { @@ -555,134 +513,112 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", + "m = geemap.Map(center=(40, -100), zoom=4)\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "m.add_layer(states, {}, \"US States\")\n", + "m.add(\"layer_editor\", layer_dict=m.ee_layers[\"US States\"])\n", "m" ] }, + { + "cell_type": "markdown", + "id": "42", + "metadata": {}, + "source": [ + "### Draw Control\n", + "\n", + "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:\n", + "\n", + "- To return the last drawn feature as an `ee.Geometry()`, use `m.user_roi`\n", + "- To return all drawn features as an `ee.FeatureCollection()`, use `m.user_rois`" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "43", "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", + "m = geemap.Map(center=(40, -100), zoom=4)\n", "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", "vis_params = {\n", " \"min\": 0,\n", " \"max\": 4000,\n", - " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + " \"palette\": \"terrain\",\n", "}\n", "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", + "m.add(\"layer_manager\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "44", "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "counties = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM0\")\n", - "fc = counties.filter(ee.Filter.eq(\"shapeName\", \"Taiwan\"))\n", - "dem = ee.Image(\"USGS/SRTMGL1_003\").clipToCollection(fc)\n", - "vis_params = {\n", - " \"min\": 0,\n", - " \"max\": 4000,\n", - " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", - "}\n", - "\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", - "m.center_object(fc, 8)\n", - "m" + "if m.user_roi is not None:\n", + " image = dem.clip(m.user_roi)\n", + " m.layers[1].visible = False\n", + " m.add_layer(image, vis_params, \"Clipped DEM\")" ] }, { "cell_type": "markdown", - "id": "44", + "id": "45", "metadata": {}, "source": [ - "### Exercise 1 - Creating cloud-free imagery\n", + "## The Earth Engine Data Catalog\n", + "\n", + "The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) hosts an extensive collection of geospatial datasets. Currently, he catalog includes over [1,000 datasets](https://github.com/opengeos/Earth-Engine-Catalog/blob/master/gee_catalog.tsv) 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](https://github.com/giswqs/Earth-Engine-Catalog/blob/master/gee_catalog.tsv).\n", "\n", - "Create a cloud-free imagery of Taiwan for the year of 2023. You can use either Landsat 9 or Sentinel-2 imagery. Relevant Earth Engine assets:\n", + "### Searching Datasets on the Earth Engine Website\n", "\n", - "- [ee.FeatureCollection(\"TIGER/2018/States\")](https://developers.google.com/earth-engine/datasets/catalog/TIGER_2018_States)\n", - "- [ee.ImageCollection(\"COPERNICUS/S2_SR\")](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR)\n", - "- [ee.ImageCollection(\"LANDSAT/LC09/C02/T1_L2\")](https://developers.google.com/earth-engine/datasets/catalog/LANDSAT_LC09_C02_T1_L2)\n", + "To browse datasets directly on the Earth Engine website:\n", "\n", - "A sample map of cloud-free imagery for the state of Texas is shown below:\n", + "- View the full catalog: https://developers.google.com/earth-engine/datasets/catalog \n", + "- Search by tags: https://developers.google.com/earth-engine/datasets/tags\n", "\n", - "![](https://i.imgur.com/i3IT0lF.png)" + "### Searching Datasets Within Geemap\n", + "\n", + "The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets/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](https://developers.google.com/earth-engine/datasets/catalog/USGS_SRTMGL1_003#description) dataset." ] }, { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "46", "metadata": {}, "outputs": [], "source": [ - "# Type your code here" + "m = geemap.Map()\n", + "m" ] }, { "cell_type": "markdown", - "id": "46", - "metadata": {}, - "source": [ - "## Visualizing Earth Engine data\n", - "\n", - "### Using the inspector tool\n", - "\n", - "Inspect pixel values and vector features using the inspector tool. " - ] - }, - { - "cell_type": "code", - "execution_count": null, "id": "47", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "\n", - "counties = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM0\")\n", - "fc = counties.filter(ee.Filter.eq(\"shapeName\", \"Taiwan\"))\n", + "![](https://i.imgur.com/B3rf4QN.jpg)\n", "\n", - "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", - "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\").select(\n", - " [\"B1\", \"B2\", \"B3\", \"B4\", \"B5\", \"B7\"]\n", - ")\n", + "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.\n", "\n", - "vis_params = {\n", - " \"min\": 0,\n", - " \"max\": 4000,\n", - " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", - "}\n", + "### Using the Datasets Module in Geemap\n", "\n", - "m.add_layer(\n", - " landsat7,\n", - " {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 20, \"max\": 200, \"gamma\": 2.0},\n", - " \"Landsat 7\",\n", - ")\n", - "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.add(\"inspector\")\n", - "m.center_object(fc, 8)\n", - "m" + "Geemap offers a built-in `datasets` module to access specific datasets programmatically. For example, to access the USGS GAP Alaska dataset:" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "48", "metadata": {}, + "outputs": [], "source": [ - "### Using the plotting tool\n", - "\n", - "Plot spectral profiles of pixels using the plotting tool." + "from geemap.datasets import DATA" ] }, { @@ -692,27 +628,10 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map(center=[40, -100], zoom=4)\n", - "\n", - "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\").select(\n", - " [\"B1\", \"B2\", \"B3\", \"B4\", \"B5\", \"B7\"]\n", - ")\n", - "\n", - "landsat_vis = {\"bands\": [\"B4\", \"B3\", \"B2\"], \"gamma\": 1.4}\n", - "m.add_layer(landsat7, landsat_vis, \"Landsat\")\n", - "\n", - "hyperion = ee.ImageCollection(\"EO1/HYPERION\").filter(\n", - " ee.Filter.date(\"2016-01-01\", \"2017-03-01\")\n", - ")\n", - "\n", - "hyperion_vis = {\n", - " \"min\": 1000.0,\n", - " \"max\": 14000.0,\n", - " \"gamma\": 2.5,\n", - "}\n", - "m.add_layer(hyperion, hyperion_vis, \"Hyperion\")\n", - "m.add_plot_gui()\n", - "m.center_object(fc, 8)\n", + "m = geemap.Map()\n", + "dataset = ee.Image(DATA.USGS_GAP_AK_2001)\n", + "m.add_layer(dataset, {}, \"GAP Alaska\")\n", + "m.centerObject(dataset, zoom=4)\n", "m" ] }, @@ -721,7 +640,7 @@ "id": "50", "metadata": {}, "source": [ - "Set plotting options for Landsat." + "To retrieve metadata for a specific dataset, use the `get_metadata` function:" ] }, { @@ -731,7 +650,9 @@ "metadata": {}, "outputs": [], "source": [ - "m.set_plot_options(add_marker_cluster=True, overlay=True)" + "from geemap.datasets import get_metadata\n", + "\n", + "get_metadata(DATA.USGS_GAP_AK_2001)" ] }, { @@ -739,7 +660,25 @@ "id": "52", "metadata": {}, "source": [ - "Set plotting options for Hyperion." + "## Earth Engine Data Types\n", + "\n", + "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.\n", + "\n", + "- **Image**: The core raster data type in Earth Engine, representing single images or scenes.\n", + "- **ImageCollection**: A collection or sequence of images, often used for time series analysis.\n", + "- **Geometry**: The fundamental vector data type, including shapes like points, lines, and polygons.\n", + "- **Feature**: A Geometry with associated attributes, used to add descriptive data to vector shapes.\n", + "- **FeatureCollection**: A collection of Features, similar to a shapefile with attribute data.\n", + "\n", + "## Earth Engine Raster Data\n", + "\n", + "### Image\n", + "\n", + "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.\n", + "\n", + "#### Loading Earth Engine Images\n", + "\n", + "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:" ] }, { @@ -749,7 +688,8 @@ "metadata": {}, "outputs": [], "source": [ - "m.set_plot_options(add_marker_cluster=True, plot_type=\"bar\")" + "image = ee.Image(\"USGS/SRTMGL1_003\")\n", + "image" ] }, { @@ -757,9 +697,9 @@ "id": "54", "metadata": {}, "source": [ - "### Legends, color bars, and labels\n", + "#### Visualizing Earth Engine Images\n", "\n", - "#### Built-in legends" + "To visualize an image, you can specify visualization parameters such as minimum and maximum values and color palettes." ] }, { @@ -769,62 +709,71 @@ "metadata": {}, "outputs": [], "source": [ - "from geemap.legends import builtin_legends" + "m = geemap.Map(center=[21.79, 70.87], zoom=3)\n", + "image = ee.Image(\"USGS/SRTMGL1_003\")\n", + "vis_params = {\n", + " \"min\": 0,\n", + " \"max\": 6000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"], # 'terrain'\n", + "}\n", + "m.add_layer(image, vis_params, \"SRTM\")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "56", "metadata": {}, - "outputs": [], "source": [ - "for legend in builtin_legends:\n", - " print(legend)" + "### ImageCollection\n", + "\n", + "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](https://developers.google.com/earth-engine/datasets).\n", + "\n", + "#### Loading Image Collections\n", + "\n", + "To load an ImageCollection, such as the Sentinel-2 surface reflectance collection:" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "57", "metadata": {}, + "outputs": [], "source": [ - "Add ESA WorldCover and legend to the map.\n", - "\n", - "https://developers.google.com/earth-engine/datasets/catalog/ESA_WorldCover_v200" + "collection = ee.ImageCollection(\"COPERNICUS/S2_SR_HARMONIZED\")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "58", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "m.add_basemap(\"Esri.WorldImagery\")\n", + "#### Filtering Image Collections\n", "\n", - "dataset = ee.ImageCollection(\"ESA/WorldCover/v200\").first()\n", - "visualization = {\"bands\": [\"Map\"]}\n", - "m.add_layer(dataset, visualization, \"Landcover\")\n", - "m.add_legend(title=\"Land Cover Type\", builtin_legend=\"ESA_WorldCover\")\n", - "m.set_center(121.4181, 23.5790, 8)\n", - "m" + "You can filter ImageCollections by location and time. For example, to filter images covering a specific location with low cloud cover:" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "59", "metadata": {}, + "outputs": [], "source": [ - "#### Custom legends" + "geometry = ee.Geometry.Point([-83.909463, 35.959111])\n", + "images = collection.filterBounds(geometry)\n", + "images.size()" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "60", "metadata": {}, + "outputs": [], "source": [ - "Add a custom legend by specifying a dictionary of colors and labels." + "images.first()" ] }, { @@ -834,28 +783,12 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "m.add_basemap(\"Esri.WorldImagery\")\n", - "\n", - "dataset = ee.ImageCollection(\"ESA/WorldCover/v200\").first()\n", - "visualization = {\"bands\": [\"Map\"]}\n", - "m.add_layer(dataset, visualization, \"Landcover\")\n", - "legend_dict = {\n", - " \"10 Trees\": \"006400\",\n", - " \"20 Shrubland\": \"ffbb22\",\n", - " \"30 Grassland\": \"ffff4c\",\n", - " \"40 Cropland\": \"f096ff\",\n", - " \"50 Built-up\": \"fa0000\",\n", - " \"60 Barren / sparse vegetation\": \"b4b4b4\",\n", - " \"70 Snow and ice\": \"f0f0f0\",\n", - " \"80 Open water\": \"0064c8\",\n", - " \"90 Herbaceous wetland\": \"0096a0\",\n", - " \"95 Mangroves\": \"00cf75\",\n", - " \"100 Moss and lichen\": \"fae6a0\",\n", - "}\n", - "m.add_legend(title=\"Land Cover Type\", legend_dict=legend_dict)\n", - "m.set_center(121.4181, 23.5790, 8)\n", - "m" + "images = (\n", + " collection.filterBounds(geometry)\n", + " .filterDate(\"2024-07-01\", \"2024-10-01\")\n", + " .filter(ee.Filter.lt(\"CLOUDY_PIXEL_PERCENTAGE\", 5))\n", + ")\n", + "images.size()" ] }, { @@ -863,9 +796,7 @@ "id": "62", "metadata": {}, "source": [ - "#### Creating color bars\n", - "\n", - "Add a horizontal color bar." + "To view the filtered collection on a map:" ] }, { @@ -876,18 +807,16 @@ "outputs": [], "source": [ "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM0\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeName\", \"Taiwan\"))\n", - "dem = ee.Image(\"USGS/SRTMGL1_003\").clipToCollection(fc)\n", - "vis_params = {\n", - " \"min\": 0,\n", - " \"max\": 4000,\n", - " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "image = images.first()\n", + "\n", + "vis = {\n", + " \"min\": 0.0,\n", + " \"max\": 3000,\n", + " \"bands\": [\"B4\", \"B3\", \"B2\"],\n", "}\n", "\n", - "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", - "m.add_colorbar(vis_params, label=\"Elevation (m)\", layer_name=\"SRTM DEM\")\n", - "m.center_object(fc, 8)\n", + "m.add_layer(image, vis, \"Sentinel-2\")\n", + "m.centerObject(image, 8)\n", "m" ] }, @@ -896,7 +825,9 @@ "id": "64", "metadata": {}, "source": [ - "Add a vertical color bar." + "#### Visualizing Image Collections\n", + "\n", + "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." ] }, { @@ -906,13 +837,18 @@ "metadata": {}, "outputs": [], "source": [ - "m.add_colorbar(\n", - " vis_params,\n", - " label=\"Elevation (m)\",\n", - " layer_name=\"SRTM DEM\",\n", - " orientation=\"vertical\",\n", - " max_width=\"100px\",\n", - ")" + "m = geemap.Map()\n", + "image = images.median()\n", + "\n", + "vis = {\n", + " \"min\": 0.0,\n", + " \"max\": 3000,\n", + " \"bands\": [\"B8\", \"B4\", \"B3\"],\n", + "}\n", + "\n", + "m.add_layer(image, vis, \"Sentinel-2\")\n", + "m.centerObject(geometry, 8)\n", + "m" ] }, { @@ -920,7 +856,13 @@ "id": "66", "metadata": {}, "source": [ - "Make the color bar background transparent." + "## Earth Engine Vector Data\n", + "\n", + "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.\n", + "\n", + "### Loading Feature Collections\n", + "\n", + "The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) 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](https://developers.google.com/earth-engine/datasets/catalog/TIGER_2016_Roads) by the U.S. Census Bureau:" ] }, { @@ -930,14 +872,11 @@ "metadata": {}, "outputs": [], "source": [ - "m.add_colorbar(\n", - " vis_params,\n", - " label=\"Elevation (m)\",\n", - " layer_name=\"SRTM DEM\",\n", - " orientation=\"vertical\",\n", - " max_width=\"100px\",\n", - " transparent_bg=True,\n", - ")" + "m = geemap.Map()\n", + "fc = ee.FeatureCollection(\"TIGER/2016/Roads\")\n", + "m.set_center(-83.909463, 35.959111, 12)\n", + "m.add_layer(fc, {}, \"Census roads\")\n", + "m" ] }, { @@ -945,11 +884,9 @@ "id": "68", "metadata": {}, "source": [ - "### Split-panel map and linked maps\n", + "### Filtering Feature Collections\n", "\n", - "### Split-panel maps\n", - "\n", - "Create a split map with basemaps. Note that ipyleaflet has a bug with the SplitControl. You can't pan the map, which should be resolved in the next ipyleaflet release." + "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\":" ] }, { @@ -960,8 +897,10 @@ "outputs": [], "source": [ "m = geemap.Map()\n", - "m.split_map(left_layer=\"Esri.WorldTopoMap\", right_layer=\"OpenTopoMap\")\n", - "m.set_center(121.4181, 23.5790, 8)\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "fc = states.filter(ee.Filter.eq(\"NAME\", \"Tennessee\"))\n", + "m.add_layer(fc, {}, \"Tennessee\")\n", + "m.center_object(fc, 7)\n", "m" ] }, @@ -970,7 +909,7 @@ "id": "70", "metadata": {}, "source": [ - "Create a split map with Earth Engine layers." + "To retrieve properties of the first feature in the collection, use:" ] }, { @@ -980,21 +919,8 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "\n", - "esa_2020 = ee.ImageCollection(\"ESA/WorldCover/v100\").first()\n", - "esa_2021 = ee.ImageCollection(\"ESA/WorldCover/v200\").first()\n", - "visualization = {\"bands\": [\"Map\"]}\n", - "\n", - "left_layer = geemap.ee_tile_layer(esa_2020, visualization, \"Land Cover 2020\")\n", - "right_layer = geemap.ee_tile_layer(esa_2021, visualization, \"Land Cover 2021\")\n", - "\n", - "m.split_map(\n", - " left_layer, right_layer, left_label=\"Land Cover 2020\", right_label=\"Land Cover 2021\"\n", - ")\n", - "m.add_legend(title=\"Land Cover Type\", builtin_legend=\"ESA_WorldCover\")\n", - "m.set_center(121.4181, 23.5790, 8)\n", - "m" + "feat = fc.first()\n", + "feat.toDictionary()" ] }, { @@ -1002,9 +928,7 @@ "id": "72", "metadata": {}, "source": [ - "### Linked maps\n", - "\n", - "Create a 2x2 linked map for visualizing Sentinel-2 imagery with different band combinations. Note that this feature does not work properly with Colab. Panning one map would not pan other maps." + "You can also convert a FeatureCollection to a Pandas DataFrame for easier analysis with:" ] }, { @@ -1014,55 +938,22 @@ "metadata": {}, "outputs": [], "source": [ - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM0\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeName\", \"Taiwan\"))\n", - "image = (\n", - " ee.ImageCollection(\"COPERNICUS/S2\")\n", - " .filterDate(\"2023-01-01\", \"2024-01-01\")\n", - " .filter(ee.Filter.lt(\"CLOUDY_PIXEL_PERCENTAGE\", 5))\n", - " .filterBounds(fc)\n", - " .map(lambda img: img.divide(10000))\n", - " .median()\n", - " .clipToCollection(fc)\n", - ")\n", - "\n", - "vis_params = [\n", - " {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", - " {\"bands\": [\"B8\", \"B11\", \"B4\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", - " {\"bands\": [\"B8\", \"B4\", \"B3\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", - " {\"bands\": [\"B12\", \"B12\", \"B4\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", - "]\n", - "\n", - "labels = [\n", - " \"Natural Color (B4/B3/B2)\",\n", - " \"Land/Water (B8/B11/B4)\",\n", - " \"Color Infrared (B8/B4/B3)\",\n", - " \"Vegetation (B12/B11/B4)\",\n", - "]\n", - "\n", - "geemap.linked_maps(\n", - " rows=2,\n", - " cols=2,\n", - " height=\"400px\",\n", - " center=[23.5790, 121.4181],\n", - " zoom=8,\n", - " ee_objects=[image],\n", - " vis_params=vis_params,\n", - " labels=labels,\n", - " label_position=\"topright\",\n", - ")" + "geemap.ee_to_df(fc)" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "74", "metadata": {}, + "outputs": [], "source": [ - "### Timeseries inspector and time slider\n", - "\n", - "#### Timeseries inspector\n", - "\n", - "Check the available years of NLCD." + "m = geemap.Map()\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "fc = states.filter(ee.Filter.inList(\"NAME\", [\"California\", \"Oregon\", \"Washington\"]))\n", + "m.add_layer(fc, {}, \"West Coast\")\n", + "m.center_object(fc, 5)\n", + "m" ] }, { @@ -1072,11 +963,13 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map(center=[40, -100], zoom=4)\n", - "collection = ee.ImageCollection(\"USGS/NLCD_RELEASES/2019_REL/NLCD\").select(\"landcover\")\n", - "vis_params = {\"bands\": [\"landcover\"]}\n", - "years = collection.aggregate_array(\"system:index\").getInfo()\n", - "years" + "region = m.user_roi\n", + "if region is None:\n", + " region = ee.Geometry.BBox(-88.40, 29.88, -77.90, 35.39)\n", + "\n", + "fc = ee.FeatureCollection(\"TIGER/2018/States\").filterBounds(region)\n", + "m.add_layer(fc, {}, \"Southeastern U.S.\")\n", + "m.center_object(fc, 6)" ] }, { @@ -1084,7 +977,9 @@ "id": "76", "metadata": {}, "source": [ - "Create a timeseries inspector for NLCD. Note that ipyleaflet has a bug with the SplitControl. You can't pan the map, which should be resolved in a future ipyleaflet release." + "### Visualizing Feature Collections\n", + "\n", + "Once loaded, feature collections can be visualized on an interactive map. For example, visualizing U.S. state boundaries from the census data:" ] }, { @@ -1094,15 +989,9 @@ "metadata": {}, "outputs": [], "source": [ - "m.ts_inspector(\n", - " left_ts=collection,\n", - " right_ts=collection,\n", - " left_names=years,\n", - " right_names=years,\n", - " left_vis=vis_params,\n", - " right_vis=vis_params,\n", - " width=\"80px\",\n", - ")\n", + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "m.add_layer(states, {}, \"US States\")\n", "m" ] }, @@ -1111,11 +1000,7 @@ "id": "78", "metadata": {}, "source": [ - "#### Time slider\n", - "\n", - "Note that this feature may not work properly with in the Colab environment. Restart Colab runtime if the time slider does not work.\n", - "\n", - "Create a map for visualizing MODIS vegetation data." + "Feature collections can also be styled with additional parameters. To apply a custom style, specify options like color and line width:" ] }, { @@ -1125,20 +1010,10 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "\n", - "collection = (\n", - " ee.ImageCollection(\"MODIS/MCD43A4_006_NDVI\")\n", - " .filter(ee.Filter.date(\"2018-06-01\", \"2018-07-01\"))\n", - " .select(\"NDVI\")\n", - ")\n", - "vis_params = {\n", - " \"min\": 0.0,\n", - " \"max\": 1.0,\n", - " \"palette\": \"ndvi\",\n", - "}\n", - "\n", - "m.add_time_slider(collection, vis_params, time_interval=2)\n", + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "style = {\"color\": \"0000ffff\", \"width\": 2, \"lineType\": \"solid\", \"fillColor\": \"FF000080\"}\n", + "m.add_layer(states.style(**style), {}, \"US States\")\n", "m" ] }, @@ -1147,7 +1022,7 @@ "id": "80", "metadata": {}, "source": [ - "Create a map for visualizing weather data." + "Using `add_styled_vector`, you can apply a color palette to style different features by attribute:" ] }, { @@ -1157,23 +1032,21 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "\n", - "collection = (\n", - " ee.ImageCollection(\"NOAA/GFS0P25\")\n", - " .filterDate(\"2018-12-22\", \"2018-12-23\")\n", - " .limit(24)\n", - " .select(\"temperature_2m_above_ground\")\n", - ")\n", - "\n", + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", "vis_params = {\n", - " \"min\": -40.0,\n", - " \"max\": 35.0,\n", - " \"palette\": [\"blue\", \"purple\", \"cyan\", \"green\", \"yellow\", \"red\"],\n", + " \"color\": \"000000\",\n", + " \"colorOpacity\": 1,\n", + " \"pointSize\": 3,\n", + " \"pointShape\": \"circle\",\n", + " \"width\": 2,\n", + " \"lineType\": \"solid\",\n", + " \"fillColorOpacity\": 0.66,\n", "}\n", - "\n", - "labels = [str(n).zfill(2) + \":00\" for n in range(0, 24)]\n", - "m.add_time_slider(collection, vis_params, labels=labels, time_interval=1, opacity=0.8)\n", + "palette = [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"]\n", + "m.add_styled_vector(\n", + " states, column=\"NAME\", palette=palette, layer_name=\"Styled vector\", **vis_params\n", + ")\n", "m" ] }, @@ -1182,7 +1055,11 @@ "id": "82", "metadata": {}, "source": [ - "Visualizing Sentinel-2 imagery" + "## More Tools for Visualizing Earth Engine Data\n", + "\n", + "### Using the Plotting Tool\n", + "\n", + "The plotting tool in geemap enables visualization of Earth Engine data layers. In this example, Landsat 7 and Hyperion data are added with specific visualization parameters, allowing for comparison of these datasets. The plot GUI is then added to facilitate detailed exploration of the data." ] }, { @@ -1192,17 +1069,26 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map(center=[37.75, -122.45], zoom=12)\n", + "m = geemap.Map(center=[40, -100], zoom=4)\n", "\n", - "collection = (\n", - " ee.ImageCollection(\"COPERNICUS/S2_SR\")\n", - " .filterBounds(ee.Geometry.Point([-122.45, 37.75]))\n", - " .filterMetadata(\"CLOUDY_PIXEL_PERCENTAGE\", \"less_than\", 10)\n", + "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\").select(\n", + " [\"B1\", \"B2\", \"B3\", \"B4\", \"B5\", \"B7\"]\n", ")\n", "\n", - "vis_params = {\"min\": 0, \"max\": 4000, \"bands\": [\"B8\", \"B4\", \"B3\"]}\n", + "landsat_vis = {\"bands\": [\"B4\", \"B3\", \"B2\"], \"gamma\": 1.4}\n", + "m.add_layer(landsat7, landsat_vis, \"Landsat\")\n", "\n", - "m.add_time_slider(collection, vis_params)\n", + "hyperion = ee.ImageCollection(\"EO1/HYPERION\").filter(\n", + " ee.Filter.date(\"2016-01-01\", \"2017-03-01\")\n", + ")\n", + "\n", + "hyperion_vis = {\n", + " \"min\": 1000.0,\n", + " \"max\": 14000.0,\n", + " \"gamma\": 2.5,\n", + "}\n", + "m.add_layer(hyperion, hyperion_vis, \"Hyperion\")\n", + "m.add_plot_gui()\n", "m" ] }, @@ -1211,49 +1097,25 @@ "id": "84", "metadata": {}, "source": [ - "### Exercise 2 - Visualizing satellite data for an area of interest\n", - "\n", - "Visualize the time series of [Sentinel-2](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR_HARMONIZED) imagery for an area of interest in Taiwan. You can use the following Earth Engine assets:\n", - "\n", - "- `ee.ImageCollection(\"COPERNICUS/S2_SR_HARMONIZED\")`" + "Set the plotting options for Landsat to add marker clusters and overlays, enhancing the interactivity of plotted data on the map." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "85", "metadata": {}, + "outputs": [], "source": [ - "## Analyzing Earth Engine data\n", - "\n", - "### Zonal statistics" + "m.set_plot_options(add_marker_cluster=True, overlay=True)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "86", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM1\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeGroup\", \"TWN\"))\n", - "# Add NASA SRTM\n", - "dem = ee.Image(\"USGS/SRTMGL1_003\").clipToCollection(fc)\n", - "dem_vis = {\n", - " \"min\": 0,\n", - " \"max\": 4000,\n", - " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", - "}\n", - "m.add_layer(dem, dem_vis, \"SRTM DEM\")\n", - "\n", - "# Add 5-year Landsat TOA composite\n", - "landsat = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\").clipToCollection(fc)\n", - "landsat_vis = {\"bands\": [\"B4\", \"B3\", \"B2\"], \"gamma\": 1.4, \"min\": 20, \"max\": 150}\n", - "m.add_layer(landsat, landsat_vis, \"Landsat\", False)\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.center_object(fc, 8)\n", - "m" + "Adjust the plotting options for Hyperion data by setting a bar plot type with marker clusters, suitable for visualizing Hyperion’s data values in bar format." ] }, { @@ -1263,18 +1125,19 @@ "metadata": {}, "outputs": [], "source": [ - "out_dem_stats = \"dem_stats.csv\"\n", - "geemap.zonal_stats(dem, fc, out_dem_stats, stat_type=\"MEAN\", scale=30, return_fc=False)" + "m.set_plot_options(add_marker_cluster=True, plot_type=\"bar\")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "88", "metadata": {}, - "outputs": [], "source": [ - "geemap.csv_to_df(out_dem_stats).sort_values(by=[\"mean\"])" + "### Legends\n", + "\n", + "#### Built-in Legends\n", + "\n", + "Geemap provides built-in legends that can be easily added to maps for better interpretability of data classes. Here, an NLCD legend is added to both a WMS layer and an Earth Engine land cover layer, giving quick visual reference to the data's classification scheme." ] }, { @@ -1284,53 +1147,34 @@ "metadata": {}, "outputs": [], "source": [ - "out_landsat_stats = \"landsat_stats.csv\"\n", - "geemap.zonal_stats(\n", - " landsat,\n", - " fc,\n", - " out_landsat_stats,\n", - " stat_type=\"MEAN\",\n", - " scale=30,\n", - " return_fc=False,\n", - ")" + "from geemap.legends import builtin_legends" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "90", "metadata": {}, - "outputs": [], "source": [ - "geemap.csv_to_df(out_landsat_stats)" + "Print out all available built-in legends, which can be used with various data layers to enhance map readability." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "91", "metadata": {}, + "outputs": [], "source": [ - "### Zonal statistics by group" + "for legend in builtin_legends:\n", + " print(legend)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "92", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "m.add_basemap(\"Esri.WorldImagery\")\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM1\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeGroup\", \"TWN\"))\n", - "dataset = ee.ImageCollection(\"ESA/WorldCover/v200\").first().clipToCollection(fc)\n", - "visualization = {\"bands\": [\"Map\"]}\n", - "m.add_layer(dataset, visualization, \"Landcover\")\n", - "m.add_legend(title=\"Land Cover Type\", builtin_legend=\"ESA_WorldCover\")\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.set_center(121.4181, 23.5790, 8)\n", - "m" + "Add an NLCD WMS layer along with its corresponding legend, which appears as an overlay, providing users with an informative legend display." ] }, { @@ -1340,26 +1184,19 @@ "metadata": {}, "outputs": [], "source": [ - "landcover_stats = \"landcover_stats.csv\"\n", - "\n", - "geemap.zonal_stats_by_group(\n", - " dataset,\n", - " fc,\n", - " landcover_stats,\n", - " stat_type=\"SUM\",\n", - " denominator=1e6,\n", - " decimal_places=2,\n", - ")" + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "m.add_basemap(\"Esri.WorldImagery\")\n", + "m.add_basemap(\"NLCD 2021 CONUS Land Cover\")\n", + "m.add_legend(builtin_legend=\"NLCD\", max_width=\"100px\", height=\"455px\")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "94", "metadata": {}, - "outputs": [], "source": [ - "geemap.csv_to_df(landcover_stats)" + "Add an Earth Engine layer for NLCD land cover and display its legend, specifying title, legend type, and dimensions for user convenience." ] }, { @@ -1369,62 +1206,54 @@ "metadata": {}, "outputs": [], "source": [ - "landcover_stats = \"landcover_stats_pct.csv\"\n", + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "m.add_basemap(\"Esri.WorldImagery\")\n", "\n", - "geemap.zonal_stats_by_group(\n", - " dataset,\n", - " fc,\n", - " landcover_stats,\n", - " stat_type=\"PERCENTAGE\",\n", - " denominator=1e6,\n", - " decimal_places=2,\n", - ")" + "nlcd = ee.Image(\"USGS/NLCD_RELEASES/2021_REL/NLCD/2021\")\n", + "landcover = nlcd.select(\"landcover\")\n", + "\n", + "m.add_layer(landcover, {}, \"NLCD Land Cover 2021\")\n", + "m.add_legend(\n", + " title=\"NLCD Land Cover Classification\", builtin_legend=\"NLCD\", height=\"455px\"\n", + ")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "96", "metadata": {}, - "outputs": [], "source": [ - "geemap.csv_to_df(landcover_stats)" + "#### Custom Legends\n", + "\n", + "Create a custom legend by defining unique labels and colors for each class, allowing for flexible map customization. This example uses a color palette for labeling several map categories." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "97", "metadata": {}, + "outputs": [], "source": [ - "## Exporting Earth Engine data\n", + "m = geemap.Map(add_google_map=False)\n", + "\n", + "keys = [\"One\", \"Two\", \"Three\", \"Four\", \"etc\"]\n", "\n", - "### Exporting images" + "# colors can be defined using either hex code or RGB (0-255, 0-255, 0-255)\n", + "colors = [\"#8DD3C7\", \"#FFFFB3\", \"#BEBADA\", \"#FB8072\", \"#80B1D3\"]\n", + "# legend_colors = [(255, 0, 0), (127, 255, 0), (127, 18, 25), (36, 70, 180), (96, 68 123)]\n", + "\n", + "m.add_legend(keys=keys, colors=colors, position=\"bottomright\")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "98", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "roi = ee.Geometry.Point([121.615219, 25.041219])\n", - "image = (\n", - " ee.ImageCollection(\"COPERNICUS/S2_SR_HARMONIZED\")\n", - " .filterDate(\"2024-01-01\", \"2024-08-01\")\n", - " .filter(ee.Filter.lt(\"CLOUDY_PIXEL_PERCENTAGE\", 10))\n", - " .filterBounds(roi)\n", - " .sort(\"CLOUDY_PIXEL_PERCENTAGE\")\n", - " .first()\n", - " .select([\"B8\", \"B4\", \"B3\"])\n", - ")\n", - "\n", - "vis_params = {\"min\": 0, \"max\": 3000}\n", - "\n", - "m.add_layer(image, vis_params, \"Sentinel-2\")\n", - "m.center_object(roi, 8)\n", - "m" + "Define a custom legend using a dictionary that links specific colors to labels. This approach provides flexibility to represent specific categories in the data, such as various land cover types." ] }, { @@ -1434,20 +1263,48 @@ "metadata": {}, "outputs": [], "source": [ - "region = ee.Geometry.BBox(121.3824, 24.9325, 121.6653, 25.1496)\n", - "fc = ee.FeatureCollection(region)\n", - "style = {\"color\": \"ffff00ff\", \"fillColor\": \"00000000\"}\n", - "m.add_layer(fc.style(**style), {}, \"ROI\")" + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "m.add_basemap(\"Esri.WorldImagery\")\n", + "\n", + "legend_dict = {\n", + " \"11 Open Water\": \"466b9f\",\n", + " \"12 Perennial Ice/Snow\": \"d1def8\",\n", + " \"21 Developed, Open Space\": \"dec5c5\",\n", + " \"22 Developed, Low Intensity\": \"d99282\",\n", + " \"23 Developed, Medium Intensity\": \"eb0000\",\n", + " \"24 Developed High Intensity\": \"ab0000\",\n", + " \"31 Barren Land (Rock/Sand/Clay)\": \"b3ac9f\",\n", + " \"41 Deciduous Forest\": \"68ab5f\",\n", + " \"42 Evergreen Forest\": \"1c5f2c\",\n", + " \"43 Mixed Forest\": \"b5c58f\",\n", + " \"51 Dwarf Scrub\": \"af963c\",\n", + " \"52 Shrub/Scrub\": \"ccb879\",\n", + " \"71 Grassland/Herbaceous\": \"dfdfc2\",\n", + " \"72 Sedge/Herbaceous\": \"d1d182\",\n", + " \"73 Lichens\": \"a3cc51\",\n", + " \"74 Moss\": \"82ba9e\",\n", + " \"81 Pasture/Hay\": \"dcd939\",\n", + " \"82 Cultivated Crops\": \"ab6c28\",\n", + " \"90 Woody Wetlands\": \"b8d9eb\",\n", + " \"95 Emergent Herbaceous Wetlands\": \"6c9fb8\",\n", + "}\n", + "\n", + "nlcd = ee.Image(\"USGS/NLCD_RELEASES/2021_REL/NLCD/2021\")\n", + "landcover = nlcd.select(\"landcover\")\n", + "\n", + "m.add_layer(landcover, {}, \"NLCD Land Cover 2021\")\n", + "m.add_legend(title=\"NLCD Land Cover Classification\", legend_dict=legend_dict)\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "100", "metadata": {}, - "outputs": [], "source": [ - "geemap.ee_export_image(image, filename=\"sentinel-2.tif\", scale=30, region=region)" + "### Color Bars\n", + "\n", + "Add a horizontal color bar representing elevation data. This example demonstrates how to add an SRTM elevation layer and overlay a color bar for quick visual reference of elevation values." ] }, { @@ -1457,43 +1314,50 @@ "metadata": {}, "outputs": [], "source": [ - "geemap.ee_export_image_to_drive(\n", - " image, description=\"sentinel-2\", folder=\"export\", region=region, scale=30\n", - ")" + "m = geemap.Map()\n", + "\n", + "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", + "vis_params = {\n", + " \"min\": 0,\n", + " \"max\": 4000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "}\n", + "\n", + "m.add_layer(dem, vis_params, \"SRTM DEM\")\n", + "m.add_colorbar(vis_params, label=\"Elevation (m)\", layer_name=\"SRTM DEM\")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "102", "metadata": {}, - "outputs": [], "source": [ - "geemap.download_ee_image(image, \"sentinel-2_10m.tif\", region=region, scale=10)" + "Add a vertical color bar for elevation data, adjusting its orientation and dimensions to fit the map layout." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "103", "metadata": {}, + "outputs": [], "source": [ - "### Export image collections" + "m.add_colorbar(\n", + " vis_params,\n", + " label=\"Elevation (m)\",\n", + " layer_name=\"SRTM DEM\",\n", + " orientation=\"vertical\",\n", + " max_width=\"100px\",\n", + ")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "104", "metadata": {}, - "outputs": [], "source": [ - "point = ee.Geometry.Point(-99.2222, 46.7816)\n", - "collection = (\n", - " ee.ImageCollection(\"USDA/NAIP/DOQQ\")\n", - " .filterBounds(point)\n", - " .filterDate(\"2008-01-01\", \"2018-01-01\")\n", - " .filter(ee.Filter.listContains(\"system:band_names\", \"N\"))\n", - ")" + "Make the color bar’s background transparent, which improves visual integration with the map when adding color bars for data layers." ] }, { @@ -1503,17 +1367,24 @@ "metadata": {}, "outputs": [], "source": [ - "collection.aggregate_array(\"system:index\")" + "m.add_colorbar(\n", + " vis_params,\n", + " label=\"Elevation (m)\",\n", + " layer_name=\"SRTM DEM\",\n", + " orientation=\"vertical\",\n", + " max_width=\"100px\",\n", + " transparent_bg=True,\n", + ")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "106", "metadata": {}, - "outputs": [], "source": [ - "geemap.ee_export_image_collection(collection, out_dir=\"naip\", scale=10)" + "### Split-panel Maps\n", + "\n", + "Create a split-panel map with basemaps, allowing users to compare two different basemaps side by side." ] }, { @@ -1523,7 +1394,9 @@ "metadata": {}, "outputs": [], "source": [ - "geemap.ee_export_image_collection_to_drive(collection, folder=\"export\", scale=10)" + "m = geemap.Map()\n", + "m.split_map(left_layer=\"Esri.WorldTopoMap\", right_layer=\"OpenTopoMap\")\n", + "m" ] }, { @@ -1531,7 +1404,7 @@ "id": "108", "metadata": {}, "source": [ - "### Exporting feature collections" + "Use Earth Engine layers in a split-panel map to compare NLCD data from 2001 and 2021. This layout is effective for examining changes between datasets across time." ] }, { @@ -1541,22 +1414,26 @@ "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "countries = ee.FeatureCollection(\"WM/geoLab/geoBoundaries/600/ADM0\")\n", - "fc = countries.filter(ee.Filter.eq(\"shapeName\", \"Taiwan\"))\n", - "m.add_layer(fc, {}, \"Taiwan\")\n", - "m.center_object(fc, 8)\n", + "m = geemap.Map(center=(40, -100), zoom=4, height=600)\n", + "\n", + "nlcd_2001 = ee.Image(\"USGS/NLCD_RELEASES/2019_REL/NLCD/2001\").select(\"landcover\")\n", + "nlcd_2021 = ee.Image(\"USGS/NLCD_RELEASES/2021_REL/NLCD/2021\").select(\"landcover\")\n", + "\n", + "left_layer = geemap.ee_tile_layer(nlcd_2001, {}, \"NLCD 2001\")\n", + "right_layer = geemap.ee_tile_layer(nlcd_2021, {}, \"NLCD 2021\")\n", + "\n", + "m.split_map(left_layer, right_layer)\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "110", "metadata": {}, - "outputs": [], "source": [ - "geemap.ee_to_shp(fc, filename=\"Taiwan.shp\")" + "### Linked Maps\n", + "\n", + "Set up a 2x2 grid of linked maps showing Sentinel-2 imagery in different band combinations, ideal for comparing multiple visual perspectives. Note that this feature may not work in Colab." ] }, { @@ -1566,17 +1443,49 @@ "metadata": {}, "outputs": [], "source": [ - "geemap.ee_export_vector(fc, filename=\"Taiwan.shp\")" + "image = (\n", + " ee.ImageCollection(\"COPERNICUS/S2_SR_HARMONIZED\")\n", + " .filterDate(\"2024-07-01\", \"2024-10-01\")\n", + " .filter(ee.Filter.lt(\"CLOUDY_PIXEL_PERCENTAGE\", 5))\n", + " .map(lambda img: img.divide(10000))\n", + " .median()\n", + ")\n", + "\n", + "vis_params = [\n", + " {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", + " {\"bands\": [\"B8\", \"B11\", \"B4\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", + " {\"bands\": [\"B8\", \"B4\", \"B3\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", + " {\"bands\": [\"B12\", \"B12\", \"B4\"], \"min\": 0, \"max\": 0.3, \"gamma\": 1.3},\n", + "]\n", + "\n", + "labels = [\n", + " \"Natural Color (B4/B3/B2)\",\n", + " \"Land/Water (B8/B11/B4)\",\n", + " \"Color Infrared (B8/B4/B3)\",\n", + " \"Vegetation (B12/B11/B4)\",\n", + "]\n", + "\n", + "geemap.linked_maps(\n", + " rows=2,\n", + " cols=2,\n", + " height=\"300px\",\n", + " center=[35.959111, -83.909463],\n", + " zoom=12,\n", + " ee_objects=[image],\n", + " vis_params=vis_params,\n", + " labels=labels,\n", + " label_position=\"topright\",\n", + ")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "112", "metadata": {}, - "outputs": [], "source": [ - "geemap.ee_to_geojson(fc, filename=\"Taiwan.geojson\")" + "### Timeseries Inspector\n", + "\n", + "Retrieve the available years in the NLCD collection for setting up a timeseries. This step helps confirm available time points for inspecting changes over time." ] }, { @@ -1586,18 +1495,19 @@ "metadata": {}, "outputs": [], "source": [ - "geemap.ee_to_csv(fc, filename=\"Taiwan.csv\")" + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "collection = ee.ImageCollection(\"USGS/NLCD_RELEASES/2019_REL/NLCD\").select(\"landcover\")\n", + "vis_params = {\"bands\": [\"landcover\"]}\n", + "years = collection.aggregate_array(\"system:index\").getInfo()\n", + "years" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "114", "metadata": {}, - "outputs": [], "source": [ - "gdf = geemap.ee_to_gdf(fc)\n", - "gdf" + "Use a timeseries inspector to compare changes in NLCD data across different years. This tool is ideal for temporal data visualization, showing how land cover changes in an interactive format." ] }, { @@ -1607,42 +1517,58 @@ "metadata": {}, "outputs": [], "source": [ - "df = geemap.ee_to_df(fc)\n", - "df" + "m.ts_inspector(\n", + " left_ts=collection,\n", + " right_ts=collection,\n", + " left_names=years,\n", + " right_names=years,\n", + " left_vis=vis_params,\n", + " right_vis=vis_params,\n", + " width=\"80px\",\n", + ")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "116", "metadata": {}, - "outputs": [], "source": [ - "geemap.ee_export_vector_to_drive(\n", - " fc, description=\"Alaska\", fileFormat=\"SHP\", folder=\"export\"\n", - ")" + "### Time Slider\n", + "\n", + "Create a time slider to explore MODIS vegetation data, setting the slider to visualize NDVI values over a specific period. This feature allows users to track vegetation changes month-by-month." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "117", "metadata": {}, + "outputs": [], "source": [ - "## Creating timelapse animations\n", + "m = geemap.Map()\n", + "\n", + "collection = (\n", + " ee.ImageCollection(\"MODIS/MCD43A4_006_NDVI\")\n", + " .filter(ee.Filter.date(\"2018-06-01\", \"2018-07-01\"))\n", + " .select(\"NDVI\")\n", + ")\n", + "vis_params = {\n", + " \"min\": 0.0,\n", + " \"max\": 1.0,\n", + " \"palette\": \"ndvi\",\n", + "}\n", "\n", - "### Landsat timelapse" + "m.add_time_slider(collection, vis_params, time_interval=2)\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "118", "metadata": {}, - "outputs": [], "source": [ - "m = geemap.Map()\n", - "m.set_center(121.615219, 25.041219, 12)\n", - "m" + "Add a time slider for visualizing NOAA weather data over a 24-hour period, using color-coding to show temperature variations. The slider enables temporal exploration of hourly data." ] }, { @@ -1652,360 +1578,3619 @@ "metadata": {}, "outputs": [], "source": [ - "roi = m.user_roi\n", - "if roi is None:\n", - " roi = ee.Geometry.BBox(121.3824, 24.9325, 121.6653, 25.1496)\n", - " m.add_layer(roi)\n", - " m.center_object(roi)" + "m = geemap.Map()\n", + "\n", + "collection = (\n", + " ee.ImageCollection(\"NOAA/GFS0P25\")\n", + " .filterDate(\"2018-12-22\", \"2018-12-23\")\n", + " .limit(24)\n", + " .select(\"temperature_2m_above_ground\")\n", + ")\n", + "\n", + "vis_params = {\n", + " \"min\": -40.0,\n", + " \"max\": 35.0,\n", + " \"palette\": [\"blue\", \"purple\", \"cyan\", \"green\", \"yellow\", \"red\"],\n", + "}\n", + "\n", + "labels = [str(n).zfill(2) + \":00\" for n in range(0, 24)]\n", + "m.add_time_slider(collection, vis_params, labels=labels, time_interval=1, opacity=0.8)\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "120", "metadata": {}, + "source": [ + "Add a time slider to visualize Sentinel-2 imagery with specific bands and cloud cover filtering. This feature enables temporal analysis of imagery data, allowing users to explore seasonal and other changes over time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "121", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "\n", + "collection = (\n", + " ee.ImageCollection(\"COPERNICUS/S2_SR_HARMONIZED\")\n", + " .filterBounds(ee.Geometry.Point([-83.909463, 35.959111]))\n", + " .filterMetadata(\"CLOUDY_PIXEL_PERCENTAGE\", \"less_than\", 10)\n", + " .filter(ee.Filter.calendarRange(6, 8, \"month\"))\n", + ")\n", + "\n", + "vis_params = {\"min\": 0, \"max\": 4000, \"bands\": [\"B8\", \"B4\", \"B3\"]}\n", + "\n", + "m.add_time_slider(collection, vis_params)\n", + "m.set_center(-83.909463, 35.959111, 12)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "122", + "metadata": {}, + "source": [ + "## Processing of Vector Data\n", + "\n", + "### From GeoJSON\n", + "\n", + "Load GeoJSON data into an Earth Engine FeatureCollection. This example retrieves countries data from a remote URL, converting it to a FeatureCollection and visualizing it with a specified style." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "123", + "metadata": {}, + "outputs": [], + "source": [ + "in_geojson = \"https://github.com/gee-community/geemap/blob/master/examples/data/countries.geojson\"\n", + "m = geemap.Map()\n", + "fc = geemap.geojson_to_ee(in_geojson)\n", + "m.add_layer(fc.style(**{\"color\": \"ff0000\", \"fillColor\": \"00000000\"}), {}, \"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "124", + "metadata": {}, + "source": [ + "### From Shapefile\n", + "\n", + "Download and load a shapefile of country boundaries. The shapefile is converted to a FeatureCollection for visualization on the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "125", + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://github.com/gee-community/geemap/blob/master/examples/data/countries.zip\"\n", + "geemap.download_file(url, overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "126", + "metadata": {}, + "outputs": [], + "source": [ + "in_shp = \"countries.shp\"\n", + "fc = geemap.shp_to_ee(in_shp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "127", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m.add_layer(fc, {}, \"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "128", + "metadata": {}, + "source": [ + "### From GeoDataFrame\n", + "\n", + "Read a shapefile into a GeoDataFrame using geopandas, then convert the GeoDataFrame to an Earth Engine FeatureCollection for mapping." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "129", + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas as gpd\n", + "\n", + "gdf = gpd.read_file(in_shp)\n", + "fc = geemap.gdf_to_ee(gdf)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "130", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m.add_layer(fc, {}, \"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "131", + "metadata": {}, + "source": [ + "### To GeoJSON\n", + "\n", + "Filter U.S. state data to select Tennessee and save it as a GeoJSON file, which can be shared or used in other GIS tools." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "132", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "fc = states.filter(ee.Filter.eq(\"NAME\", \"Tennessee\"))\n", + "m.add_layer(fc, {}, \"Tennessee\")\n", + "m.center_object(fc, 7)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "133", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_geojson(fc, filename=\"Tennessee.geojson\")" + ] + }, + { + "cell_type": "markdown", + "id": "134", + "metadata": {}, + "source": [ + "### To Shapefile\n", + "\n", + "Export the filtered Tennessee FeatureCollection to a shapefile format for offline use or compatibility with desktop GIS software." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "135", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_shp(fc, filename=\"Tennessee.shp\")" + ] + }, + { + "cell_type": "markdown", + "id": "136", + "metadata": {}, + "source": [ + "### To GeoDataFrame\n", + "\n", + "Convert an Earth Engine FeatureCollection to a GeoDataFrame for further analysis in Python or use in interactive maps." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "137", + "metadata": {}, + "outputs": [], + "source": [ + "gdf = geemap.ee_to_gdf(fc)\n", + "gdf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "138", + "metadata": {}, + "outputs": [], + "source": [ + "gdf.explore()" + ] + }, + { + "cell_type": "markdown", + "id": "139", + "metadata": {}, + "source": [ + "### To DataFrame\n", + "\n", + "Transform an Earth Engine FeatureCollection into a pandas DataFrame, which can then be used for data analysis in Python." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "140", + "metadata": {}, + "outputs": [], + "source": [ + "df = geemap.ee_to_df(fc)\n", + "df" + ] + }, + { + "cell_type": "markdown", + "id": "141", + "metadata": {}, + "source": [ + "### To CSV\n", + "\n", + "Export the FeatureCollection data to a CSV file, useful for spreadsheet applications and data reporting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "142", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_csv(fc, filename=\"Indiana.csv\")" + ] + }, + { + "cell_type": "markdown", + "id": "143", + "metadata": {}, + "source": [ + "## Processing of Raster Data\n", + "\n", + "### Extract Pixel Values\n", + "\n", + "#### Extracting Values to Points\n", + "\n", + "Load and visualize SRTM DEM and Landsat 7 imagery. Points from a shapefile of U.S. cities are added, and the tool extracts DEM values to these points, saving the results in a shapefile." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "144", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "\n", + "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", + "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\")\n", + "\n", + "vis_params = {\n", + " \"min\": 0,\n", + " \"max\": 4000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "}\n", + "\n", + "m.add_layer(\n", + " landsat7,\n", + " {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 20, \"max\": 200, \"gamma\": 2},\n", + " \"Landsat 7\",\n", + ")\n", + "m.add_layer(dem, vis_params, \"SRTM DEM\", True, 1)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "145", + "metadata": {}, + "outputs": [], + "source": [ + "in_shp = \"us_cities.shp\"\n", + "url = \"https://github.com/giswqs/data/raw/main/us/us_cities.zip\"\n", + "geemap.download_file(url)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "146", + "metadata": {}, + "outputs": [], + "source": [ + "in_fc = geemap.shp_to_ee(in_shp)\n", + "m.add_layer(in_fc, {}, \"Cities\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "147", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.extract_values_to_points(in_fc, dem, out_fc=\"dem.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "148", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.shp_to_gdf(\"dem.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "149", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.extract_values_to_points(in_fc, landsat7, \"landsat.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "150", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.csv_to_df(\"landsat.csv\")" + ] + }, + { + "cell_type": "markdown", + "id": "151", + "metadata": {}, + "source": [ + "#### Extracting Pixel Values Along a Transect\n", + "\n", + "Visualize SRTM DEM with a terrain basemap. Define a line transect, either interactively or manually, and extract elevation values along this line. Display the elevation profile in a line chart, then export it as a CSV file for further analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "152", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "m.add_basemap(\"TERRAIN\")\n", + "\n", + "image = ee.Image(\"USGS/SRTMGL1_003\")\n", + "vis_params = {\n", + " \"min\": 0,\n", + " \"max\": 4000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "}\n", + "m.add_layer(image, vis_params, \"SRTM DEM\", True, 0.5)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "153", + "metadata": {}, + "outputs": [], + "source": [ + "line = m.user_roi\n", + "if line is None:\n", + " line = ee.Geometry.LineString(\n", + " [[-120.2232, 36.3148], [-118.9269, 36.7121], [-117.2022, 36.7562]]\n", + " )\n", + " m.add_layer(line, {}, \"ROI\")\n", + "m.centerObject(line)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "154", + "metadata": {}, + "outputs": [], + "source": [ + "reducer = \"mean\"\n", + "transect = geemap.extract_transect(\n", + " image, line, n_segments=100, reducer=reducer, to_pandas=True\n", + ")\n", + "transect" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "155", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.line_chart(\n", + " data=transect,\n", + " x=\"distance\",\n", + " y=\"mean\",\n", + " markers=True,\n", + " x_label=\"Distance (m)\",\n", + " y_label=\"Elevation (m)\",\n", + " height=400,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156", + "metadata": {}, + "outputs": [], + "source": [ + "transect.to_csv(\"transect.csv\")" + ] + }, + { + "cell_type": "markdown", + "id": "157", + "metadata": {}, + "source": [ + "### Zonal Statistics\n", + "\n", + "#### Zonal Statistics with an Image and a Feature Collection\n", + "\n", + "This section demonstrates the use of zonal statistics to calculate the mean elevation values within U.S. state boundaries using NASA’s SRTM DEM data and a 5-year Landsat composite. The `geemap.zonal_stats` function exports results to CSV files for analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "158", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "\n", + "# Add NASA SRTM\n", + "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", + "dem_vis = {\n", + " \"min\": 0,\n", + " \"max\": 4000,\n", + " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", + "}\n", + "m.add_layer(dem, dem_vis, \"SRTM DEM\")\n", + "\n", + "# Add 5-year Landsat TOA composite\n", + "landsat = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\")\n", + "landsat_vis = {\"bands\": [\"B4\", \"B3\", \"B2\"], \"gamma\": 1.4}\n", + "m.add_layer(landsat, landsat_vis, \"Landsat\", False)\n", + "\n", + "# Add US Census States\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "style = {\"fillColor\": \"00000000\"}\n", + "m.add_layer(states.style(**style), {}, \"US States\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "159", + "metadata": {}, + "outputs": [], + "source": [ + "out_dem_stats = \"dem_stats.csv\"\n", + "geemap.zonal_stats(\n", + " dem, states, out_dem_stats, statistics_type=\"MEAN\", scale=1000, return_fc=False\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "160", + "metadata": {}, + "outputs": [], + "source": [ + "out_landsat_stats = \"landsat_stats.csv\"\n", + "geemap.zonal_stats(\n", + " landsat,\n", + " states,\n", + " out_landsat_stats,\n", + " statistics_type=\"MEAN\",\n", + " scale=1000,\n", + " return_fc=False,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "161", + "metadata": {}, + "source": [ + "#### Zonal Statistics by Group\n", + "\n", + "Here, zonal statistics are applied to NLCD land cover data, calculating the area of each land cover type within each U.S. state. The results are saved to CSV files as both raw totals and percentages. This provides insights into the spatial distribution of land cover categories across states." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "162", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "\n", + "# Add NLCD data\n", + "dataset = ee.Image(\"USGS/NLCD_RELEASES/2019_REL/NLCD/2019\")\n", + "landcover = dataset.select(\"landcover\")\n", + "m.add_layer(landcover, {}, \"NLCD 2019\")\n", + "\n", + "# Add US census states\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "style = {\"fillColor\": \"00000000\"}\n", + "m.add_layer(states.style(**style), {}, \"US States\")\n", + "\n", + "# Add NLCD legend\n", + "m.add_legend(title=\"NLCD Land Cover\", builtin_legend=\"NLCD\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "163", + "metadata": {}, + "outputs": [], + "source": [ + "nlcd_stats = \"nlcd_stats.csv\"\n", + "\n", + "geemap.zonal_stats_by_group(\n", + " landcover,\n", + " states,\n", + " nlcd_stats,\n", + " statistics_type=\"SUM\",\n", + " denominator=1e6,\n", + " decimal_places=2,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "164", + "metadata": {}, + "outputs": [], + "source": [ + "nlcd_stats = \"nlcd_stats_pct.csv\"\n", + "\n", + "geemap.zonal_stats_by_group(\n", + " landcover,\n", + " states,\n", + " nlcd_stats,\n", + " statistics_type=\"PERCENTAGE\",\n", + " denominator=1e6,\n", + " decimal_places=2,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "165", + "metadata": {}, + "source": [ + "#### Zonal Statistics with Two Images\n", + "\n", + "This example calculates the mean elevation values within different NLCD land cover types using DEM and NLCD data. The `geemap.image_stats_by_zone` function provides summary statistics (e.g., mean and standard deviation), which can be exported to CSV files for further analysis or visualization." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "166", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "dem = ee.Image(\"USGS/3DEP/10m\")\n", + "vis = {\"min\": 0, \"max\": 4000, \"palette\": \"terrain\"}\n", + "m.add_layer(dem, vis, \"DEM\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "167", + "metadata": {}, + "outputs": [], + "source": [ + "landcover = ee.Image(\"USGS/NLCD_RELEASES/2019_REL/NLCD/2019\").select(\"landcover\")\n", + "m.add_layer(landcover, {}, \"NLCD 2019\")\n", + "m.add_legend(title=\"NLCD Land Cover Classification\", builtin_legend=\"NLCD\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "168", + "metadata": {}, + "outputs": [], + "source": [ + "stats = geemap.image_stats_by_zone(dem, landcover, reducer=\"MEAN\")\n", + "stats" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "169", + "metadata": {}, + "outputs": [], + "source": [ + "stats.to_csv(\"mean.csv\", index=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "170", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.image_stats_by_zone(dem, landcover, out_csv=\"std.csv\", reducer=\"STD\")" + ] + }, + { + "cell_type": "markdown", + "id": "171", + "metadata": {}, + "source": [ + "### Map Algebra\n", + "\n", + "This example demonstrates basic map algebra by computing the Normalized Difference Vegetation Index (NDVI) for a 5-year Landsat composite and the Enhanced Vegetation Index (EVI) for a Landsat 8 image. These indices are visualized with color scales to highlight areas of vegetation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "172", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "\n", + "# Load a 5-year Landsat 7 composite 1999-2003.\n", + "landsat_1999 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\")\n", + "\n", + "# Compute NDVI.\n", + "ndvi_1999 = (\n", + " landsat_1999.select(\"B4\")\n", + " .subtract(landsat_1999.select(\"B3\"))\n", + " .divide(landsat_1999.select(\"B4\").add(landsat_1999.select(\"B3\")))\n", + ")\n", + "\n", + "vis = {\"min\": 0, \"max\": 1, \"palette\": \"ndvi\"}\n", + "m.add_layer(ndvi_1999, vis, \"NDVI\")\n", + "m.add_colorbar(vis, label=\"NDVI\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "173", + "metadata": {}, + "outputs": [], + "source": [ + "# Load a Landsat 8 image.\n", + "image = ee.Image(\"LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318\")\n", + "\n", + "# Compute the EVI using an expression.\n", + "evi = image.expression(\n", + " \"2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))\",\n", + " {\n", + " \"NIR\": image.select(\"B5\"),\n", + " \"RED\": image.select(\"B4\"),\n", + " \"BLUE\": image.select(\"B2\"),\n", + " },\n", + ")\n", + "\n", + "# Define a map centered on San Francisco Bay.\n", + "m = geemap.Map(center=[37.4675, -122.1363], zoom=9)\n", + "\n", + "vis = {\"min\": 0, \"max\": 1, \"palette\": \"ndvi\"}\n", + "m.add_layer(evi, vis, \"EVI\")\n", + "m.add_colorbar(vis, label=\"EVI\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "174", + "metadata": {}, + "source": [ + "## Working with Local Geospatial Data\n", + "\n", + "### Raster Data\n", + "\n", + "Single-band and multi-band raster data can be loaded from local files. Here, a digital elevation model (DEM) is loaded and displayed with a terrain color scheme, and another raster is displayed with a false-color composite to highlight different features.\n", + "\n", + "#### Single-Band Raster Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "175", + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://github.com/giswqs/data/raw/main/raster/srtm90.tif\"\n", + "filename = \"dem.tif\"\n", + "geemap.download_file(url, filename)" + ] + }, + { + "cell_type": "markdown", + "id": "176", + "metadata": {}, + "source": [ + "#### Multi-Band Raster Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "177", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m.add_raster(filename, cmap=\"terrain\", layer_name=\"DEM\")\n", + "vis_params = {\"min\": 0, \"max\": 4000, \"palette\": \"terrain\"}\n", + "m.add_colorbar(vis_params, label=\"Elevation (m)\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "178", + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://github.com/giswqs/data/raw/main/raster/cog.tif\"\n", + "filename = \"cog.tif\"\n", + "geemap.download_file(url, filename)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "179", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m.add_raster(filename, indexes=[4, 1, 2], layer_name=\"False color\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "180", + "metadata": {}, + "source": [ + "### Vector Data\n", + "\n", + "Various vector data formats can be loaded and visualized with geemap, including GeoJSON, Shapefile, GeoDataFrame, and GeoPackage formats. GeoJSON data is styled dynamically with custom colors based on attributes, while Shapefiles and GeoDataFrames allow for simple, structured addition of geographic features to the map.\n", + "\n", + "#### GeoJSON" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "181", + "metadata": {}, + "outputs": [], + "source": [ + "in_geojson = (\n", + " \"https://github.com/opengeos/datasets/releases/download/vector/cables.geojson\"\n", + ")\n", + "m = geemap.Map()\n", + "m.add_geojson(in_geojson, layer_name=\"Cable lines\", info_mode=\"on_hover\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "182", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m.add_basemap(\"CartoDB.DarkMatter\")\n", + "callback = lambda feat: {\"color\": feat[\"properties\"][\"color\"], \"weight\": 2}\n", + "m.add_geojson(in_geojson, layer_name=\"Cable lines\", style_callback=callback)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "183", + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://github.com/opengeos/datasets/releases/download/world/countries.geojson\"\n", + "m = geemap.Map()\n", + "m.add_geojson(\n", + " url, layer_name=\"Countries\", fill_colors=[\"red\", \"yellow\", \"green\", \"orange\"]\n", + ")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "184", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "m = geemap.Map()\n", + "\n", + "\n", + "def random_color(feature):\n", + " return {\n", + " \"color\": \"black\",\n", + " \"weight\": 3,\n", + " \"fillColor\": random.choice([\"red\", \"yellow\", \"green\", \"orange\"]),\n", + " }\n", + "\n", + "\n", + "m.add_geojson(url, layer_name=\"Countries\", style_callback=random_color)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "185", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "\n", + "style = {\n", + " \"stroke\": True,\n", + " \"color\": \"#0000ff\",\n", + " \"weight\": 2,\n", + " \"opacity\": 1,\n", + " \"fill\": True,\n", + " \"fillColor\": \"#0000ff\",\n", + " \"fillOpacity\": 0.1,\n", + "}\n", + "\n", + "hover_style = {\"fillOpacity\": 0.7}\n", + "\n", + "m.add_geojson(url, layer_name=\"Countries\", style=style, hover_style=hover_style)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "186", + "metadata": {}, + "source": [ + "#### Shapefile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "187", + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://github.com/opengeos/datasets/releases/download/world/countries.zip\"\n", + "geemap.download_file(url, overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "188", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "in_shp = \"countries.shp\"\n", + "m.add_shp(in_shp, layer_name=\"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "189", + "metadata": {}, + "source": [ + "#### GeoDataFrame" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "190", + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas as gpd\n", + "\n", + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "gdf = gpd.read_file(\"countries.shp\")\n", + "m.add_gdf(gdf, layer_name=\"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "191", + "metadata": {}, + "source": [ + "#### GeoPackage" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "192", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "data = \"https://github.com/opengeos/datasets/releases/download/world/countries.gpkg\"\n", + "m.add_vector(data, layer_name=\"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "193", + "metadata": {}, + "source": [ + "#### CSV to Vector\n", + "\n", + "Geemap enables easy conversion from CSV files to vector data formats like GeoJSON, Shapefile, and GeoPackage. The data from a CSV is visualized on a map, where cities are displayed with markers styled by region, and a legend is added for clear reference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "194", + "metadata": {}, + "outputs": [], + "source": [ + "data = \"https://github.com/gee-community/geemap/blob/master/examples/data/us_cities.csv\"\n", + "geemap.csv_to_df(data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "195", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.csv_to_geojson(\n", + " data, \"cities.geojson\", latitude=\"latitude\", longitude=\"longitude\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "196", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.csv_to_shp(data, \"cities.shp\", latitude=\"latitude\", longitude=\"longitude\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "197", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.csv_to_vector(data, \"cities.gpkg\", latitude=\"latitude\", longitude=\"longitude\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "198", + "metadata": {}, + "outputs": [], + "source": [ + "gdf = geemap.csv_to_gdf(data, latitude=\"latitude\", longitude=\"longitude\")\n", + "gdf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "199", + "metadata": {}, + "outputs": [], + "source": [ + "cities = (\n", + " \"https://github.com/gee-community/geemap/blob/master/examples/data/us_cities.csv\"\n", + ")\n", + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "m.add_points_from_xy(cities, x=\"longitude\", y=\"latitude\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "200", + "metadata": {}, + "outputs": [], + "source": [ + "regions = \"https://github.com/gee-community/geemap/blob/master/examples/data/us_regions.geojson\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "201", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "m.add_geojson(regions, layer_name=\"US Regions\")\n", + "m.add_points_from_xy(\n", + " cities,\n", + " x=\"longitude\",\n", + " y=\"latitude\",\n", + " layer_name=\"US Cities\",\n", + " color_column=\"region\",\n", + " icon_names=[\"gear\", \"map\", \"leaf\", \"globe\"],\n", + " spin=True,\n", + " add_legend=True,\n", + ")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "202", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[40, -100], zoom=4)\n", + "m.add_circle_markers_from_xy(\n", + " data,\n", + " x=\"longitude\",\n", + " y=\"latitude\",\n", + " radius=8,\n", + " color=\"blue\",\n", + " fill_color=\"black\",\n", + " fill_opacity=0.5,\n", + ")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "203", + "metadata": {}, + "source": [ + "## Accessing Cloud Optimized GeoTIFFs\n", + "\n", + "### Cloud Optimized GeoTIFFs (COG)\n", + "\n", + "This section shows how to add Cloud Optimized GeoTIFF (COG) layers to a map using URLs, which allows for efficient loading and visualization of large raster files stored on the cloud. In this example, a pre-event image of the 2020 California wildfire is added to a map, allowing for remote sensing analysis without local file storage." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "204", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "url = \"https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2018-02-16/pine-gulch-fire20/1030010076004E00.tif\"\n", + "m.add_cog_layer(url, name=\"Fire (pre-event)\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "205", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.cog_center(url)" + ] + }, + { + "cell_type": "markdown", + "id": "206", + "metadata": {}, + "source": [ + "The COG functions also allow inspection of data, such as viewing the bounds and available bands in the GeoTIFF file. The pre- and post-event images can be compared in a split map view to analyze changes due to the fire." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "207", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.cog_bands(url)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "208", + "metadata": {}, + "outputs": [], + "source": [ + "url2 = \"https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-14/pine-gulch-fire20/10300100AAC8DD00.tif\"\n", + "m.add_cog_layer(url2, name=\"Fire (post-event)\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "209", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[39.4568, -108.5107], zoom=12)\n", + "m.split_map(left_layer=url2, right_layer=url)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "210", + "metadata": {}, + "source": [ + "### SpatioTemporal Asset Catalog (STAC)\n", + "\n", + "STAC collections can be accessed and visualized similarly, enabling dynamic mapping of spatiotemporal data. By specifying STAC URLs, you can view dataset bounds, center the map on a dataset, and select specific bands to add as layers. This example shows adding panchromatic and false-color imagery for enhanced visual analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "211", + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://tinyurl.com/22vptbws\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "212", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.stac_bounds(url)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "213", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.stac_center(url)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "214", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.stac_bands(url)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "215", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m.add_stac_layer(url, bands=[\"pan\"], name=\"Panchromatic\")\n", + "m.add_stac_layer(url, bands=[\"B3\", \"B2\", \"B1\"], name=\"False color\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "216", + "metadata": {}, + "source": [ + "## Exporting Earth Engine Data\n", + "\n", + "### Exporting Images\n", + "\n", + "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.\n", + "\n", + "\n", + "Add a Landsat image to the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "217", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "\n", + "image = ee.Image(\"LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318\").select(\n", + " [\"B5\", \"B4\", \"B3\"]\n", + ")\n", + "\n", + "vis_params = {\"min\": 0, \"max\": 0.5, \"gamma\": [0.95, 1.1, 1]}\n", + "\n", + "m.center_object(image)\n", + "m.add_layer(image, vis_params, \"Landsat\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "218", + "metadata": {}, + "source": [ + "Add a rectangle to the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "219", + "metadata": {}, + "outputs": [], + "source": [ + "region = ee.Geometry.BBox(-122.5955, 37.5339, -122.0982, 37.8252)\n", + "fc = ee.FeatureCollection(region)\n", + "style = {\"color\": \"ffff00ff\", \"fillColor\": \"00000000\"}\n", + "m.add_layer(fc.style(**style), {}, \"ROI\")" + ] + }, + { + "cell_type": "markdown", + "id": "220", + "metadata": {}, + "source": [ + "To local drive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "221", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_export_image(image, filename=\"landsat.tif\", scale=30, region=region)" + ] + }, + { + "cell_type": "markdown", + "id": "222", + "metadata": {}, + "source": [ + "Check image projection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "223", + "metadata": {}, + "outputs": [], + "source": [ + "projection = image.select(0).projection().getInfo()\n", + "projection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "224", + "metadata": {}, + "outputs": [], + "source": [ + "crs = projection[\"crs\"]\n", + "crs_transform = projection[\"transform\"]" + ] + }, + { + "cell_type": "markdown", + "id": "225", + "metadata": {}, + "source": [ + "Specify region, crs, and crs_transform." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "226", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_export_image(\n", + " image,\n", + " filename=\"landsat_crs.tif\",\n", + " crs=crs,\n", + " crs_transform=crs_transform,\n", + " region=region,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "227", + "metadata": {}, + "source": [ + "To Google Drive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "228", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_export_image_to_drive(\n", + " image, description=\"landsat\", folder=\"export\", region=region, scale=30\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "229", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.download_ee_image(image, \"landsat.tif\", scale=90)" + ] + }, + { + "cell_type": "markdown", + "id": "230", + "metadata": {}, + "source": [ + "### Exporting Image Collections\n", + "\n", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "231", + "metadata": {}, + "outputs": [], + "source": [ + "point = ee.Geometry.Point(-99.2222, 46.7816)\n", + "collection = (\n", + " ee.ImageCollection(\"USDA/NAIP/DOQQ\")\n", + " .filterBounds(point)\n", + " .filterDate(\"2008-01-01\", \"2018-01-01\")\n", + " .filter(ee.Filter.listContains(\"system:band_names\", \"N\"))\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "232", + "metadata": {}, + "outputs": [], + "source": [ + "collection.aggregate_array(\"system:index\")" + ] + }, + { + "cell_type": "markdown", + "id": "233", + "metadata": {}, + "source": [ + "To local drive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "234", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_export_image_collection(collection, out_dir=\".\", scale=10)" + ] + }, + { + "cell_type": "markdown", + "id": "235", + "metadata": {}, + "source": [ + "To Google Drive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "236", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_export_image_collection_to_drive(collection, folder=\"export\", scale=10)" + ] + }, + { + "cell_type": "markdown", + "id": "237", + "metadata": {}, + "source": [ + "### Exporting Feature Collections\n", + "\n", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "238", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", + "fc = states.filter(ee.Filter.eq(\"NAME\", \"Alaska\"))\n", + "m.add_layer(fc, {}, \"Alaska\")\n", + "m.center_object(fc, 4)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "239", + "metadata": {}, + "source": [ + "To local drive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "240", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_shp(fc, filename=\"Alaska.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "241", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_export_vector(fc, filename=\"Alaska.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "242", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_geojson(fc, filename=\"Alaska.geojson\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "243", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_csv(fc, filename=\"Alaska.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "244", + "metadata": {}, + "outputs": [], + "source": [ + "gdf = geemap.ee_to_gdf(fc)\n", + "gdf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "245", + "metadata": {}, + "outputs": [], + "source": [ + "df = geemap.ee_to_df(fc)\n", + "df" + ] + }, + { + "cell_type": "markdown", + "id": "246", + "metadata": {}, + "source": [ + "To Google Drive" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "247", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_export_vector_to_drive(\n", + " fc, description=\"Alaska\", fileFormat=\"SHP\", folder=\"export\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "248", + "metadata": {}, + "source": [ + "## Creating Timelapse Animations\n", + "\n", + "### Landsat Timelapse\n", + "\n", + "The Landsat timelapse tool creates animations of changes over time. Here, we use a defined region of interest (ROI) to generate timelapse GIFs for Las Vegas (1984-2023):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "249", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "roi = ee.Geometry.BBox(-115.5541, 35.8044, -113.9035, 36.5581)\n", + "m.add_layer(roi)\n", + "m.center_object(roi)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "250", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.landsat_timelapse(\n", + " roi,\n", + " out_gif=\"las_vegas.gif\",\n", + " start_year=1984,\n", + " end_year=2023,\n", + " bands=[\"NIR\", \"Red\", \"Green\"],\n", + " frames_per_second=5,\n", + " title=\"Las Vegas, NV\",\n", + " font_color=\"blue\",\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "251", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "roi = ee.Geometry.BBox(113.8252, 22.1988, 114.0851, 22.3497)\n", + "m.add_layer(roi)\n", + "m.center_object(roi)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "252", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.landsat_timelapse(\n", + " roi,\n", + " out_gif=\"hong_kong.gif\",\n", + " start_year=1990,\n", + " end_year=2022,\n", + " start_date=\"01-01\",\n", + " end_date=\"12-31\",\n", + " bands=[\"SWIR1\", \"NIR\", \"Red\"],\n", + " frames_per_second=3,\n", + " title=\"Hong Kong\",\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "markdown", + "id": "253", + "metadata": {}, + "source": [ + "### Sentinel-2 Timelapse\n", + "\n", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "254", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map(center=[41.718934, -86.894547], zoom=12)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "255", + "metadata": {}, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "256", + "metadata": {}, + "outputs": [], + "source": [ + "roi = m.user_roi\n", + "if roi is None:\n", + " roi = ee.Geometry.BBox(-87.0492, 41.6545, -86.7903, 41.7604)\n", + " m.add_layer(roi)\n", + " m.center_object(roi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "257", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.sentinel2_timelapse(\n", + " roi,\n", + " out_gif=\"sentinel2.gif\",\n", + " start_year=2017,\n", + " end_year=2023,\n", + " start_date=\"06-01\",\n", + " end_date=\"09-01\",\n", + " frequency=\"year\",\n", + " bands=[\"SWIR1\", \"NIR\", \"Red\"],\n", + " frames_per_second=3,\n", + " title=\"Sentinel-2 Timelapse\",\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "markdown", + "id": "258", + "metadata": {}, + "source": [ + "### MODIS Timelapse\n", + "\n", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "259", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "260", + "metadata": {}, + "outputs": [], + "source": [ + "roi = m.user_roi\n", + "if roi is None:\n", + " roi = ee.Geometry.BBox(-18.6983, -36.1630, 52.2293, 38.1446)\n", + " m.add_layer(roi)\n", + " m.center_object(roi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "261", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.modis_ndvi_timelapse(\n", + " roi,\n", + " out_gif=\"ndvi.gif\",\n", + " data=\"Terra\",\n", + " band=\"NDVI\",\n", + " start_date=\"2000-01-01\",\n", + " end_date=\"2022-12-31\",\n", + " frames_per_second=3,\n", + " title=\"MODIS NDVI Timelapse\",\n", + " overlay_data=\"countries\",\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "markdown", + "id": "262", + "metadata": {}, + "source": [ + "MODIS temperature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "263", + "metadata": {}, + "outputs": [], + "source": [ + "m = geemap.Map()\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "264", + "metadata": {}, + "outputs": [], + "source": [ + "roi = m.user_roi\n", + "if roi is None:\n", + " roi = ee.Geometry.BBox(-171.21, -57.13, 177.53, 79.99)\n", + " m.add_layer(roi)\n", + " m.center_object(roi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "265", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.modis_ocean_color_timelapse(\n", + " satellite=\"Aqua\",\n", + " start_date=\"2018-01-01\",\n", + " end_date=\"2020-12-31\",\n", + " roi=roi,\n", + " frequency=\"month\",\n", + " out_gif=\"temperature.gif\",\n", + " overlay_data=\"continents\",\n", + " overlay_color=\"yellow\",\n", + " overlay_opacity=0.5,\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "markdown", + "id": "266", + "metadata": {}, + "source": [ + "### GOES Timelapse\n", + "\n", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "267", + "metadata": {}, + "outputs": [], + "source": [ + "roi = ee.Geometry.BBox(167.1898, -28.5757, 202.6258, -12.4411)\n", + "start_date = \"2022-01-15T03:00:00\"\n", + "end_date = \"2022-01-15T07:00:00\"\n", + "data = \"GOES-17\"\n", + "scan = \"full_disk\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "268", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.goes_timelapse(\n", + " roi, \"goes.gif\", start_date, end_date, data, scan, framesPerSecond=5\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "269", + "metadata": {}, + "outputs": [], + "source": [ + "roi = ee.Geometry.BBox(-159.5954, 24.5178, -114.2438, 60.4088)\n", + "start_date = \"2021-10-24T14:00:00\"\n", + "end_date = \"2021-10-25T01:00:00\"\n", + "data = \"GOES-17\"\n", + "scan = \"full_disk\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "270", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.goes_timelapse(\n", + " roi, \"hurricane.gif\", start_date, end_date, data, scan, framesPerSecond=5\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "271", + "metadata": {}, + "outputs": [], + "source": [ + "roi = ee.Geometry.BBox(-121.0034, 36.8488, -117.9052, 39.0490)\n", + "start_date = \"2020-09-05T15:00:00\"\n", + "end_date = \"2020-09-06T02:00:00\"\n", + "data = \"GOES-17\"\n", + "scan = \"full_disk\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "272", + "metadata": {}, + "outputs": [], + "source": [ + "timelapse = geemap.goes_fire_timelapse(\n", + " roi, \"fire.gif\", start_date, end_date, data, scan, framesPerSecond=5\n", + ")\n", + "geemap.show_image(timelapse)" + ] + }, + { + "cell_type": "markdown", + "id": "273", + "metadata": {}, + "source": [ + "## Charting\n", + "\n", + "### Charting Features\n", + "\n", + "#### Import libraries\n", + "\n", + "To create charts for Earth Engine features, import required libraries like `calendar`, `ee`, `geemap`, and `chart`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "274", + "metadata": {}, + "outputs": [], + "source": [ + "import calendar\n", + "import ee\n", + "import geemap\n", + "from geemap import chart" + ] + }, + { + "cell_type": "markdown", + "id": "275", + "metadata": {}, + "source": [ + "Initialize Earth Engine using `geemap.ee_initialize()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "276", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_initialize()" + ] + }, + { + "cell_type": "markdown", + "id": "277", + "metadata": {}, + "source": [ + "#### feature_by_feature\n", + "\n", + "This function allows feature plotting along the x-axis, with values from selected properties represented along the y-axis. Ecoregions data is used to chart average monthly temperature across regions, where months serve as series columns." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "278", + "metadata": {}, + "outputs": [], + "source": [ + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "features = ecoregions.select(\"[0-9][0-9]_tmean|label\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "279", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_df(features)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "280", + "metadata": {}, + "outputs": [], + "source": [ + "x_property = \"label\"\n", + "y_properties = [str(x).zfill(2) + \"_tmean\" for x in range(1, 13)]\n", + "\n", + "labels = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...]\n", + "\n", + "colors = [\n", + " \"#604791\",\n", + " \"#1d6b99\",\n", + " \"#39a8a7\",\n", + " \"#0f8755\",\n", + " \"#76b349\",\n", + " \"#f0af07\",\n", + " \"#e37d05\",\n", + " \"#cf513e\",\n", + " \"#96356f\",\n", + " \"#724173\",\n", + " \"#9c4f97\",\n", + " \"#696969\",\n", + "]\n", + "title = \"Average Monthly Temperature by Ecoregion\"\n", + "x_label = \"Ecoregion\"\n", + "y_label = \"Temperature\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "281", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.feature_by_feature(\n", + " features,\n", + " x_property,\n", + " y_properties,\n", + " colors=colors,\n", + " labels=labels,\n", + " title=title,\n", + " x_label=x_label,\n", + " y_label=y_label,\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "282", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/MZa99Vf.png)" + ] + }, + { + "cell_type": "markdown", + "id": "283", + "metadata": {}, + "source": [ + "#### feature.by_property\n", + "\n", + "Displays average precipitation by month for each ecoregion. Properties represent precipitation values for each month, allowing for visual comparison across regions by month." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "284", + "metadata": {}, + "outputs": [], + "source": [ + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "features = ecoregions.select(\"[0-9][0-9]_ppt|label\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "285", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_df(features)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "286", + "metadata": {}, + "outputs": [], + "source": [ + "keys = [str(x).zfill(2) + \"_ppt\" for x in range(1, 13)]\n", + "values = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "287", + "metadata": {}, + "outputs": [], + "source": [ + "x_properties = dict(zip(keys, values))\n", + "series_property = \"label\"\n", + "title = \"Average Ecoregion Precipitation by Month\"\n", + "colors = [\"#f0af07\", \"#0f8755\", \"#76b349\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "288", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.feature_by_property(\n", + " features,\n", + " x_properties,\n", + " series_property,\n", + " title=title,\n", + " colors=colors,\n", + " x_label=\"Month\",\n", + " y_label=\"Precipitation (mm)\",\n", + " legend_location=\"top-left\",\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "289", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/6RhuUc7.png)" + ] + }, + { + "cell_type": "markdown", + "id": "290", + "metadata": {}, + "source": [ + "#### feature_groups\n", + "\n", + "Plots groups of features based on property values, showing average January temperature for ecoregions divided into \"Warm\" and \"Cold\" categories." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "291", + "metadata": {}, + "outputs": [], + "source": [ + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "features = ecoregions.select(\"[0-9][0-9]_ppt|label\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "292", + "metadata": {}, + "outputs": [], + "source": [ + "features = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "x_property = \"label\"\n", + "y_property = \"01_tmean\"\n", + "series_property = \"warm\"\n", + "title = \"Average January Temperature by Ecoregion\"\n", + "colors = [\"#cf513e\", \"#1d6b99\"]\n", + "labels = [\"Warm\", \"Cold\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "293", + "metadata": {}, + "outputs": [], + "source": [ + "chart.feature_groups(\n", + " features,\n", + " x_property,\n", + " y_property,\n", + " series_property,\n", + " title=title,\n", + " colors=colors,\n", + " x_label=\"Ecoregion\",\n", + " y_label=\"January Temperature (°C)\",\n", + " legend_location=\"top-right\",\n", + " labels=labels,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "294", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/YFZlJtc.png)" + ] + }, + { + "cell_type": "markdown", + "id": "295", + "metadata": {}, + "source": [ + "#### feature_histogram\n", + "\n", + "Generates a histogram displaying July precipitation distribution across a region, showing pixel count for different precipitation levels, useful for understanding data spread." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "296", + "metadata": {}, + "outputs": [], + "source": [ + "source = ee.ImageCollection(\"OREGONSTATE/PRISM/Norm91m\").toBands()\n", + "region = ee.Geometry.Rectangle(-123.41, 40.43, -116.38, 45.14)\n", + "features = source.sample(region, 5000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "297", + "metadata": {}, + "outputs": [], + "source": [ + "geemap.ee_to_df(features.limit(5).select([\"07_ppt\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "298", + "metadata": {}, + "outputs": [], + "source": [ + "property = \"07_ppt\"\n", + "title = \"July Precipitation Distribution for NW USA\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "299", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.feature_histogram(\n", + " features,\n", + " property,\n", + " max_buckets=None,\n", + " title=title,\n", + " x_label=\"Precipitation (mm)\",\n", + " y_label=\"Pixel Count\",\n", + " colors=[\"#1d6b99\"],\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "300", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/ErIp7Oy.png)\n", + "\n", + "### Charting Images\n", + "\n", + "#### image_by_region\n", + "\n", + "Displays monthly temperature for each ecoregion using the `image_by_region` function, aggregating data to visualize average temperature by month across regions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "301", + "metadata": {}, + "outputs": [], + "source": [ + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "image = (\n", + " ee.ImageCollection(\"OREGONSTATE/PRISM/Norm91m\").toBands().select(\"[0-9][0-9]_tmean\")\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "302", + "metadata": {}, + "outputs": [], + "source": [ + "labels = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...]\n", + "title = \"Average Monthly Temperature by Ecoregion\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "303", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_by_region(\n", + " image,\n", + " ecoregions,\n", + " reducer=\"mean\",\n", + " scale=500,\n", + " x_property=\"label\",\n", + " title=title,\n", + " x_label=\"Ecoregion\",\n", + " y_label=\"Temperature\",\n", + " labels=labels,\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "304", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/y4rp3dK.png)" + ] + }, + { + "cell_type": "markdown", + "id": "305", + "metadata": {}, + "source": [ + "#### image_regions\n", + "\n", + "\n", + "Generates a chart showing average monthly precipitation across regions, using properties to represent months and comparing precipitation levels." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "306", + "metadata": {}, + "outputs": [], + "source": [ + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "image = (\n", + " ee.ImageCollection(\"OREGONSTATE/PRISM/Norm91m\").toBands().select(\"[0-9][0-9]_ppt\")\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "307", + "metadata": {}, + "outputs": [], + "source": [ + "keys = [str(x).zfill(2) + \"_ppt\" for x in range(1, 13)]\n", + "values = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "308", + "metadata": {}, + "outputs": [], + "source": [ + "x_properties = dict(zip(keys, values))\n", + "title = \"Average Ecoregion Precipitation by Month\"\n", + "colors = [\"#f0af07\", \"#0f8755\", \"#76b349\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "309", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_regions(\n", + " image,\n", + " ecoregions,\n", + " reducer=\"mean\",\n", + " scale=500,\n", + " series_property=\"label\",\n", + " x_labels=x_properties,\n", + " title=title,\n", + " colors=colors,\n", + " x_label=\"Month\",\n", + " y_label=\"Precipitation (mm)\",\n", + " legend_location=\"top-left\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "310", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/5WJVCNY.png)" + ] + }, + { + "cell_type": "markdown", + "id": "311", + "metadata": {}, + "source": [ + "#### image_by_class\n", + "\n", + "Displays spectral signatures of ecoregions by wavelength, showing reflectance values across spectral bands for comparison." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "312", + "metadata": {}, + "outputs": [], + "source": [ + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "\n", + "image = (\n", + " ee.ImageCollection(\"MODIS/061/MOD09A1\")\n", + " .filter(ee.Filter.date(\"2018-06-01\", \"2018-09-01\"))\n", + " .select(\"sur_refl_b0[0-7]\")\n", + " .mean()\n", + " .select([2, 3, 0, 1, 4, 5, 6])\n", + ")\n", + "\n", + "wavelengths = [469, 555, 655, 858, 1240, 1640, 2130]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "313", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_by_class(\n", + " image,\n", + " class_band=\"label\",\n", + " region=ecoregions,\n", + " reducer=\"MEAN\",\n", + " scale=500,\n", + " x_labels=wavelengths,\n", + " title=\"Ecoregion Spectral Signatures\",\n", + " x_label=\"Wavelength (nm)\",\n", + " y_label=\"Reflectance (x1e4)\",\n", + " colors=[\"#f0af07\", \"#0f8755\", \"#76b349\"],\n", + " legend_location=\"top-left\",\n", + " interpolation=\"basis\",\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "314", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/XqYHvBV.png)" + ] + }, + { + "cell_type": "markdown", + "id": "315", + "metadata": {}, + "source": [ + "#### image_histogram\n", + "\n", + "Generates histograms to visualize MODIS surface reflectance distribution across red, NIR, and SWIR bands, providing insights into data distribution by band." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "316", + "metadata": {}, "outputs": [], "source": [ - "timelapse = geemap.landsat_timelapse(\n", - " roi,\n", - " out_gif=\"Taiwan.gif\",\n", - " start_year=1988,\n", - " end_year=2024,\n", - " start_date=\"01-01\",\n", - " end_date=\"12-31\",\n", - " bands=[\"SWIR1\", \"NIR\", \"Red\"],\n", - " frames_per_second=5,\n", - " title=\"Taipei\",\n", - " progress_bar_color=\"blue\",\n", - " mp4=True,\n", + "image = (\n", + " ee.ImageCollection(\"MODIS/061/MOD09A1\")\n", + " .filter(ee.Filter.date(\"2018-06-01\", \"2018-09-01\"))\n", + " .select([\"sur_refl_b01\", \"sur_refl_b02\", \"sur_refl_b06\"])\n", + " .mean()\n", ")\n", - "geemap.show_image(timelapse)" + "\n", + "region = ee.Geometry.Rectangle([-112.60, 40.60, -111.18, 41.22])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "317", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_histogram(\n", + " image,\n", + " region,\n", + " scale=500,\n", + " max_buckets=200,\n", + " min_bucket_width=1.0,\n", + " max_raw=1000,\n", + " max_pixels=int(1e6),\n", + " title=\"MODIS SR Reflectance Histogram\",\n", + " labels=[\"Red\", \"NIR\", \"SWIR\"],\n", + " colors=[\"#cf513e\", \"#1d6b99\", \"#f0af07\"],\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "318", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/mY4yoYH.png)\n", + "\n", + "### Charting Image Collections\n", + "\n", + "#### image_series\n", + "\n", + "Creates a time series chart of vegetation indices (NDVI and EVI) for a forest region, helping to track vegetation health over time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "319", + "metadata": {}, + "outputs": [], + "source": [ + "# Define the forest feature collection.\n", + "forest = ee.FeatureCollection(\"projects/google/charts_feature_example\").filter(\n", + " ee.Filter.eq(\"label\", \"Forest\")\n", + ")\n", + "\n", + "# Load MODIS vegetation indices data and subset a decade of images.\n", + "veg_indices = (\n", + " ee.ImageCollection(\"MODIS/061/MOD13A1\")\n", + " .filter(ee.Filter.date(\"2010-01-01\", \"2020-01-01\"))\n", + " .select([\"NDVI\", \"EVI\"])\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "320", + "metadata": {}, + "outputs": [], + "source": [ + "title = \"Average Vegetation Index Value by Date for Forest\"\n", + "x_label = \"Year\"\n", + "y_label = \"Vegetation index (x1e4)\"\n", + "colors = [\"#e37d05\", \"#1d6b99\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "321", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_series(\n", + " veg_indices,\n", + " region=forest,\n", + " reducer=ee.Reducer.mean(),\n", + " scale=500,\n", + " x_property=\"system:time_start\",\n", + " chart_type=\"LineChart\",\n", + " x_cols=\"date\",\n", + " y_cols=[\"NDVI\", \"EVI\"],\n", + " colors=colors,\n", + " title=title,\n", + " x_label=x_label,\n", + " y_label=y_label,\n", + " legend_location=\"right\",\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "322", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/r9zSJh6.png)" + ] + }, + { + "cell_type": "markdown", + "id": "323", + "metadata": {}, + "source": [ + "#### image_series_by_region\n", + "\n", + "Shows NDVI time series by region, comparing desert, forest, and grassland areas. Each region has a unique series for easy comparison." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "324", + "metadata": {}, + "outputs": [], + "source": [ + "# Import the example feature collection.\n", + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "\n", + "# Load MODIS vegetation indices data and subset a decade of images.\n", + "veg_indices = (\n", + " ee.ImageCollection(\"MODIS/061/MOD13A1\")\n", + " .filter(ee.Filter.date(\"2010-01-01\", \"2020-01-01\"))\n", + " .select([\"NDVI\"])\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "325", + "metadata": {}, + "outputs": [], + "source": [ + "title = \"Average NDVI Value by Date\"\n", + "x_label = \"Date\"\n", + "y_label = \"NDVI (x1e4)\"\n", + "x_cols = \"index\"\n", + "y_cols = [\"Desert\", \"Forest\", \"Grassland\"]\n", + "colors = [\"#f0af07\", \"#0f8755\", \"#76b349\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "326", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_series_by_region(\n", + " veg_indices,\n", + " regions=ecoregions,\n", + " reducer=ee.Reducer.mean(),\n", + " band=\"NDVI\",\n", + " scale=500,\n", + " x_property=\"system:time_start\",\n", + " series_property=\"label\",\n", + " chart_type=\"LineChart\",\n", + " x_cols=x_cols,\n", + " y_cols=y_cols,\n", + " title=title,\n", + " x_label=x_label,\n", + " y_label=y_label,\n", + " colors=colors,\n", + " stroke_width=3,\n", + " legend_location=\"bottom-left\",\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "327", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/rnILSfI.png)" + ] + }, + { + "cell_type": "markdown", + "id": "328", + "metadata": {}, + "source": [ + "#### image_doy_series\n", + "\n", + "Plots average vegetation index by day of year for a specific region, showing seasonal vegetation patterns over a decade." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "329", + "metadata": {}, + "outputs": [], + "source": [ + "# Import the example feature collection and subset the grassland feature.\n", + "grassland = ee.FeatureCollection(\"projects/google/charts_feature_example\").filter(\n", + " ee.Filter.eq(\"label\", \"Grassland\")\n", + ")\n", + "\n", + "# Load MODIS vegetation indices data and subset a decade of images.\n", + "veg_indices = (\n", + " ee.ImageCollection(\"MODIS/061/MOD13A1\")\n", + " .filter(ee.Filter.date(\"2010-01-01\", \"2020-01-01\"))\n", + " .select([\"NDVI\", \"EVI\"])\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "330", + "metadata": {}, + "outputs": [], + "source": [ + "title = \"Average Vegetation Index Value by Day of Year for Grassland\"\n", + "x_label = \"Day of Year\"\n", + "y_label = \"Vegetation Index (x1e4)\"\n", + "colors = [\"#f0af07\", \"#0f8755\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "331", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_doy_series(\n", + " image_collection=veg_indices,\n", + " region=grassland,\n", + " scale=500,\n", + " chart_type=\"LineChart\",\n", + " title=title,\n", + " x_label=x_label,\n", + " y_label=y_label,\n", + " colors=colors,\n", + " stroke_width=5,\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "332", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/F0z088e.png)" + ] + }, + { + "cell_type": "markdown", + "id": "333", + "metadata": {}, + "source": [ + "#### image_doy_series_by_year\n", + "\n", + "Compares NDVI by day of year across two different years, showing how vegetation patterns vary between 2012 and 2019." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "334", + "metadata": {}, + "outputs": [], + "source": [ + "# Import the example feature collection and subset the grassland feature.\n", + "grassland = ee.FeatureCollection(\"projects/google/charts_feature_example\").filter(\n", + " ee.Filter.eq(\"label\", \"Grassland\")\n", + ")\n", + "\n", + "# Load MODIS vegetation indices data and subset years 2012 and 2019.\n", + "veg_indices = (\n", + " ee.ImageCollection(\"MODIS/061/MOD13A1\")\n", + " .filter(\n", + " ee.Filter.Or(\n", + " ee.Filter.date(\"2012-01-01\", \"2013-01-01\"),\n", + " ee.Filter.date(\"2019-01-01\", \"2020-01-01\"),\n", + " )\n", + " )\n", + " .select([\"NDVI\", \"EVI\"])\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "335", + "metadata": {}, + "outputs": [], + "source": [ + "title = \"Average Vegetation Index Value by Day of Year for Grassland\"\n", + "x_label = \"Day of Year\"\n", + "y_label = \"Vegetation Index (x1e4)\"\n", + "colors = [\"#e37d05\", \"#1d6b99\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "336", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.doy_series_by_year(\n", + " veg_indices,\n", + " band_name=\"NDVI\",\n", + " region=grassland,\n", + " scale=500,\n", + " chart_type=\"LineChart\",\n", + " colors=colors,\n", + " title=title,\n", + " x_label=x_label,\n", + " y_label=y_label,\n", + " stroke_width=5,\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "337", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/ui6zpbl.png)" + ] + }, + { + "cell_type": "markdown", + "id": "338", + "metadata": {}, + "source": [ + "#### image_doy_series_by_region\n", + "\n", + "Visualizes average NDVI by day of year across regions, showcasing seasonal changes for desert, forest, and grassland ecoregions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "339", + "metadata": {}, + "outputs": [], + "source": [ + "# Import the example feature collection and subset the grassland feature.\n", + "ecoregions = ee.FeatureCollection(\"projects/google/charts_feature_example\")\n", + "\n", + "# Load MODIS vegetation indices data and subset a decade of images.\n", + "veg_indices = (\n", + " ee.ImageCollection(\"MODIS/061/MOD13A1\")\n", + " .filter(ee.Filter.date(\"2010-01-01\", \"2020-01-01\"))\n", + " .select([\"NDVI\"])\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "340", + "metadata": {}, + "outputs": [], + "source": [ + "title = \"Average Vegetation Index Value by Day of Year for Grassland\"\n", + "x_label = \"Day of Year\"\n", + "y_label = \"Vegetation Index (x1e4)\"\n", + "colors = [\"#f0af07\", \"#0f8755\", \"#76b349\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "341", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.image_doy_series_by_region(\n", + " veg_indices,\n", + " \"NDVI\",\n", + " ecoregions,\n", + " region_reducer=\"mean\",\n", + " scale=500,\n", + " year_reducer=ee.Reducer.mean(),\n", + " start_day=1,\n", + " end_day=366,\n", + " series_property=\"label\",\n", + " stroke_width=5,\n", + " chart_type=\"LineChart\",\n", + " title=title,\n", + " x_label=x_label,\n", + " y_label=y_label,\n", + " colors=colors,\n", + " legend_location=\"right\",\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "342", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/eGqGoRs.png)\n", + "\n", + "### Charting Array and List\n", + "\n", + "#### Scatter Plot\n", + "\n", + "Displays relationships between spectral bands, plotting red reflectance against NIR and SWIR reflectance to examine band correlations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "343", + "metadata": {}, + "outputs": [], + "source": [ + "# Import the example feature collection and subset the forest feature.\n", + "forest = ee.FeatureCollection(\"projects/google/charts_feature_example\").filter(\n", + " ee.Filter.eq(\"label\", \"Forest\")\n", + ")\n", + "\n", + "# Define a MODIS surface reflectance composite.\n", + "modisSr = (\n", + " ee.ImageCollection(\"MODIS/061/MOD09A1\")\n", + " .filter(ee.Filter.date(\"2018-06-01\", \"2018-09-01\"))\n", + " .select(\"sur_refl_b0[0-7]\")\n", + " .mean()\n", + ")\n", + "\n", + "# Reduce MODIS reflectance bands by forest region; get a dictionary with\n", + "# band names as keys, pixel values as lists.\n", + "pixel_vals = modisSr.reduceRegion(\n", + " **{\"reducer\": ee.Reducer.toList(), \"geometry\": forest.geometry(), \"scale\": 2000}\n", + ")\n", + "\n", + "# Convert NIR and SWIR value lists to an array to be plotted along the y-axis.\n", + "y_values = pixel_vals.toArray([\"sur_refl_b02\", \"sur_refl_b06\"])\n", + "\n", + "\n", + "# Get the red band value list; to be plotted along the x-axis.\n", + "x_values = ee.List(pixel_vals.get(\"sur_refl_b01\"))" ] }, { "cell_type": "code", "execution_count": null, - "id": "121", + "id": "344", "metadata": {}, "outputs": [], "source": [ - "roi = ee.Geometry.BBox(113.8252, 22.1988, 114.0851, 22.3497)\n", - "timelapse = geemap.landsat_timelapse(\n", - " roi,\n", - " out_gif=\"hong_kong.gif\",\n", - " start_year=1990,\n", - " end_year=2022,\n", - " start_date=\"01-01\",\n", - " end_date=\"12-31\",\n", - " bands=[\"SWIR1\", \"NIR\", \"Red\"],\n", - " frames_per_second=3,\n", - " title=\"Hong Kong\",\n", - ")\n", - "geemap.show_image(timelapse)" + "title = \"Relationship Among Spectral Bands for Forest Pixels\"\n", + "colors = [\"rgba(29,107,153,0.4)\", \"rgba(207,81,62,0.4)\"]" ] }, { "cell_type": "code", "execution_count": null, - "id": "122", + "id": "345", "metadata": {}, "outputs": [], "source": [ - "roi = ee.Geometry.BBox(-115.5541, 35.8044, -113.9035, 36.5581)\n", - "timelapse = geemap.landsat_timelapse(\n", - " roi,\n", - " out_gif=\"las_vegas.gif\",\n", - " start_year=1984,\n", - " end_year=2023,\n", - " bands=[\"NIR\", \"Red\", \"Green\"],\n", - " frames_per_second=5,\n", - " title=\"Las Vegas, NV\",\n", - " font_color=\"blue\",\n", + "fig = chart.array_values(\n", + " y_values,\n", + " axis=1,\n", + " x_labels=x_values,\n", + " series_names=[\"NIR\", \"SWIR\"],\n", + " chart_type=\"ScatterChart\",\n", + " colors=colors,\n", + " title=title,\n", + " x_label=\"Red reflectance (x1e4)\",\n", + " y_label=\"NIR & SWIR reflectance (x1e4)\",\n", + " default_size=15,\n", + " xlim=(0, 800),\n", ")\n", - "geemap.show_image(timelapse)" + "fig" ] }, { "cell_type": "markdown", - "id": "123", + "id": "346", "metadata": {}, "source": [ - "### Sentinel-2 timelapse" + "![](https://i.imgur.com/zkPlZIO.png)" ] }, { "cell_type": "code", "execution_count": null, - "id": "124", + "id": "347", "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "m.set_center(121.615219, 25.041219, 12)\n", - "m" + "x = ee.List(pixel_vals.get(\"sur_refl_b01\"))\n", + "y = ee.List(pixel_vals.get(\"sur_refl_b06\"))" ] }, { "cell_type": "code", "execution_count": null, - "id": "125", + "id": "348", "metadata": {}, "outputs": [], "source": [ - "roi = m.user_roi\n", - "if roi is None:\n", - " roi = ee.Geometry.BBox(121.3824, 24.9325, 121.6653, 25.1496)\n", - " m.add_layer(roi)\n", - " m.center_object(roi)" + "fig = chart.array_values(\n", + " y,\n", + " x_labels=x,\n", + " series_names=[\"SWIR\"],\n", + " chart_type=\"ScatterChart\",\n", + " colors=[\"rgba(207,81,62,0.4)\"],\n", + " title=title,\n", + " x_label=\"Red reflectance (x1e4)\",\n", + " y_label=\"SWIR reflectance (x1e4)\",\n", + " default_size=15,\n", + " xlim=(0, 800),\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "349", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/WHUHjH6.png)" + ] + }, + { + "cell_type": "markdown", + "id": "350", + "metadata": {}, + "source": [ + " #### Transect Line Plot\n", + "\n", + "Plots elevation along a transect line across a specific region, providing a profile view of terrain elevation." ] }, { "cell_type": "code", "execution_count": null, - "id": "126", + "id": "351", "metadata": {}, "outputs": [], "source": [ - "timelapse = geemap.sentinel2_timelapse(\n", - " roi,\n", - " out_gif=\"sentinel2.gif\",\n", - " start_year=2017,\n", - " end_year=2024,\n", - " start_date=\"01-01\",\n", - " end_date=\"12-31\",\n", - " frequency=\"year\",\n", - " bands=[\"SWIR1\", \"NIR\", \"Red\"],\n", - " frames_per_second=3,\n", - " title=\"Sentinel-2 Timelapse\",\n", + "# Define a line across the Olympic Peninsula, USA.\n", + "transect = ee.Geometry.LineString([[-122.8, 47.8], [-124.5, 47.8]])\n", + "\n", + "# Define a pixel coordinate image.\n", + "lat_lon_img = ee.Image.pixelLonLat()\n", + "\n", + "# Import a digital surface model and add latitude and longitude bands.\n", + "elev_img = ee.Image(\"USGS/SRTMGL1_003\").select(\"elevation\").addBands(lat_lon_img)\n", + "\n", + "# Reduce elevation and coordinate bands by transect line; get a dictionary with\n", + "# band names as keys, pixel values as lists.\n", + "elev_transect = elev_img.reduceRegion(\n", + " reducer=ee.Reducer.toList(),\n", + " geometry=transect,\n", + " scale=1000,\n", ")\n", - "geemap.show_image(timelapse)" + "\n", + "# Get longitude and elevation value lists from the reduction dictionary.\n", + "lon = ee.List(elev_transect.get(\"longitude\"))\n", + "elev = ee.List(elev_transect.get(\"elevation\"))\n", + "\n", + "# Sort the longitude and elevation values by ascending longitude.\n", + "lon_sort = lon.sort(lon)\n", + "elev_sort = elev.sort(lon)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "352", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.array_values(\n", + " elev_sort,\n", + " x_labels=lon_sort,\n", + " series_names=[\"Elevation\"],\n", + " chart_type=\"AreaChart\",\n", + " colors=[\"#1d6b99\"],\n", + " title=\"Elevation Profile Across Longitude\",\n", + " x_label=\"Longitude\",\n", + " y_label=\"Elevation (m)\",\n", + " stroke_width=5,\n", + " fill=\"bottom\",\n", + " fill_opacities=[0.4],\n", + " ylim=(0, 2500),\n", + ")\n", + "fig" ] }, { "cell_type": "markdown", - "id": "127", + "id": "353", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/k3XRita.png)" + ] + }, + { + "cell_type": "markdown", + "id": "354", "metadata": {}, "source": [ - "### MODIS vegetation indices" + "#### Metadata Scatter Plot\n", + "\n", + "Visualizes Landsat image metadata by plotting cloud cover against geometric RMSE, useful for quality assessment of image collections." ] }, { "cell_type": "code", "execution_count": null, - "id": "128", + "id": "355", "metadata": {}, "outputs": [], "source": [ - "Map = geemap.Map()\n", - "Map" + "# Import a Landsat 8 collection and filter to a single path/row.\n", + "col = ee.ImageCollection(\"LANDSAT/LC08/C02/T1_L2\").filter(\n", + " ee.Filter.expression(\"WRS_PATH == 45 && WRS_ROW == 30\")\n", + ")\n", + "\n", + "# Reduce image properties to a series of lists; one for each selected property.\n", + "propVals = col.reduceColumns(\n", + " reducer=ee.Reducer.toList().repeat(2),\n", + " selectors=[\"CLOUD_COVER\", \"GEOMETRIC_RMSE_MODEL\"],\n", + ").get(\"list\")\n", + "\n", + "# Get selected image property value lists; to be plotted along x and y axes.\n", + "x = ee.List(ee.List(propVals).get(0))\n", + "y = ee.List(ee.List(propVals).get(1))" ] }, { "cell_type": "code", "execution_count": null, - "id": "129", + "id": "356", "metadata": {}, "outputs": [], "source": [ - "roi = Map.user_roi\n", - "if roi is None:\n", - " roi = ee.Geometry.BBox(-18.6983, -36.1630, 52.2293, 38.1446)\n", - " Map.addLayer(roi)\n", - " Map.centerObject(roi)" + "colors = [geemap.hex_to_rgba(\"#96356f\", 0.4)]\n", + "print(colors)" ] }, { "cell_type": "code", "execution_count": null, - "id": "130", + "id": "357", "metadata": {}, "outputs": [], "source": [ - "timelapse = geemap.modis_ndvi_timelapse(\n", - " roi,\n", - " out_gif=\"ndvi.gif\",\n", - " data=\"Terra\",\n", - " band=\"NDVI\",\n", - " start_date=\"2000-01-01\",\n", - " end_date=\"2022-12-31\",\n", - " frames_per_second=3,\n", - " title=\"MODIS NDVI Timelapse\",\n", - " overlay_data=\"countries\",\n", + "fig = chart.array_values(\n", + " y,\n", + " x_labels=x,\n", + " series_names=[\"RMSE\"],\n", + " chart_type=\"ScatterChart\",\n", + " colors=colors,\n", + " title=\"Landsat 8 Image Collection Metadata (045030)\",\n", + " x_label=\"Cloud cover (%)\",\n", + " y_label=\"Geometric RMSE (m)\",\n", + " default_size=15,\n", ")\n", - "geemap.show_image(timelapse)" + "fig" ] }, { "cell_type": "markdown", - "id": "131", + "id": "358", "metadata": {}, "source": [ - "### MODIS temperature data" + "![](https://i.imgur.com/3COY3xd.png)" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "132", + "cell_type": "markdown", + "id": "359", "metadata": {}, - "outputs": [], "source": [ - "Map = geemap.Map()\n", - "Map" + "#### Mapped Function Scatter & Line Plot\n", + "\n", + "Plots a sine function as a line chart, mapping mathematical functions onto chart axes for visualization." ] }, { "cell_type": "code", "execution_count": null, - "id": "133", + "id": "360", "metadata": {}, "outputs": [], "source": [ - "roi = Map.user_roi\n", - "if roi is None:\n", - " roi = ee.Geometry.BBox(-171.21, -57.13, 177.53, 79.99)\n", - " Map.addLayer(roi)\n", - " Map.centerObject(roi)" + "import math\n", + "\n", + "start = -2 * math.pi\n", + "end = 2 * math.pi\n", + "points = ee.List.sequence(start, end, None, 50)\n", + "\n", + "\n", + "def sin_func(val):\n", + " return ee.Number(val).sin()\n", + "\n", + "\n", + "values = points.map(sin_func)" ] }, { "cell_type": "code", "execution_count": null, - "id": "134", + "id": "361", "metadata": {}, "outputs": [], "source": [ - "timelapse = geemap.modis_ocean_color_timelapse(\n", - " satellite=\"Aqua\",\n", - " start_date=\"2018-01-01\",\n", - " end_date=\"2020-12-31\",\n", - " roi=roi,\n", - " frequency=\"month\",\n", - " out_gif=\"temperature.gif\",\n", - " overlay_data=\"continents\",\n", - " overlay_color=\"yellow\",\n", - " overlay_opacity=0.5,\n", + "fig = chart.array_values(\n", + " values,\n", + " points,\n", + " chart_type=\"LineChart\",\n", + " colors=[\"#39a8a7\"],\n", + " title=\"Sine Function\",\n", + " x_label=\"radians\",\n", + " y_label=\"sin(x)\",\n", + " marker=\"circle\",\n", ")\n", - "geemap.show_image(timelapse)" + "fig" ] }, { "cell_type": "markdown", - "id": "135", + "id": "362", "metadata": {}, "source": [ - "### GOES timelapse" + "![](https://i.imgur.com/7qcxvey.png)\n", + "\n", + "### Charting Data Table\n", + "\n", + "#### Manual DataTable chart\n", + "\n", + "Creates a chart from a manually created DataTable, such as US state populations." ] }, { "cell_type": "code", "execution_count": null, - "id": "136", + "id": "363", "metadata": {}, "outputs": [], "source": [ - "roi = ee.Geometry.BBox(167.1898, -28.5757, 202.6258, -12.4411)\n", - "start_date = \"2022-01-15T03:00:00\"\n", - "end_date = \"2022-01-15T07:00:00\"\n", - "data = \"GOES-17\"\n", - "scan = \"full_disk\"" + "import pandas as pd" ] }, { "cell_type": "code", "execution_count": null, - "id": "137", + "id": "364", "metadata": {}, "outputs": [], "source": [ - "timelapse = geemap.goes_timelapse(\n", - " roi, \"goes.gif\", start_date, end_date, data, scan, framesPerSecond=5\n", - ")\n", - "geemap.show_image(timelapse)" + "data = {\n", + " \"State\": [\"CA\", \"NY\", \"IL\", \"MI\", \"OR\"],\n", + " \"Population\": [37253956, 19378102, 12830632, 9883640, 3831074],\n", + "}\n", + "\n", + "df = pd.DataFrame(data)\n", + "df" ] }, { "cell_type": "code", "execution_count": null, - "id": "138", + "id": "365", "metadata": {}, "outputs": [], "source": [ - "roi = ee.Geometry.BBox(-159.5954, 24.5178, -114.2438, 60.4088)\n", - "start_date = \"2021-10-24T14:00:00\"\n", - "end_date = \"2021-10-25T01:00:00\"\n", - "data = \"GOES-17\"\n", - "scan = \"full_disk\"" + "fig = chart.Chart(\n", + " df,\n", + " x_cols=[\"State\"],\n", + " y_cols=[\"Population\"],\n", + " chart_type=\"ColumnChart\",\n", + " colors=[\"#1d6b99\"],\n", + " title=\"State Population (US census, 2010)\",\n", + " x_label=\"State\",\n", + " y_label=\"Population\",\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "366", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/vuxNmuh.png)" + ] + }, + { + "cell_type": "markdown", + "id": "367", + "metadata": {}, + "source": [ + "#### Computed DataTable chart\n", + "\n", + "Computes a DataTable from MODIS vegetation indices data for a forest area, then charts NDVI and EVI time series." ] }, { "cell_type": "code", "execution_count": null, - "id": "139", + "id": "368", "metadata": {}, "outputs": [], "source": [ - "timelapse = geemap.goes_timelapse(\n", - " roi, \"hurricane.gif\", start_date, end_date, data, scan, framesPerSecond=5\n", + "# Import the example feature collection and subset the forest feature.\n", + "forest = ee.FeatureCollection(\"projects/google/charts_feature_example\").filter(\n", + " ee.Filter.eq(\"label\", \"Forest\")\n", ")\n", - "geemap.show_image(timelapse)" + "\n", + "# Load MODIS vegetation indices data and subset a decade of images.\n", + "veg_indices = (\n", + " ee.ImageCollection(\"MODIS/061/MOD13A1\")\n", + " .filter(ee.Filter.date(\"2010-01-01\", \"2020-01-01\"))\n", + " .select([\"NDVI\", \"EVI\"])\n", + ")\n", + "\n", + "# Build a feature collection where each feature has a property that represents\n", + "# a DataFrame row.\n", + "\n", + "\n", + "def aggregate(img):\n", + " # Reduce the image to the mean of pixels intersecting the forest ecoregion.\n", + " stat = img.reduceRegion(\n", + " **{\"reducer\": ee.Reducer.mean(), \"geometry\": forest, \"scale\": 500}\n", + " )\n", + "\n", + " # Extract the reduction results along with the image date.\n", + " date = geemap.image_date(img)\n", + " evi = stat.get(\"EVI\")\n", + " ndvi = stat.get(\"NDVI\")\n", + "\n", + " # Make a list of observation attributes to define a row in the DataTable.\n", + " row = ee.List([date, evi, ndvi])\n", + "\n", + " # Return the row as a property of an ee.Feature.\n", + " return ee.Feature(None, {\"row\": row})\n", + "\n", + "\n", + "reduction_table = veg_indices.map(aggregate)\n", + "\n", + "# Aggregate the 'row' property from all features in the new feature collection\n", + "# to make a server-side 2-D list (DataTable).\n", + "data_table_server = reduction_table.aggregate_array(\"row\")\n", + "\n", + "# Define column names and properties for the DataTable. The order should\n", + "# correspond to the order in the construction of the 'row' property above.\n", + "column_header = ee.List([[\"Date\", \"EVI\", \"NDVI\"]])\n", + "\n", + "# Concatenate the column header to the table.\n", + "data_table_server = column_header.cat(data_table_server)" ] }, { "cell_type": "code", "execution_count": null, - "id": "140", + "id": "369", "metadata": {}, "outputs": [], "source": [ - "roi = ee.Geometry.BBox(-121.0034, 36.8488, -117.9052, 39.0490)\n", - "start_date = \"2020-09-05T15:00:00\"\n", - "end_date = \"2020-09-06T02:00:00\"\n", - "data = \"GOES-17\"\n", - "scan = \"full_disk\"" + "data_table = chart.DataTable(data_table_server, date_column=\"Date\")\n", + "data_table.head()" ] }, { "cell_type": "code", "execution_count": null, - "id": "141", + "id": "370", "metadata": {}, "outputs": [], "source": [ - "timelapse = geemap.goes_fire_timelapse(\n", - " roi, \"fire.gif\", start_date, end_date, data, scan, framesPerSecond=5\n", + "fig = chart.Chart(\n", + " data_table,\n", + " chart_type=\"LineChart\",\n", + " x_cols=\"Date\",\n", + " y_cols=[\"EVI\", \"NDVI\"],\n", + " colors=[\"#e37d05\", \"#1d6b99\"],\n", + " title=\"Average Vegetation Index Value by Date for Forest\",\n", + " x_label=\"Date\",\n", + " y_label=\"Vegetation index (x1e4)\",\n", + " stroke_width=3,\n", + " legend_location=\"right\",\n", ")\n", - "geemap.show_image(timelapse)" + "fig" ] }, { "cell_type": "markdown", - "id": "142", + "id": "371", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/PWei7QC.png)" + ] + }, + { + "cell_type": "markdown", + "id": "372", + "metadata": {}, + "source": [ + "#### Interval chart\n", + "\n", + "Displays annual NDVI time series with variance, showing inter-annual vegetation change and variability across the year." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "373", "metadata": {}, + "outputs": [], "source": [ - "### Exercise 3 - Creating timelapse animations\n", + "# Define a point to extract an NDVI time series for.\n", + "geometry = ee.Geometry.Point([-121.679, 36.479])\n", + "\n", + "# Define a band of interest (NDVI), import the MODIS vegetation index dataset,\n", + "# and select the band.\n", + "band = \"NDVI\"\n", + "ndvi_col = ee.ImageCollection(\"MODIS/061/MOD13Q1\").select(band)\n", + "\n", + "# Map over the collection to add a day of year (doy) property to each image.\n", + "\n", + "\n", + "def set_doy(img):\n", + " doy = ee.Date(img.get(\"system:time_start\")).getRelative(\"day\", \"year\")\n", + " # Add 8 to day of year number so that the doy label represents the middle of\n", + " # the 16-day MODIS NDVI composite.\n", + " return img.set(\"doy\", ee.Number(doy).add(8))\n", + "\n", + "\n", + "ndvi_col = ndvi_col.map(set_doy)\n", + "\n", + "# Join all coincident day of year observations into a set of image collections.\n", + "distinct_doy = ndvi_col.filterDate(\"2013-01-01\", \"2014-01-01\")\n", + "filter = ee.Filter.equals(**{\"leftField\": \"doy\", \"rightField\": \"doy\"})\n", + "join = ee.Join.saveAll(\"doy_matches\")\n", + "join_col = ee.ImageCollection(join.apply(distinct_doy, ndvi_col, filter))\n", + "\n", + "# Calculate the absolute range, interquartile range, and median for the set\n", + "# of images composing each coincident doy observation group. The result is\n", + "# an image collection with an image representative per unique doy observation\n", + "# with bands that describe the 0, 25, 50, 75, 100 percentiles for the set of\n", + "# coincident doy images.\n", + "\n", + "\n", + "def cal_percentiles(img):\n", + " doyCol = ee.ImageCollection.fromImages(img.get(\"doy_matches\"))\n", + "\n", + " return doyCol.reduce(\n", + " ee.Reducer.percentile([0, 25, 50, 75, 100], [\"p0\", \"p25\", \"p50\", \"p75\", \"p100\"])\n", + " ).set({\"doy\": img.get(\"doy\")})\n", + "\n", + "\n", + "comp = ee.ImageCollection(join_col.map(cal_percentiles))\n", + "\n", + "# Extract the inter-annual NDVI doy percentile statistics for the\n", + "# point of interest per unique doy representative. The result is\n", + "# is a feature collection where each feature is a doy representative that\n", + "# contains a property (row) describing the respective inter-annual NDVI\n", + "# variance, formatted as a list of values.\n", "\n", - "Use the geemap timelapse GUI to create a timelapse animation for any location of your choice. Share the timelapse on social media and use the hashtag such as #EarthEngine and #geemap. See [this](https://i.imgur.com/YaCHvKC.gif) example.\n", "\n", - "![](https://i.imgur.com/ohrXeFC.png)" + "def order_percentiles(img):\n", + " stats = ee.Dictionary(\n", + " img.reduceRegion(\n", + " **{\"reducer\": ee.Reducer.first(), \"geometry\": geometry, \"scale\": 250}\n", + " )\n", + " )\n", + "\n", + " # Order the percentile reduction elements according to how you want columns\n", + " # in the DataTable arranged (x-axis values need to be first).\n", + " row = ee.List(\n", + " [\n", + " img.get(\"doy\"),\n", + " stats.get(band + \"_p50\"),\n", + " stats.get(band + \"_p0\"),\n", + " stats.get(band + \"_p25\"),\n", + " stats.get(band + \"_p75\"),\n", + " stats.get(band + \"_p100\"),\n", + " ]\n", + " )\n", + "\n", + " # Return the row as a property of an ee.Feature.\n", + " return ee.Feature(None, {\"row\": row})\n", + "\n", + "\n", + "reduction_table = comp.map(order_percentiles)\n", + "\n", + "# Aggregate the 'row' properties to make a server-side 2-D array (DataTable).\n", + "data_table_server = reduction_table.aggregate_array(\"row\")\n", + "\n", + "# Define column names and properties for the DataTable. The order should\n", + "# correspond to the order in the construction of the 'row' property above.\n", + "column_header = ee.List([[\"DOY\", \"median\", \"p0\", \"p25\", \"p75\", \"p100\"]])\n", + "\n", + "# Concatenate the column header to the table.\n", + "data_table_server = column_header.cat(data_table_server)" ] }, { "cell_type": "code", "execution_count": null, - "id": "143", + "id": "374", "metadata": {}, "outputs": [], "source": [ - "m = geemap.Map()\n", - "m.add_gui(\"timelapse\")\n", - "m" + "df = chart.DataTable(data_table_server)\n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "375", + "metadata": {}, + "outputs": [], + "source": [ + "fig = chart.Chart(\n", + " df,\n", + " chart_type=\"IntervalChart\",\n", + " x_cols=\"DOY\",\n", + " y_cols=[\"p0\", \"p25\", \"median\", \"p75\", \"p100\"],\n", + " title=\"Annual NDVI Time Series with Inter-Annual Variance\",\n", + " x_label=\"Day of Year\",\n", + " y_label=\"Vegetation index (x1e4)\",\n", + " stroke_width=1,\n", + " fill=\"between\",\n", + " fill_colors=[\"#b6d1c6\", \"#83b191\", \"#83b191\", \"#b6d1c6\"],\n", + " fill_opacities=[0.6] * 4,\n", + " labels=[\"p0\", \"p25\", \"median\", \"p75\", \"p100\"],\n", + " display_legend=True,\n", + " legend_location=\"top-right\",\n", + " ylim=(0, 10000),\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "376", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/i8ZrGPR.png)" + ] + }, + { + "cell_type": "markdown", + "id": "377", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This lecture introduced **geemap**, a Python library designed to streamline geospatial data visualization and analysis in the **Google Earth Engine (GEE)** environment. Geemap enables users to perform GIS and remote sensing tasks with GEE data interactively in Python notebooks. Key topics included setting up GEE with geemap, loading and manipulating data from the Earth Engine Data Catalog, and processing vector and raster data. Techniques for creating animated timelapses using data from Landsat, Sentinel, MODIS, and GOES illustrated dynamic landscape monitoring, while interactive charting tools, like scatter plots and time series, supported data exploration and trend analysis. The lecture also covered exporting images and features in multiple formats, such as GeoTIFF, Shapefile, and CSV, to facilitate data sharing. Overall, this session provided a foundational understanding of geemap's capabilities, enabling users to efficiently visualize, analyze, and derive insights from Earth observation data in a Python setting." ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "geo", "language": "python", "name": "python3" }, @@ -2019,7 +5204,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.12.7" } }, "nbformat": 4, diff --git a/book/geospatial/geemap.md b/book/geospatial/geemap.md index b73e59d..ced17d0 100644 --- a/book/geospatial/geemap.md +++ b/book/geospatial/geemap.md @@ -4,9 +4,9 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.16.2 + jupytext_version: 1.16.4 kernelspec: - display_name: Python 3 + display_name: geo language: python name: python3 --- @@ -15,170 +15,376 @@ kernelspec: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/giswqs/geog-312/blob/main/book/geospatial/geemap.ipynb) -## Introduction +## Overview -This notebook is designed for users who are new to Google Earth Engine and geemap. It provides an introduction to Earth Engine and geemap, and demonstrates how to use geemap to explore and analyze Earth Engine data. +This lecture introduces cloud-based geospatial analysis using the [Google Earth Engine](https://earthengine.google.com) (GEE) API in combination with the [geemap](https://geemap.org) Python package. We will cover core concepts of Earth Engine, visualization techniques, and practical workflows to perform analyses within a Jupyter environment. + +## Learning Objectives + +By the end of this lecture, you will be able to: +* Explain the fundamentals of the Google Earth Engine platform, including its data types and cloud-based capabilities +* Set up and configure the geemap library for conducting geospatial analyses in the cloud +* Perform essential geospatial operations, including filtering, visualizing, and exporting data from Earth Engine +* Access and manipulate both raster and vector data using Earth Engine +* Create timelapse animations from satellite imagery +* Create interactive charts from Earth Engine data ### Prerequisites -- To use geemap and the Earth Engine Python API, you must [register](https://code.earthengine.google.com/register) for an Earth Engine account and follow the instructions [here](https://docs.google.com/document/d/1ZGSmrNm6_baqd8CHt33kIBWOlvkh-HLr46bODgJN1h0/edit?usp=sharing) to create a Cloud Project. Earth Engine is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). To test whether you can use authenticate the Earth Engine Python API, please run [this notebook](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/notebooks/geemap_colab.ipynb) on Google Colab. +Before beginning with geemap and Google Earth Engine, ensure the following steps are completed: -- 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 short course without installing anything on their computer. +- **Register for a Google Earth Engine account**: Go to [https://code.earthengine.google.com/register](https://code.earthengine.google.com/register) to sign up for a GEE account. After registering, you will need to set up a Google Cloud Project and enable the Earth Engine API by following the instructions [here](https://docs.google.com/document/d/1ZGSmrNm6_baqd8CHt33kIBWOlvkh-HLr46bODgJN1h0/edit?usp=sharing). GEE is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). +- **Verify API Authentication**: To confirm that your Earth Engine account is set up correctly, try running [this test notebook](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/notebooks/geemap_colab.ipynb) in Google Colab. -### Agenda +## Introduction to Google Earth Engine -The main topics to be covered in this workshop include: +### Google Earth Engine Overview -* Create interactive maps -* Visualize Earth Engine data -* Explore Earth Engine Data Catalogs -* Analyze Earth Engine data -* Export Earth Engine data -* Create timelapse animations +[Google Earth Engine](https://earthengine.google.com) 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](https://earthengine.google.com/noncommercial). 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](https://earthengine.google.com/commercial) may require a paid license. -## Introduction to Earth Engine and geemap +### Installing Geemap -Earth Engine is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). For more than a decade, Earth Engine has enabled planetary-scale Earth data science and analysis by nonprofit organizations, research scientists, and other impact users. +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: + +```{code-cell} ipython3 +# %pip install -U "geemap[workshop]" +``` -With the launch of Earth Engine for [commercial use](https://earthengine.google.com/commercial), commercial customers will be charged for Earth Engine services. However, Earth Engine will remain free of charge for noncommercial use and research projects. Nonprofit organizations, academic institutions, educators, news media, Indigenous governments, and government researchers are eligible to use Earth Engine free of charge, just as they have done for over a decade. +### Import Libraries -The geemap Python package is built upon the Earth Engine Python API and open-source mapping libraries. It allows Earth Engine users to interactively manipulate, analyze, and visualize geospatial big data in a Jupyter environment. Since its creation in April 2020, geemap has received over 3,300 GitHub stars and is being used by over 2,700 projects on GitHub. +To start, import the necessary libraries for working with Google Earth Engine (GEE) and geemap. -## Google Colab and Earth Engine Python API authentication +```{code-cell} ipython3 +import ee +import geemap +``` -[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/workshops/Taiwan_2024.ipynb) +### Authenticate and Initialize Earth Engine -### Change Colab dark theme +To authenticate and initialize your Earth Engine environment, you’ll need to create a Google Cloud Project and enable the [Earth Engine API](https://console.cloud.google.com/apis/api/earthengine.googleapis.com) for the project if you have not done so already. You can find detailed setup instructions [here](https://developers.google.com/earth-engine/guides/access#a-role-in-a-cloud-project). -Currently, ipywidgets does not work well with Colab dark theme. Some of the geemap widgets may not display properly in Colab dark theme.It is recommended that you change Colab to the light theme. +```{code-cell} ipython3 +geemap.ee_initialize() +``` -![](https://i.imgur.com/EJ0GDP8.png) +Running the code above will prompt you to authenticate your Earth Engine account. Follow the instructions to complete the authentication process on Colab. To avoid this step in the future, you can set up a Colab secret with the name `EARTHENGINE_TOKEN` and your Earth Engine token as the value, which can be obtained by running `geemap.get_ee_token()`. +```{code-cell} ipython3 +# geemap.get_ee_token() +``` -### Install geemap +### Creating Interactive Maps -The geemap package is pre-installed in Google Colab and is updated to the latest minor or major release every few weeks. Some optional dependencies of geemap being used by this notebook are not pre-installed in Colab. Uncomment the following code block to install geemap and some optional dependencies. +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`. ```{code-cell} ipython3 -# %pip install -U "geemap[workshop]" +m = geemap.Map() ``` -Note that some geemap features may not work properly in the Google Colab environmennt. If you are familiar with [Anaconda](https://www.anaconda.com/download) or [Miniconda](https://docs.anaconda.com/free/miniconda), it is recommended to create a new conda environment to install geemap and its optional dependencies on your local computer. +To display the map in a Jupyter notebook, simply enter the map object: -```bash -conda create -n gee python=3.11 -conda activate gee -conda install -c conda-forge mamba -mamba install -c conda-forge geemap pygis +```{code-cell} ipython3 +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`. + +#### Example Maps -### Import libraries +**Map of the Contiguous United States** -Import the earthengine-api and geemap. + To center the map on the contiguous United States, use: ```{code-cell} ipython3 -import ee -import geemap +m = geemap.Map(center=[40, -100], zoom=4, height="600px") +m +``` + +**Map of the state of Tennessee** + +```{code-cell} ipython3 +m = geemap.Map(center=[35.746512, -86.209818], zoom=8) +m +``` + +**Map of the city of Knoxville, TN** + +```{code-cell} ipython3 +m = geemap.Map(center=[35.959111, -83.909463], zoom=13) +m +``` + +To hide specific map controls, set the corresponding control argument to `False`, such as `draw_ctrl=False` to hide the drawing control. + +```{code-cell} ipython3 +m = geemap.Map(data_ctrl=False, toolbar_ctrl=False, draw_ctrl=False) +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. + +```{code-cell} ipython3 +m = geemap.Map(basemap="Esri.WorldImagery") +m +``` + +#### Adding Multiple Basemaps + +You can add multiple basemaps to a map by calling `add_basemap` multiple times. For example, the following code adds the `Esri.WorldTopoMap` and `OpenTopoMap` basemaps to the existing map: + +```{code-cell} ipython3 +m.add_basemap("Esri.WorldTopoMap") +``` + +```{code-cell} ipython3 +m.add_basemap("OpenTopoMap") ``` -### Authenticate and initialize Earth Engine +#### Listing Available Basemaps -You will need to create a [Google Cloud Project](https://console.cloud.google.com/projectcreate) and enable the [Earth Engine API](https://console.cloud.google.com/apis/api/earthengine.googleapis.com) for the project. You can find detailed instructions [here](https://book.geemap.org/chapters/01_introduction.html#earth-engine-authentication). +To view the first 10 available basemaps, use: ```{code-cell} ipython3 -ee.Authenticate() +basemaps = list(geemap.basemaps.keys()) +len(geemap.basemaps) ``` ```{code-cell} ipython3 -ee.Initialize(project="YOUR-PROJECT-ID") +basemaps[:10] +``` + +### Google Basemaps + +Due to licensing restrictions, Google basemaps are not included in geemap by default. However, users can add Google basemaps manually at their own discretion using the following URLs: + +```text + ROADMAP: https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z} + SATELLITE: https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z} + TERRAIN: https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z} + HYBRID: https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z} +``` + +For example, to add Google Satellite as a tile layer: + +```{code-cell} ipython3 +m = geemap.Map() +url = "https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}" +m.add_tile_layer(url, name="Google Satellite", attribution="Google") +m ``` -## Creating interactive maps +## Introduction to Interactive Maps and Tools -Let's create an interactive map using the `ipyleaflet` plotting backend. The [`geemap.Map`](https://geemap.org/geemap/#geemap.geemap.m) class inherits the [`ipyleaflet.Map`](https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/map.html) class. Therefore, you can use the same syntax to create an interactive map as you would with `ipyleaflet.Map`. +### Basemap Selector + +The basemap selector allows you to choose from various basemaps via a dropdown menu. Adding it to your map provides easy access to different map backgrounds. ```{code-cell} ipython3 m = geemap.Map() +m.add("basemap_selector") +m ``` -To display it in a Jupyter notebook, simply ask for the object representation: +### 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. ```{code-cell} ipython3 +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 ``` -To customize the map, you can specify various keyword arguments, such as `center` ([lat, lon]), `zoom`, `width`, and `height`. The default `width` is `100%`, which takes up the entire cell width of the Jupyter notebook. The `height` argument accepts a number or a string. If a number is provided, it represents the height of the map in pixels. If a string is provided, the string must be in the format of a number followed by `px`, e.g., `600px`. +### 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. ```{code-cell} ipython3 -m = geemap.Map(center=[40, -100], zoom=4, height="600xp") +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 ``` -To hide a control, set `control_name` to `False`, e.g., `draw_ctrl=False`. +### Layer Editor + +With the layer editor, you can adjust visualization parameters of Earth Engine data for better clarity and focus. It supports single-band images, multi-band images, and feature collections. + +#### Single-Band image ```{code-cell} ipython3 -m = geemap.Map(data_ctrl=False, toolbar_ctrl=False, draw_ctrl=False) +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 ``` -### Adding basemaps +#### Multi-Band image -There are several ways to add basemaps to a map. You can specify the basemap to use in the `basemap` keyword argument when creating the map. Alternatively, you can add basemap layers to the map using the `add_basemap` method. Geemap has hundreds of built-in basemaps available that can be easily added to the map with only one line of code. +```{code-cell} ipython3 +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 +``` -Create a map by specifying the basemap to use as follows. For example, the `Esri.WorldImagery` basemap represents the Esri world imagery basemap. +#### Feature Collection ```{code-cell} ipython3 -m = geemap.Map(basemap="Esri.WorldImagery") +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 ``` -You can add as many basemaps as you like to the map. For example, the following code adds the `OpenTopoMap` basemap to the map above: +### 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()`, use `m.user_roi` +- To return all drawn features as an `ee.FeatureCollection()`, use `m.user_rois` ```{code-cell} ipython3 -m.add_basemap("OpenTopoMap") +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 +``` + +```{code-cell} ipython3 +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](https://developers.google.com/earth-engine/datasets) hosts an extensive collection of geospatial datasets. Currently, he catalog includes over [1,000 datasets](https://github.com/opengeos/Earth-Engine-Catalog/blob/master/gee_catalog.tsv) 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](https://github.com/giswqs/Earth-Engine-Catalog/blob/master/gee_catalog.tsv). + +### 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](https://developers.google.com/earth-engine/datasets/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](https://developers.google.com/earth-engine/datasets/catalog/USGS_SRTMGL1_003#description) dataset. + +```{code-cell} ipython3 +m = geemap.Map() +m ``` -You can also change basemaps interactively using the basemap GUI. +![](https://i.imgur.com/B3rf4QN.jpg) + +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. + +### Using the Datasets Module in Geemap + +Geemap offers a built-in `datasets` module to access specific datasets programmatically. For example, to access the USGS GAP Alaska dataset: + +```{code-cell} ipython3 +from geemap.datasets import DATA +``` ```{code-cell} ipython3 m = geemap.Map() -m.add("basemap_selector") +dataset = ee.Image(DATA.USGS_GAP_AK_2001) +m.add_layer(dataset, {}, "GAP Alaska") +m.centerObject(dataset, zoom=4) m ``` -## Using Earth Engine data +To retrieve metadata for a specific dataset, use the `get_metadata` function: + +```{code-cell} ipython3 +from geemap.datasets import get_metadata + +get_metadata(DATA.USGS_GAP_AK_2001) +``` + +## Earth Engine Data Types -### 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. -Earth Engine objects are server-side objects rather than client-side objects, which means that they are not stored locally on your computer. Similar to video streaming services (e.g., YouTube, Netflix, and Hulu), which store videos/movies on their servers, Earth Engine data are stored on the Earth Engine servers. We can stream geospatial data from Earth Engine on-the-fly without having to download the data just like we can watch videos from streaming services using a web browser without having to download the entire video to your computer. +- **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. -- **Image**: the fundamental raster data type in Earth Engine. -- **ImageCollection**: a stack or time-series of images. -- **Geometry**: the fundamental vector data type in Earth Engine. -- **Feature**: a Geometry with attributes. -- **FeatureCollection**: a set of features. +## Earth Engine Raster Data ### Image -Raster data in Earth Engine are represented as **Image** objects. Images are composed of one or more bands and each band has its own name, data type, scale, mask and projection. Each image has metadata stored as a set of properties. +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 +#### 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: ```{code-cell} ipython3 image = ee.Image("USGS/SRTMGL1_003") image ``` -#### Visualizing Earth Engine images +#### Visualizing Earth Engine Images + +To visualize an image, you can specify visualization parameters such as minimum and maximum values and color palettes. ```{code-cell} ipython3 -m = geemap.Map(center=[23.5790, 121.4181], zoom=8) +m = geemap.Map(center=[21.79, 70.87], zoom=3) image = ee.Image("USGS/SRTMGL1_003") vis_params = { "min": 0, @@ -191,28 +397,44 @@ m ### ImageCollection -An `ImageCollection` is a stack or sequence of images. An `ImageCollection` can be loaded by passing an Earth Engine asset ID into the `ImageCollection` constructor. You can find `ImageCollection` IDs in the [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets). +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](https://developers.google.com/earth-engine/datasets). -#### Loading image collections +#### Loading Image Collections -For example, to load the image collection of the [Sentinel-2 surface reflectance](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR): +To load an ImageCollection, such as the Sentinel-2 surface reflectance collection: ```{code-cell} ipython3 -collection = ee.ImageCollection("COPERNICUS/S2_SR") +collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") ``` -#### Visualizing image collections +#### Filtering Image Collections -To visualize an Earth Engine **ImageCollection**, we need to convert an **ImageCollection** to an **Image** by compositing all the images in the collection to a single image representing, for example, the min, max, median, mean or standard deviation of the images. For example, to create a median value image from a collection, use the `collection.median()` method. Let's create a median image from the Sentinel-2 surface reflectance collection: +You can filter ImageCollections by location and time. For example, to filter images covering a specific location with low cloud cover: ```{code-cell} ipython3 -m = geemap.Map() -collection = ( - ee.ImageCollection("COPERNICUS/S2_SR") - .filterDate("2023-01-01", "2024-01-01") +geometry = ee.Geometry.Point([-83.909463, 35.959111]) +images = collection.filterBounds(geometry) +images.size() +``` + +```{code-cell} ipython3 +images.first() +``` + +```{code-cell} ipython3 +images = ( + collection.filterBounds(geometry) + .filterDate("2024-07-01", "2024-10-01") .filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 5)) ) -image = collection.median() +images.size() +``` + +To view the filtered collection on a map: + +```{code-cell} ipython3 +m = geemap.Map() +image = images.first() vis = { "min": 0.0, @@ -220,189 +442,138 @@ vis = { "bands": ["B4", "B3", "B2"], } -m.set_center(121.4181, 23.5790, 8) m.add_layer(image, vis, "Sentinel-2") +m.centerObject(image, 8) m ``` -### FeatureCollection - -A **FeatureCollection** is a collection of Features. A FeatureCollection is analogous to a GeoJSON FeatureCollection object, i.e., a collection of features with associated properties/attributes. Data contained in a shapefile can be represented as a FeatureCollection. +#### Visualizing Image Collections -#### Loading feature collections - -The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) hosts a variety of vector datasets (e.g,, US Census data, country boundaries, and more) as feature collections. You can find feature collection IDs by searching the data catalog. For example, to load the [TIGER roads data](https://developers.google.com/earth-engine/datasets/catalog/TIGER_2016_Roads) by the U.S. Census Bureau: +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. ```{code-cell} ipython3 m = geemap.Map() -fc = ee.FeatureCollection("TIGER/2016/Roads") -m.set_center(-73.9596, 40.7688, 12) -m.add_layer(fc, {}, "Census roads") -m -``` - -#### Filtering feature collections +image = images.median() -* [geoBoundaries: Political administrative boundaries at Country level (ADM0)](https://developers.google.com/earth-engine/datasets/catalog/WM_geoLab_geoBoundaries_600_ADM0) -* [geoBoundaries: Political administrative boundaries at District level (ADM1)](https://developers.google.com/earth-engine/datasets/catalog/WM_geoLab_geoBoundaries_600_ADM1) +vis = { + "min": 0.0, + "max": 3000, + "bands": ["B8", "B4", "B3"], +} -```{code-cell} ipython3 -m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM0") -fc = countries.filter(ee.Filter.eq("shapeName", "Taiwan")) -m.add_layer(fc, {}, "Taiwan") -m.center_object(fc, 8) +m.add_layer(image, vis, "Sentinel-2") +m.centerObject(geometry, 8) m ``` -```{code-cell} ipython3 -m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM1") -fc = countries.filter(ee.Filter.eq("shapeGroup", "TWN")) -m.add_layer(fc, {}, "Taiwan") -m.center_object(fc, 8) -m -``` +## Earth Engine Vector Data -```{code-cell} ipython3 -region = m.user_roi -if region is None: - region = ee.Geometry.BBox(119.1687, 21.7799, 122.981, 25.4234) +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. -fc = fc.filterBounds(region) -m.add_layer(fc, {}, "Taiwan 2") -m.center_object(fc, 8) -``` +### Loading Feature Collections -#### Visualizing feature collections +The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) 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](https://developers.google.com/earth-engine/datasets/catalog/TIGER_2016_Roads) by the U.S. Census Bureau: ```{code-cell} ipython3 m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM1") -fc = countries.filter(ee.Filter.eq("shapeGroup", "TWN")) -m.add_layer(fc, {}, "Taiwan") -m.center_object(fc, 8) +fc = ee.FeatureCollection("TIGER/2016/Roads") +m.set_center(-83.909463, 35.959111, 12) +m.add_layer(fc, {}, "Census roads") m ``` -```{code-cell} ipython3 -m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM1") -fc = countries.filter(ee.Filter.eq("shapeGroup", "TWN")) -style = {"color": "000000ff", "width": 2, "lineType": "solid", "fillColor": "FF000000"} -m.add_layer(fc.style(**style), {}, "Taiwan") -m.center_object(fc, 8) -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": ```{code-cell} ipython3 m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM1") -fc = countries.filter(ee.Filter.eq("shapeGroup", "TWN")) -style = {"color": "0000ffff", "width": 2, "lineType": "solid", "fillColor": "FF000080"} -m.add_layer(fc.style(**style), {}, "Taiwan") -m.center_object(fc, 8) +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 ``` -### Earth Engine Data Catalog - -The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) hosts a variety of geospatial datasets. As of July 2024, the catalog contains over [1,100 datasets](https://github.com/opengeos/Earth-Engine-Catalog/blob/master/gee_catalog.tsv) with a total size of over 100 petabytes. Some notable datasets include: Landsat, Sentinel, MODIS, NAIP, etc. For a complete list of datasets in CSV or JSON formats, see the [Earth Engine Datasets List](https://github.com/giswqs/Earth-Engine-Catalog/blob/master/gee_catalog.tsv). +To retrieve properties of the first feature in the collection, use: -#### Searching for datasets - -The [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets/catalog) is searchable. You can search datasets by name, keyword, or tag. For example, enter "elevation" in the search box will filter the catalog to show only datasets containing "elevation" in their name, description, or tags. 52 datasets are returned for this search query. Scroll down the list to find the [NASA SRTM Digital Elevation 30m](https://developers.google.com/earth-engine/datasets/catalog/USGS_SRTMGL1_003#description) dataset. On each dataset page, you can find the following information, including Dataset Availability, Dataset Provider, Earth Engine Snippet, Tags, Description, Code Example, and more. One important piece of information is the Image/ImageCollection/FeatureCollection ID of each dataset, which is essential for accessing the dataset through the Earth Engine JavaScript or Python APIs. +```{code-cell} ipython3 +feat = fc.first() +feat.toDictionary() +``` -![](https://i.imgur.com/B3rf4QN.jpg) +You can also convert a FeatureCollection to a Pandas DataFrame for easier analysis with: ```{code-cell} ipython3 -m = geemap.Map() -m +geemap.ee_to_df(fc) ``` ```{code-cell} ipython3 m = geemap.Map() -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") +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 ``` ```{code-cell} ipython3 -m = geemap.Map() -counties = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM0") -fc = counties.filter(ee.Filter.eq("shapeName", "Taiwan")) -dem = ee.Image("USGS/SRTMGL1_003").clipToCollection(fc) -vis_params = { - "min": 0, - "max": 4000, - "palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"], -} +region = m.user_roi +if region is None: + region = ee.Geometry.BBox(-88.40, 29.88, -77.90, 35.39) -m.add_layer(fc, {}, "Taiwan") -m.add_layer(dem, vis_params, "SRTM DEM") -m.center_object(fc, 8) -m +fc = ee.FeatureCollection("TIGER/2018/States").filterBounds(region) +m.add_layer(fc, {}, "Southeastern U.S.") +m.center_object(fc, 6) ``` -### Exercise 1 - Creating cloud-free imagery - -Create a cloud-free imagery of Taiwan for the year of 2023. You can use either Landsat 9 or Sentinel-2 imagery. Relevant Earth Engine assets: - -- [ee.FeatureCollection("TIGER/2018/States")](https://developers.google.com/earth-engine/datasets/catalog/TIGER_2018_States) -- [ee.ImageCollection("COPERNICUS/S2_SR")](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR) -- [ee.ImageCollection("LANDSAT/LC09/C02/T1_L2")](https://developers.google.com/earth-engine/datasets/catalog/LANDSAT_LC09_C02_T1_L2) - -A sample map of cloud-free imagery for the state of Texas is shown below: +### Visualizing Feature Collections -![](https://i.imgur.com/i3IT0lF.png) +Once loaded, feature collections can be visualized on an interactive map. For example, visualizing U.S. state boundaries from the census data: ```{code-cell} ipython3 -# Type your code here +m = geemap.Map(center=[40, -100], zoom=4) +states = ee.FeatureCollection("TIGER/2018/States") +m.add_layer(states, {}, "US States") +m ``` -## Visualizing Earth Engine data - -### Using the inspector tool - -Inspect pixel values and vector features using the inspector tool. +Feature collections can also be styled with additional parameters. To apply a custom style, specify options like color and line width: ```{code-cell} ipython3 -m = geemap.Map() - -counties = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM0") -fc = counties.filter(ee.Filter.eq("shapeName", "Taiwan")) +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 +``` -dem = ee.Image("USGS/SRTMGL1_003") -landsat7 = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003").select( - ["B1", "B2", "B3", "B4", "B5", "B7"] -) +Using `add_styled_vector`, you can apply a color palette to style different features by attribute: +```{code-cell} ipython3 +m = geemap.Map(center=[40, -100], zoom=4) +states = ee.FeatureCollection("TIGER/2018/States") vis_params = { - "min": 0, - "max": 4000, - "palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"], + "color": "000000", + "colorOpacity": 1, + "pointSize": 3, + "pointShape": "circle", + "width": 2, + "lineType": "solid", + "fillColorOpacity": 0.66, } - -m.add_layer( - landsat7, - {"bands": ["B4", "B3", "B2"], "min": 20, "max": 200, "gamma": 2.0}, - "Landsat 7", +palette = ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"] +m.add_styled_vector( + states, column="NAME", palette=palette, layer_name="Styled vector", **vis_params ) -m.add_layer(dem, vis_params, "SRTM DEM") -m.add_layer(fc, {}, "Taiwan") -m.add("inspector") -m.center_object(fc, 8) m ``` -### Using the plotting tool +## More Tools for Visualizing Earth Engine Data -Plot spectral profiles of pixels using the plotting tool. +### Using the Plotting Tool + +The plotting tool in geemap enables visualization of Earth Engine data layers. In this example, Landsat 7 and Hyperion data are added with specific visualization parameters, allowing for comparison of these datasets. The plot GUI is then added to facilitate detailed exploration of the data. ```{code-cell} ipython3 m = geemap.Map(center=[40, -100], zoom=4) @@ -425,91 +596,126 @@ hyperion_vis = { } m.add_layer(hyperion, hyperion_vis, "Hyperion") m.add_plot_gui() -m.center_object(fc, 8) m ``` -Set plotting options for Landsat. +Set the plotting options for Landsat to add marker clusters and overlays, enhancing the interactivity of plotted data on the map. ```{code-cell} ipython3 m.set_plot_options(add_marker_cluster=True, overlay=True) ``` -Set plotting options for Hyperion. +Adjust the plotting options for Hyperion data by setting a bar plot type with marker clusters, suitable for visualizing Hyperion’s data values in bar format. ```{code-cell} ipython3 m.set_plot_options(add_marker_cluster=True, plot_type="bar") ``` -### Legends, color bars, and labels +### Legends + +#### Built-in Legends -#### Built-in legends +Geemap provides built-in legends that can be easily added to maps for better interpretability of data classes. Here, an NLCD legend is added to both a WMS layer and an Earth Engine land cover layer, giving quick visual reference to the data's classification scheme. ```{code-cell} ipython3 from geemap.legends import builtin_legends ``` +Print out all available built-in legends, which can be used with various data layers to enhance map readability. + ```{code-cell} ipython3 for legend in builtin_legends: print(legend) ``` -Add ESA WorldCover and legend to the map. - -https://developers.google.com/earth-engine/datasets/catalog/ESA_WorldCover_v200 +Add an NLCD WMS layer along with its corresponding legend, which appears as an overlay, providing users with an informative legend display. ```{code-cell} ipython3 -m = geemap.Map() +m = geemap.Map(center=[40, -100], zoom=4) m.add_basemap("Esri.WorldImagery") - -dataset = ee.ImageCollection("ESA/WorldCover/v200").first() -visualization = {"bands": ["Map"]} -m.add_layer(dataset, visualization, "Landcover") -m.add_legend(title="Land Cover Type", builtin_legend="ESA_WorldCover") -m.set_center(121.4181, 23.5790, 8) +m.add_basemap("NLCD 2021 CONUS Land Cover") +m.add_legend(builtin_legend="NLCD", max_width="100px", height="455px") m ``` -#### Custom legends - -+++ - -Add a custom legend by specifying a dictionary of colors and labels. +Add an Earth Engine layer for NLCD land cover and display its legend, specifying title, legend type, and dimensions for user convenience. ```{code-cell} ipython3 -m = geemap.Map() +m = geemap.Map(center=[40, -100], zoom=4) +m.add_basemap("Esri.WorldImagery") + +nlcd = ee.Image("USGS/NLCD_RELEASES/2021_REL/NLCD/2021") +landcover = nlcd.select("landcover") + +m.add_layer(landcover, {}, "NLCD Land Cover 2021") +m.add_legend( + title="NLCD Land Cover Classification", builtin_legend="NLCD", height="455px" +) +m +``` + +#### Custom Legends + +Create a custom legend by defining unique labels and colors for each class, allowing for flexible map customization. This example uses a color palette for labeling several map categories. + +```{code-cell} ipython3 +m = geemap.Map(add_google_map=False) + +keys = ["One", "Two", "Three", "Four", "etc"] + +# colors can be defined using either hex code or RGB (0-255, 0-255, 0-255) +colors = ["#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072", "#80B1D3"] +# legend_colors = [(255, 0, 0), (127, 255, 0), (127, 18, 25), (36, 70, 180), (96, 68 123)] + +m.add_legend(keys=keys, colors=colors, position="bottomright") +m +``` + +Define a custom legend using a dictionary that links specific colors to labels. This approach provides flexibility to represent specific categories in the data, such as various land cover types. + +```{code-cell} ipython3 +m = geemap.Map(center=[40, -100], zoom=4) m.add_basemap("Esri.WorldImagery") -dataset = ee.ImageCollection("ESA/WorldCover/v200").first() -visualization = {"bands": ["Map"]} -m.add_layer(dataset, visualization, "Landcover") legend_dict = { - "10 Trees": "006400", - "20 Shrubland": "ffbb22", - "30 Grassland": "ffff4c", - "40 Cropland": "f096ff", - "50 Built-up": "fa0000", - "60 Barren / sparse vegetation": "b4b4b4", - "70 Snow and ice": "f0f0f0", - "80 Open water": "0064c8", - "90 Herbaceous wetland": "0096a0", - "95 Mangroves": "00cf75", - "100 Moss and lichen": "fae6a0", + "11 Open Water": "466b9f", + "12 Perennial Ice/Snow": "d1def8", + "21 Developed, Open Space": "dec5c5", + "22 Developed, Low Intensity": "d99282", + "23 Developed, Medium Intensity": "eb0000", + "24 Developed High Intensity": "ab0000", + "31 Barren Land (Rock/Sand/Clay)": "b3ac9f", + "41 Deciduous Forest": "68ab5f", + "42 Evergreen Forest": "1c5f2c", + "43 Mixed Forest": "b5c58f", + "51 Dwarf Scrub": "af963c", + "52 Shrub/Scrub": "ccb879", + "71 Grassland/Herbaceous": "dfdfc2", + "72 Sedge/Herbaceous": "d1d182", + "73 Lichens": "a3cc51", + "74 Moss": "82ba9e", + "81 Pasture/Hay": "dcd939", + "82 Cultivated Crops": "ab6c28", + "90 Woody Wetlands": "b8d9eb", + "95 Emergent Herbaceous Wetlands": "6c9fb8", } -m.add_legend(title="Land Cover Type", legend_dict=legend_dict) -m.set_center(121.4181, 23.5790, 8) + +nlcd = ee.Image("USGS/NLCD_RELEASES/2021_REL/NLCD/2021") +landcover = nlcd.select("landcover") + +m.add_layer(landcover, {}, "NLCD Land Cover 2021") +m.add_legend(title="NLCD Land Cover Classification", legend_dict=legend_dict) m ``` -#### Creating color bars +### Color Bars -Add a horizontal color bar. +Add a horizontal color bar representing elevation data. This example demonstrates how to add an SRTM elevation layer and overlay a color bar for quick visual reference of elevation values. ```{code-cell} ipython3 m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM0") -fc = countries.filter(ee.Filter.eq("shapeName", "Taiwan")) -dem = ee.Image("USGS/SRTMGL1_003").clipToCollection(fc) + +dem = ee.Image("USGS/SRTMGL1_003") vis_params = { "min": 0, "max": 4000, @@ -518,11 +724,10 @@ vis_params = { m.add_layer(dem, vis_params, "SRTM DEM") m.add_colorbar(vis_params, label="Elevation (m)", layer_name="SRTM DEM") -m.center_object(fc, 8) m ``` -Add a vertical color bar. +Add a vertical color bar for elevation data, adjusting its orientation and dimensions to fit the map layout. ```{code-cell} ipython3 m.add_colorbar( @@ -534,7 +739,7 @@ m.add_colorbar( ) ``` -Make the color bar background transparent. +Make the color bar’s background transparent, which improves visual integration with the map when adding color bars for data layers. ```{code-cell} ipython3 m.add_colorbar( @@ -547,54 +752,42 @@ m.add_colorbar( ) ``` -### Split-panel map and linked maps - -### Split-panel maps +### Split-panel Maps -Create a split map with basemaps. Note that ipyleaflet has a bug with the SplitControl. You can't pan the map, which should be resolved in the next ipyleaflet release. +Create a split-panel map with basemaps, allowing users to compare two different basemaps side by side. ```{code-cell} ipython3 m = geemap.Map() m.split_map(left_layer="Esri.WorldTopoMap", right_layer="OpenTopoMap") -m.set_center(121.4181, 23.5790, 8) m ``` -Create a split map with Earth Engine layers. +Use Earth Engine layers in a split-panel map to compare NLCD data from 2001 and 2021. This layout is effective for examining changes between datasets across time. ```{code-cell} ipython3 -m = geemap.Map() +m = geemap.Map(center=(40, -100), zoom=4, height=600) -esa_2020 = ee.ImageCollection("ESA/WorldCover/v100").first() -esa_2021 = ee.ImageCollection("ESA/WorldCover/v200").first() -visualization = {"bands": ["Map"]} +nlcd_2001 = ee.Image("USGS/NLCD_RELEASES/2019_REL/NLCD/2001").select("landcover") +nlcd_2021 = ee.Image("USGS/NLCD_RELEASES/2021_REL/NLCD/2021").select("landcover") -left_layer = geemap.ee_tile_layer(esa_2020, visualization, "Land Cover 2020") -right_layer = geemap.ee_tile_layer(esa_2021, visualization, "Land Cover 2021") +left_layer = geemap.ee_tile_layer(nlcd_2001, {}, "NLCD 2001") +right_layer = geemap.ee_tile_layer(nlcd_2021, {}, "NLCD 2021") -m.split_map( - left_layer, right_layer, left_label="Land Cover 2020", right_label="Land Cover 2021" -) -m.add_legend(title="Land Cover Type", builtin_legend="ESA_WorldCover") -m.set_center(121.4181, 23.5790, 8) +m.split_map(left_layer, right_layer) m ``` -### Linked maps +### Linked Maps -Create a 2x2 linked map for visualizing Sentinel-2 imagery with different band combinations. Note that this feature does not work properly with Colab. Panning one map would not pan other maps. +Set up a 2x2 grid of linked maps showing Sentinel-2 imagery in different band combinations, ideal for comparing multiple visual perspectives. Note that this feature may not work in Colab. ```{code-cell} ipython3 -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM0") -fc = countries.filter(ee.Filter.eq("shapeName", "Taiwan")) image = ( - ee.ImageCollection("COPERNICUS/S2") - .filterDate("2023-01-01", "2024-01-01") + ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") + .filterDate("2024-07-01", "2024-10-01") .filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 5)) - .filterBounds(fc) .map(lambda img: img.divide(10000)) .median() - .clipToCollection(fc) ) vis_params = [ @@ -614,9 +807,9 @@ labels = [ geemap.linked_maps( rows=2, cols=2, - height="400px", - center=[23.5790, 121.4181], - zoom=8, + height="300px", + center=[35.959111, -83.909463], + zoom=12, ee_objects=[image], vis_params=vis_params, labels=labels, @@ -624,11 +817,9 @@ geemap.linked_maps( ) ``` -### Timeseries inspector and time slider - -#### Timeseries inspector +### Timeseries Inspector -Check the available years of NLCD. +Retrieve the available years in the NLCD collection for setting up a timeseries. This step helps confirm available time points for inspecting changes over time. ```{code-cell} ipython3 m = geemap.Map(center=[40, -100], zoom=4) @@ -638,7 +829,7 @@ years = collection.aggregate_array("system:index").getInfo() years ``` -Create a timeseries inspector for NLCD. Note that ipyleaflet has a bug with the SplitControl. You can't pan the map, which should be resolved in a future ipyleaflet release. +Use a timeseries inspector to compare changes in NLCD data across different years. This tool is ideal for temporal data visualization, showing how land cover changes in an interactive format. ```{code-cell} ipython3 m.ts_inspector( @@ -653,11 +844,9 @@ m.ts_inspector( m ``` -#### Time slider +### Time Slider -Note that this feature may not work properly with in the Colab environment. Restart Colab runtime if the time slider does not work. - -Create a map for visualizing MODIS vegetation data. +Create a time slider to explore MODIS vegetation data, setting the slider to visualize NDVI values over a specific period. This feature allows users to track vegetation changes month-by-month. ```{code-cell} ipython3 m = geemap.Map() @@ -677,7 +866,7 @@ m.add_time_slider(collection, vis_params, time_interval=2) m ``` -Create a map for visualizing weather data. +Add a time slider for visualizing NOAA weather data over a 24-hour period, using color-coding to show temperature variations. The slider enables temporal exploration of hourly data. ```{code-cell} ipython3 m = geemap.Map() @@ -700,457 +889,2075 @@ m.add_time_slider(collection, vis_params, labels=labels, time_interval=1, opacit m ``` -Visualizing Sentinel-2 imagery +Add a time slider to visualize Sentinel-2 imagery with specific bands and cloud cover filtering. This feature enables temporal analysis of imagery data, allowing users to explore seasonal and other changes over time. ```{code-cell} ipython3 -m = geemap.Map(center=[37.75, -122.45], zoom=12) +m = geemap.Map() collection = ( - ee.ImageCollection("COPERNICUS/S2_SR") - .filterBounds(ee.Geometry.Point([-122.45, 37.75])) + ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") + .filterBounds(ee.Geometry.Point([-83.909463, 35.959111])) .filterMetadata("CLOUDY_PIXEL_PERCENTAGE", "less_than", 10) + .filter(ee.Filter.calendarRange(6, 8, "month")) ) vis_params = {"min": 0, "max": 4000, "bands": ["B8", "B4", "B3"]} m.add_time_slider(collection, vis_params) +m.set_center(-83.909463, 35.959111, 12) m ``` -### Exercise 2 - Visualizing satellite data for an area of interest - -Visualize the time series of [Sentinel-2](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR_HARMONIZED) imagery for an area of interest in Taiwan. You can use the following Earth Engine assets: - -- `ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")` - -+++ +## Processing of Vector Data -## Analyzing Earth Engine data +### From GeoJSON -### Zonal statistics +Load GeoJSON data into an Earth Engine FeatureCollection. This example retrieves countries data from a remote URL, converting it to a FeatureCollection and visualizing it with a specified style. ```{code-cell} ipython3 +in_geojson = "https://github.com/gee-community/geemap/blob/master/examples/data/countries.geojson" m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM1") -fc = countries.filter(ee.Filter.eq("shapeGroup", "TWN")) -# Add NASA SRTM -dem = ee.Image("USGS/SRTMGL1_003").clipToCollection(fc) -dem_vis = { - "min": 0, - "max": 4000, - "palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"], -} -m.add_layer(dem, dem_vis, "SRTM DEM") - -# Add 5-year Landsat TOA composite -landsat = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003").clipToCollection(fc) -landsat_vis = {"bands": ["B4", "B3", "B2"], "gamma": 1.4, "min": 20, "max": 150} -m.add_layer(landsat, landsat_vis, "Landsat", False) -m.add_layer(fc, {}, "Taiwan") -m.center_object(fc, 8) +fc = geemap.geojson_to_ee(in_geojson) +m.add_layer(fc.style(**{"color": "ff0000", "fillColor": "00000000"}), {}, "Countries") m ``` -```{code-cell} ipython3 -out_dem_stats = "dem_stats.csv" -geemap.zonal_stats(dem, fc, out_dem_stats, stat_type="MEAN", scale=30, return_fc=False) -``` +### From Shapefile -```{code-cell} ipython3 -geemap.csv_to_df(out_dem_stats).sort_values(by=["mean"]) -``` +Download and load a shapefile of country boundaries. The shapefile is converted to a FeatureCollection for visualization on the map. ```{code-cell} ipython3 -out_landsat_stats = "landsat_stats.csv" -geemap.zonal_stats( - landsat, - fc, - out_landsat_stats, - stat_type="MEAN", - scale=30, - return_fc=False, -) +url = "https://github.com/gee-community/geemap/blob/master/examples/data/countries.zip" +geemap.download_file(url, overwrite=True) ``` ```{code-cell} ipython3 -geemap.csv_to_df(out_landsat_stats) +in_shp = "countries.shp" +fc = geemap.shp_to_ee(in_shp) ``` -### Zonal statistics by group - ```{code-cell} ipython3 m = geemap.Map() -m.add_basemap("Esri.WorldImagery") -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM1") -fc = countries.filter(ee.Filter.eq("shapeGroup", "TWN")) -dataset = ee.ImageCollection("ESA/WorldCover/v200").first().clipToCollection(fc) -visualization = {"bands": ["Map"]} -m.add_layer(dataset, visualization, "Landcover") -m.add_legend(title="Land Cover Type", builtin_legend="ESA_WorldCover") -m.add_layer(fc, {}, "Taiwan") -m.set_center(121.4181, 23.5790, 8) +m.add_layer(fc, {}, "Countries") m ``` -```{code-cell} ipython3 -landcover_stats = "landcover_stats.csv" - -geemap.zonal_stats_by_group( - dataset, - fc, - landcover_stats, - stat_type="SUM", - denominator=1e6, - decimal_places=2, -) -``` +### From GeoDataFrame -```{code-cell} ipython3 -geemap.csv_to_df(landcover_stats) -``` +Read a shapefile into a GeoDataFrame using geopandas, then convert the GeoDataFrame to an Earth Engine FeatureCollection for mapping. ```{code-cell} ipython3 -landcover_stats = "landcover_stats_pct.csv" +import geopandas as gpd -geemap.zonal_stats_by_group( - dataset, - fc, - landcover_stats, - stat_type="PERCENTAGE", - denominator=1e6, - decimal_places=2, -) +gdf = gpd.read_file(in_shp) +fc = geemap.gdf_to_ee(gdf) ``` ```{code-cell} ipython3 -geemap.csv_to_df(landcover_stats) +m = geemap.Map() +m.add_layer(fc, {}, "Countries") +m ``` -## Exporting Earth Engine data +### To GeoJSON -### Exporting images +Filter U.S. state data to select Tennessee and save it as a GeoJSON file, which can be shared or used in other GIS tools. ```{code-cell} ipython3 m = geemap.Map() -roi = ee.Geometry.Point([121.615219, 25.041219]) -image = ( - ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") - .filterDate("2024-01-01", "2024-08-01") - .filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 10)) - .filterBounds(roi) - .sort("CLOUDY_PIXEL_PERCENTAGE") - .first() - .select(["B8", "B4", "B3"]) -) - -vis_params = {"min": 0, "max": 3000} - -m.add_layer(image, vis_params, "Sentinel-2") -m.center_object(roi, 8) +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 ``` ```{code-cell} ipython3 -region = ee.Geometry.BBox(121.3824, 24.9325, 121.6653, 25.1496) -fc = ee.FeatureCollection(region) -style = {"color": "ffff00ff", "fillColor": "00000000"} -m.add_layer(fc.style(**style), {}, "ROI") +geemap.ee_to_geojson(fc, filename="Tennessee.geojson") ``` -```{code-cell} ipython3 -geemap.ee_export_image(image, filename="sentinel-2.tif", scale=30, region=region) -``` +### To Shapefile -```{code-cell} ipython3 -geemap.ee_export_image_to_drive( - image, description="sentinel-2", folder="export", region=region, scale=30 -) -``` +Export the filtered Tennessee FeatureCollection to a shapefile format for offline use or compatibility with desktop GIS software. ```{code-cell} ipython3 -geemap.download_ee_image(image, "sentinel-2_10m.tif", region=region, scale=10) +geemap.ee_to_shp(fc, filename="Tennessee.shp") ``` -### Export image collections +### To GeoDataFrame -```{code-cell} ipython3 -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")) -) -``` +Convert an Earth Engine FeatureCollection to a GeoDataFrame for further analysis in Python or use in interactive maps. ```{code-cell} ipython3 -collection.aggregate_array("system:index") +gdf = geemap.ee_to_gdf(fc) +gdf ``` ```{code-cell} ipython3 -geemap.ee_export_image_collection(collection, out_dir="naip", scale=10) +gdf.explore() ``` +### To DataFrame + +Transform an Earth Engine FeatureCollection into a pandas DataFrame, which can then be used for data analysis in Python. + ```{code-cell} ipython3 -geemap.ee_export_image_collection_to_drive(collection, folder="export", scale=10) +df = geemap.ee_to_df(fc) +df ``` -### Exporting feature collections +### To CSV + +Export the FeatureCollection data to a CSV file, useful for spreadsheet applications and data reporting. ```{code-cell} ipython3 -m = geemap.Map() -countries = ee.FeatureCollection("WM/geoLab/geoBoundaries/600/ADM0") -fc = countries.filter(ee.Filter.eq("shapeName", "Taiwan")) -m.add_layer(fc, {}, "Taiwan") -m.center_object(fc, 8) -m +geemap.ee_to_csv(fc, filename="Indiana.csv") ``` +## Processing of Raster Data + +### Extract Pixel Values + +#### Extracting Values to Points + +Load and visualize SRTM DEM and Landsat 7 imagery. Points from a shapefile of U.S. cities are added, and the tool extracts DEM values to these points, saving the results in a shapefile. + ```{code-cell} ipython3 -geemap.ee_to_shp(fc, filename="Taiwan.shp") +m = geemap.Map(center=[40, -100], zoom=4) + +dem = ee.Image("USGS/SRTMGL1_003") +landsat7 = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003") + +vis_params = { + "min": 0, + "max": 4000, + "palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"], +} + +m.add_layer( + landsat7, + {"bands": ["B4", "B3", "B2"], "min": 20, "max": 200, "gamma": 2}, + "Landsat 7", +) +m.add_layer(dem, vis_params, "SRTM DEM", True, 1) +m ``` ```{code-cell} ipython3 -geemap.ee_export_vector(fc, filename="Taiwan.shp") +in_shp = "us_cities.shp" +url = "https://github.com/giswqs/data/raw/main/us/us_cities.zip" +geemap.download_file(url) ``` ```{code-cell} ipython3 -geemap.ee_to_geojson(fc, filename="Taiwan.geojson") +in_fc = geemap.shp_to_ee(in_shp) +m.add_layer(in_fc, {}, "Cities") ``` ```{code-cell} ipython3 -geemap.ee_to_csv(fc, filename="Taiwan.csv") +geemap.extract_values_to_points(in_fc, dem, out_fc="dem.shp") ``` ```{code-cell} ipython3 -gdf = geemap.ee_to_gdf(fc) -gdf +geemap.shp_to_gdf("dem.shp") ``` ```{code-cell} ipython3 -df = geemap.ee_to_df(fc) -df +geemap.extract_values_to_points(in_fc, landsat7, "landsat.csv") ``` ```{code-cell} ipython3 -geemap.ee_export_vector_to_drive( - fc, description="Alaska", fileFormat="SHP", folder="export" -) +geemap.csv_to_df("landsat.csv") ``` -## Creating timelapse animations +#### Extracting Pixel Values Along a Transect -### Landsat timelapse +Visualize SRTM DEM with a terrain basemap. Define a line transect, either interactively or manually, and extract elevation values along this line. Display the elevation profile in a line chart, then export it as a CSV file for further analysis. ```{code-cell} ipython3 -m = geemap.Map() -m.set_center(121.615219, 25.041219, 12) +m = geemap.Map(center=[40, -100], zoom=4) +m.add_basemap("TERRAIN") + +image = ee.Image("USGS/SRTMGL1_003") +vis_params = { + "min": 0, + "max": 4000, + "palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"], +} +m.add_layer(image, vis_params, "SRTM DEM", True, 0.5) m ``` ```{code-cell} ipython3 -roi = m.user_roi -if roi is None: - roi = ee.Geometry.BBox(121.3824, 24.9325, 121.6653, 25.1496) - m.add_layer(roi) - m.center_object(roi) +line = m.user_roi +if line is None: + line = ee.Geometry.LineString( + [[-120.2232, 36.3148], [-118.9269, 36.7121], [-117.2022, 36.7562]] + ) + m.add_layer(line, {}, "ROI") +m.centerObject(line) ``` ```{code-cell} ipython3 -timelapse = geemap.landsat_timelapse( - roi, - out_gif="Taiwan.gif", - start_year=1988, - end_year=2024, - start_date="01-01", - end_date="12-31", - bands=["SWIR1", "NIR", "Red"], - frames_per_second=5, - title="Taipei", - progress_bar_color="blue", - mp4=True, +reducer = "mean" +transect = geemap.extract_transect( + image, line, n_segments=100, reducer=reducer, to_pandas=True ) -geemap.show_image(timelapse) +transect ``` ```{code-cell} ipython3 -roi = ee.Geometry.BBox(113.8252, 22.1988, 114.0851, 22.3497) -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.line_chart( + data=transect, + x="distance", + y="mean", + markers=True, + x_label="Distance (m)", + y_label="Elevation (m)", + height=400, ) -geemap.show_image(timelapse) ``` ```{code-cell} ipython3 -roi = ee.Geometry.BBox(-115.5541, 35.8044, -113.9035, 36.5581) -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) +transect.to_csv("transect.csv") ``` -### Sentinel-2 timelapse +### Zonal Statistics + +#### Zonal Statistics with an Image and a Feature Collection + +This section demonstrates the use of zonal statistics to calculate the mean elevation values within U.S. state boundaries using NASA’s SRTM DEM data and a 5-year Landsat composite. The `geemap.zonal_stats` function exports results to CSV files for analysis. ```{code-cell} ipython3 -m = geemap.Map() -m.set_center(121.615219, 25.041219, 12) +m = geemap.Map(center=[40, -100], zoom=4) + +# Add NASA SRTM +dem = ee.Image("USGS/SRTMGL1_003") +dem_vis = { + "min": 0, + "max": 4000, + "palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"], +} +m.add_layer(dem, dem_vis, "SRTM DEM") + +# Add 5-year Landsat TOA composite +landsat = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003") +landsat_vis = {"bands": ["B4", "B3", "B2"], "gamma": 1.4} +m.add_layer(landsat, landsat_vis, "Landsat", False) + +# Add US Census States +states = ee.FeatureCollection("TIGER/2018/States") +style = {"fillColor": "00000000"} +m.add_layer(states.style(**style), {}, "US States") m ``` ```{code-cell} ipython3 -roi = m.user_roi -if roi is None: - roi = ee.Geometry.BBox(121.3824, 24.9325, 121.6653, 25.1496) - m.add_layer(roi) - m.center_object(roi) +out_dem_stats = "dem_stats.csv" +geemap.zonal_stats( + dem, states, out_dem_stats, statistics_type="MEAN", scale=1000, return_fc=False +) ``` ```{code-cell} ipython3 -timelapse = geemap.sentinel2_timelapse( - roi, - out_gif="sentinel2.gif", - start_year=2017, - end_year=2024, - start_date="01-01", - end_date="12-31", - frequency="year", - bands=["SWIR1", "NIR", "Red"], - frames_per_second=3, - title="Sentinel-2 Timelapse", +out_landsat_stats = "landsat_stats.csv" +geemap.zonal_stats( + landsat, + states, + out_landsat_stats, + statistics_type="MEAN", + scale=1000, + return_fc=False, ) -geemap.show_image(timelapse) ``` -### MODIS vegetation indices +#### Zonal Statistics by Group + +Here, zonal statistics are applied to NLCD land cover data, calculating the area of each land cover type within each U.S. state. The results are saved to CSV files as both raw totals and percentages. This provides insights into the spatial distribution of land cover categories across states. ```{code-cell} ipython3 -Map = geemap.Map() -Map +m = geemap.Map(center=[40, -100], zoom=4) + +# Add NLCD data +dataset = ee.Image("USGS/NLCD_RELEASES/2019_REL/NLCD/2019") +landcover = dataset.select("landcover") +m.add_layer(landcover, {}, "NLCD 2019") + +# Add US census states +states = ee.FeatureCollection("TIGER/2018/States") +style = {"fillColor": "00000000"} +m.add_layer(states.style(**style), {}, "US States") + +# Add NLCD legend +m.add_legend(title="NLCD Land Cover", builtin_legend="NLCD") +m ``` ```{code-cell} ipython3 -roi = Map.user_roi -if roi is None: - roi = ee.Geometry.BBox(-18.6983, -36.1630, 52.2293, 38.1446) - Map.addLayer(roi) - Map.centerObject(roi) +nlcd_stats = "nlcd_stats.csv" + +geemap.zonal_stats_by_group( + landcover, + states, + nlcd_stats, + statistics_type="SUM", + denominator=1e6, + decimal_places=2, +) ``` ```{code-cell} ipython3 -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", +nlcd_stats = "nlcd_stats_pct.csv" + +geemap.zonal_stats_by_group( + landcover, + states, + nlcd_stats, + statistics_type="PERCENTAGE", + denominator=1e6, + decimal_places=2, ) -geemap.show_image(timelapse) ``` -### MODIS temperature data +#### Zonal Statistics with Two Images + +This example calculates the mean elevation values within different NLCD land cover types using DEM and NLCD data. The `geemap.image_stats_by_zone` function provides summary statistics (e.g., mean and standard deviation), which can be exported to CSV files for further analysis or visualization. ```{code-cell} ipython3 -Map = geemap.Map() -Map +m = geemap.Map(center=[40, -100], zoom=4) +dem = ee.Image("USGS/3DEP/10m") +vis = {"min": 0, "max": 4000, "palette": "terrain"} +m.add_layer(dem, vis, "DEM") +m ``` ```{code-cell} ipython3 -roi = Map.user_roi -if roi is None: - roi = ee.Geometry.BBox(-171.21, -57.13, 177.53, 79.99) - Map.addLayer(roi) - Map.centerObject(roi) +landcover = ee.Image("USGS/NLCD_RELEASES/2019_REL/NLCD/2019").select("landcover") +m.add_layer(landcover, {}, "NLCD 2019") +m.add_legend(title="NLCD Land Cover Classification", builtin_legend="NLCD") ``` ```{code-cell} ipython3 -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, +stats = geemap.image_stats_by_zone(dem, landcover, reducer="MEAN") +stats +``` + +```{code-cell} ipython3 +stats.to_csv("mean.csv", index=False) +``` + +```{code-cell} ipython3 +geemap.image_stats_by_zone(dem, landcover, out_csv="std.csv", reducer="STD") +``` + +### Map Algebra + +This example demonstrates basic map algebra by computing the Normalized Difference Vegetation Index (NDVI) for a 5-year Landsat composite and the Enhanced Vegetation Index (EVI) for a Landsat 8 image. These indices are visualized with color scales to highlight areas of vegetation. + +```{code-cell} ipython3 +m = geemap.Map() + +# Load a 5-year Landsat 7 composite 1999-2003. +landsat_1999 = ee.Image("LANDSAT/LE7_TOA_5YEAR/1999_2003") + +# Compute NDVI. +ndvi_1999 = ( + landsat_1999.select("B4") + .subtract(landsat_1999.select("B3")) + .divide(landsat_1999.select("B4").add(landsat_1999.select("B3"))) ) -geemap.show_image(timelapse) + +vis = {"min": 0, "max": 1, "palette": "ndvi"} +m.add_layer(ndvi_1999, vis, "NDVI") +m.add_colorbar(vis, label="NDVI") +m +``` + +```{code-cell} ipython3 +# Load a Landsat 8 image. +image = ee.Image("LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318") + +# Compute the EVI using an expression. +evi = image.expression( + "2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))", + { + "NIR": image.select("B5"), + "RED": image.select("B4"), + "BLUE": image.select("B2"), + }, +) + +# Define a map centered on San Francisco Bay. +m = geemap.Map(center=[37.4675, -122.1363], zoom=9) + +vis = {"min": 0, "max": 1, "palette": "ndvi"} +m.add_layer(evi, vis, "EVI") +m.add_colorbar(vis, label="EVI") +m ``` -### GOES timelapse +## Working with Local Geospatial Data + +### Raster Data + +Single-band and multi-band raster data can be loaded from local files. Here, a digital elevation model (DEM) is loaded and displayed with a terrain color scheme, and another raster is displayed with a false-color composite to highlight different features. + +#### Single-Band Raster Data ```{code-cell} ipython3 -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" +url = "https://github.com/giswqs/data/raw/main/raster/srtm90.tif" +filename = "dem.tif" +geemap.download_file(url, filename) ``` +#### Multi-Band Raster Data + ```{code-cell} ipython3 -timelapse = geemap.goes_timelapse( - roi, "goes.gif", start_date, end_date, data, scan, framesPerSecond=5 +m = geemap.Map() +m.add_raster(filename, cmap="terrain", layer_name="DEM") +vis_params = {"min": 0, "max": 4000, "palette": "terrain"} +m.add_colorbar(vis_params, label="Elevation (m)") +m +``` + +```{code-cell} ipython3 +url = "https://github.com/giswqs/data/raw/main/raster/cog.tif" +filename = "cog.tif" +geemap.download_file(url, filename) +``` + +```{code-cell} ipython3 +m = geemap.Map() +m.add_raster(filename, indexes=[4, 1, 2], layer_name="False color") +m +``` + +### Vector Data + +Various vector data formats can be loaded and visualized with geemap, including GeoJSON, Shapefile, GeoDataFrame, and GeoPackage formats. GeoJSON data is styled dynamically with custom colors based on attributes, while Shapefiles and GeoDataFrames allow for simple, structured addition of geographic features to the map. + +#### GeoJSON + +```{code-cell} ipython3 +in_geojson = ( + "https://github.com/opengeos/datasets/releases/download/vector/cables.geojson" ) -geemap.show_image(timelapse) +m = geemap.Map() +m.add_geojson(in_geojson, layer_name="Cable lines", info_mode="on_hover") +m ``` ```{code-cell} ipython3 -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" +m = geemap.Map() +m.add_basemap("CartoDB.DarkMatter") +callback = lambda feat: {"color": feat["properties"]["color"], "weight": 2} +m.add_geojson(in_geojson, layer_name="Cable lines", style_callback=callback) +m ``` ```{code-cell} ipython3 -timelapse = geemap.goes_timelapse( - roi, "hurricane.gif", start_date, end_date, data, scan, framesPerSecond=5 +url = "https://github.com/opengeos/datasets/releases/download/world/countries.geojson" +m = geemap.Map() +m.add_geojson( + url, layer_name="Countries", fill_colors=["red", "yellow", "green", "orange"] ) -geemap.show_image(timelapse) +m ``` ```{code-cell} ipython3 -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" +import random + +m = geemap.Map() + + +def random_color(feature): + return { + "color": "black", + "weight": 3, + "fillColor": random.choice(["red", "yellow", "green", "orange"]), + } + + +m.add_geojson(url, layer_name="Countries", style_callback=random_color) +m ``` ```{code-cell} ipython3 -timelapse = geemap.goes_fire_timelapse( - roi, "fire.gif", start_date, end_date, data, scan, framesPerSecond=5 +m = geemap.Map() + +style = { + "stroke": True, + "color": "#0000ff", + "weight": 2, + "opacity": 1, + "fill": True, + "fillColor": "#0000ff", + "fillOpacity": 0.1, +} + +hover_style = {"fillOpacity": 0.7} + +m.add_geojson(url, layer_name="Countries", style=style, hover_style=hover_style) +m +``` + +#### Shapefile + +```{code-cell} ipython3 +url = "https://github.com/opengeos/datasets/releases/download/world/countries.zip" +geemap.download_file(url, overwrite=True) +``` + +```{code-cell} ipython3 +m = geemap.Map() +in_shp = "countries.shp" +m.add_shp(in_shp, layer_name="Countries") +m +``` + +#### GeoDataFrame + +```{code-cell} ipython3 +import geopandas as gpd + +m = geemap.Map(center=[40, -100], zoom=4) +gdf = gpd.read_file("countries.shp") +m.add_gdf(gdf, layer_name="Countries") +m +``` + +#### GeoPackage + +```{code-cell} ipython3 +m = geemap.Map() +data = "https://github.com/opengeos/datasets/releases/download/world/countries.gpkg" +m.add_vector(data, layer_name="Countries") +m +``` + +#### CSV to Vector + +Geemap enables easy conversion from CSV files to vector data formats like GeoJSON, Shapefile, and GeoPackage. The data from a CSV is visualized on a map, where cities are displayed with markers styled by region, and a legend is added for clear reference. + +```{code-cell} ipython3 +data = "https://github.com/gee-community/geemap/blob/master/examples/data/us_cities.csv" +geemap.csv_to_df(data) +``` + +```{code-cell} ipython3 +geemap.csv_to_geojson( + data, "cities.geojson", latitude="latitude", longitude="longitude" ) -geemap.show_image(timelapse) ``` -### Exercise 3 - Creating timelapse animations +```{code-cell} ipython3 +geemap.csv_to_shp(data, "cities.shp", latitude="latitude", longitude="longitude") +``` + +```{code-cell} ipython3 +geemap.csv_to_vector(data, "cities.gpkg", latitude="latitude", longitude="longitude") +``` + +```{code-cell} ipython3 +gdf = geemap.csv_to_gdf(data, latitude="latitude", longitude="longitude") +gdf +``` + +```{code-cell} ipython3 +cities = ( + "https://github.com/gee-community/geemap/blob/master/examples/data/us_cities.csv" +) +m = geemap.Map(center=[40, -100], zoom=4) +m.add_points_from_xy(cities, x="longitude", y="latitude") +m +``` + +```{code-cell} ipython3 +regions = "https://github.com/gee-community/geemap/blob/master/examples/data/us_regions.geojson" +``` + +```{code-cell} ipython3 +m = geemap.Map(center=[40, -100], zoom=4) +m.add_geojson(regions, layer_name="US Regions") +m.add_points_from_xy( + cities, + x="longitude", + y="latitude", + layer_name="US Cities", + color_column="region", + icon_names=["gear", "map", "leaf", "globe"], + spin=True, + add_legend=True, +) +m +``` + +```{code-cell} ipython3 +m = geemap.Map(center=[40, -100], zoom=4) +m.add_circle_markers_from_xy( + data, + x="longitude", + y="latitude", + radius=8, + color="blue", + fill_color="black", + fill_opacity=0.5, +) +m +``` + +## Accessing Cloud Optimized GeoTIFFs + +### Cloud Optimized GeoTIFFs (COG) + +This section shows how to add Cloud Optimized GeoTIFF (COG) layers to a map using URLs, which allows for efficient loading and visualization of large raster files stored on the cloud. In this example, a pre-event image of the 2020 California wildfire is added to a map, allowing for remote sensing analysis without local file storage. + +```{code-cell} ipython3 +m = geemap.Map() +url = "https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2018-02-16/pine-gulch-fire20/1030010076004E00.tif" +m.add_cog_layer(url, name="Fire (pre-event)") +m +``` + +```{code-cell} ipython3 +geemap.cog_center(url) +``` + +The COG functions also allow inspection of data, such as viewing the bounds and available bands in the GeoTIFF file. The pre- and post-event images can be compared in a split map view to analyze changes due to the fire. + +```{code-cell} ipython3 +geemap.cog_bands(url) +``` + +```{code-cell} ipython3 +url2 = "https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-14/pine-gulch-fire20/10300100AAC8DD00.tif" +m.add_cog_layer(url2, name="Fire (post-event)") +``` + +```{code-cell} ipython3 +m = geemap.Map(center=[39.4568, -108.5107], zoom=12) +m.split_map(left_layer=url2, right_layer=url) +m +``` + +### SpatioTemporal Asset Catalog (STAC) + +STAC collections can be accessed and visualized similarly, enabling dynamic mapping of spatiotemporal data. By specifying STAC URLs, you can view dataset bounds, center the map on a dataset, and select specific bands to add as layers. This example shows adding panchromatic and false-color imagery for enhanced visual analysis. + +```{code-cell} ipython3 +url = "https://tinyurl.com/22vptbws" +``` + +```{code-cell} ipython3 +geemap.stac_bounds(url) +``` + +```{code-cell} ipython3 +geemap.stac_center(url) +``` + +```{code-cell} ipython3 +geemap.stac_bands(url) +``` + +```{code-cell} ipython3 +m = geemap.Map() +m.add_stac_layer(url, bands=["pan"], name="Panchromatic") +m.add_stac_layer(url, bands=["B3", "B2", "B1"], name="False color") +m +``` -Use the geemap timelapse GUI to create a timelapse animation for any location of your choice. Share the timelapse on social media and use the hashtag such as #EarthEngine and #geemap. See [this](https://i.imgur.com/YaCHvKC.gif) example. +## Exporting Earth Engine Data -![](https://i.imgur.com/ohrXeFC.png) +### 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. ```{code-cell} ipython3 m = geemap.Map() -m.add_gui("timelapse") + +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. + +```{code-cell} ipython3 +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 + +```{code-cell} ipython3 +geemap.ee_export_image(image, filename="landsat.tif", scale=30, region=region) +``` + +Check image projection. + +```{code-cell} ipython3 +projection = image.select(0).projection().getInfo() +projection +``` + +```{code-cell} ipython3 +crs = projection["crs"] +crs_transform = projection["transform"] +``` + +Specify region, crs, and crs_transform. + +```{code-cell} ipython3 +geemap.ee_export_image( + image, + filename="landsat_crs.tif", + crs=crs, + crs_transform=crs_transform, + region=region, +) +``` + +To Google Drive + +```{code-cell} ipython3 +geemap.ee_export_image_to_drive( + image, description="landsat", folder="export", region=region, scale=30 +) +``` + +```{code-cell} ipython3 +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. + +```{code-cell} ipython3 +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")) +) +``` + +```{code-cell} ipython3 +collection.aggregate_array("system:index") +``` + +To local drive + +```{code-cell} ipython3 +geemap.ee_export_image_collection(collection, out_dir=".", scale=10) +``` + +To Google Drive + +```{code-cell} ipython3 +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. + +```{code-cell} ipython3 +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 + +```{code-cell} ipython3 +geemap.ee_to_shp(fc, filename="Alaska.shp") +``` + +```{code-cell} ipython3 +geemap.ee_export_vector(fc, filename="Alaska.shp") +``` + +```{code-cell} ipython3 +geemap.ee_to_geojson(fc, filename="Alaska.geojson") +``` + +```{code-cell} ipython3 +geemap.ee_to_csv(fc, filename="Alaska.csv") +``` + +```{code-cell} ipython3 +gdf = geemap.ee_to_gdf(fc) +gdf +``` + +```{code-cell} ipython3 +df = geemap.ee_to_df(fc) +df +``` + +To Google Drive + +```{code-cell} ipython3 +geemap.ee_export_vector_to_drive( + fc, description="Alaska", fileFormat="SHP", folder="export" +) +``` + +## Creating Timelapse Animations + +### Landsat Timelapse + +The Landsat timelapse tool creates animations of changes over time. Here, we use a defined region of interest (ROI) to generate timelapse GIFs for Las Vegas (1984-2023): + +```{code-cell} ipython3 +m = geemap.Map() +roi = ee.Geometry.BBox(-115.5541, 35.8044, -113.9035, 36.5581) +m.add_layer(roi) +m.center_object(roi) +m +``` + +```{code-cell} ipython3 +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) +``` + +```{code-cell} ipython3 +m = geemap.Map() +roi = ee.Geometry.BBox(113.8252, 22.1988, 114.0851, 22.3497) +m.add_layer(roi) +m.center_object(roi) +m +``` + +```{code-cell} ipython3 +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. + +```{code-cell} ipython3 +m = geemap.Map(center=[41.718934, -86.894547], zoom=12) +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. + +```{code-cell} ipython3 +roi = m.user_roi +if roi is None: + roi = ee.Geometry.BBox(-87.0492, 41.6545, -86.7903, 41.7604) + m.add_layer(roi) + m.center_object(roi) +``` + +```{code-cell} ipython3 +timelapse = geemap.sentinel2_timelapse( + roi, + out_gif="sentinel2.gif", + start_year=2017, + end_year=2023, + start_date="06-01", + end_date="09-01", + 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. + +```{code-cell} ipython3 +m = geemap.Map() +m +``` + +```{code-cell} ipython3 +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) +``` + +```{code-cell} ipython3 +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 + +```{code-cell} ipython3 +m = geemap.Map() +m +``` + +```{code-cell} ipython3 +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) +``` + +```{code-cell} ipython3 +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. + +```{code-cell} ipython3 +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" +``` + +```{code-cell} ipython3 +timelapse = geemap.goes_timelapse( + roi, "goes.gif", start_date, end_date, data, scan, framesPerSecond=5 +) +geemap.show_image(timelapse) +``` + +```{code-cell} ipython3 +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" +``` + +```{code-cell} ipython3 +timelapse = geemap.goes_timelapse( + roi, "hurricane.gif", start_date, end_date, data, scan, framesPerSecond=5 +) +geemap.show_image(timelapse) +``` + +```{code-cell} ipython3 +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" +``` + +```{code-cell} ipython3 +timelapse = geemap.goes_fire_timelapse( + roi, "fire.gif", start_date, end_date, data, scan, framesPerSecond=5 +) +geemap.show_image(timelapse) +``` + +## Charting + +### Charting Features + +#### Import libraries + +To create charts for Earth Engine features, import required libraries like `calendar`, `ee`, `geemap`, and `chart`. + +```{code-cell} ipython3 +import calendar +import ee +import geemap +from geemap import chart +``` + +Initialize Earth Engine using `geemap.ee_initialize()`. + +```{code-cell} ipython3 +geemap.ee_initialize() +``` + +#### feature_by_feature + +This function allows feature plotting along the x-axis, with values from selected properties represented along the y-axis. Ecoregions data is used to chart average monthly temperature across regions, where months serve as series columns. + +```{code-cell} ipython3 +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") +features = ecoregions.select("[0-9][0-9]_tmean|label") +``` + +```{code-cell} ipython3 +geemap.ee_to_df(features) +``` + +```{code-cell} ipython3 +x_property = "label" +y_properties = [str(x).zfill(2) + "_tmean" for x in range(1, 13)] + +labels = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...] + +colors = [ + "#604791", + "#1d6b99", + "#39a8a7", + "#0f8755", + "#76b349", + "#f0af07", + "#e37d05", + "#cf513e", + "#96356f", + "#724173", + "#9c4f97", + "#696969", +] +title = "Average Monthly Temperature by Ecoregion" +x_label = "Ecoregion" +y_label = "Temperature" +``` + +```{code-cell} ipython3 +fig = chart.feature_by_feature( + features, + x_property, + y_properties, + colors=colors, + labels=labels, + title=title, + x_label=x_label, + y_label=y_label, +) +fig +``` + +![](https://i.imgur.com/MZa99Vf.png) + ++++ + +#### feature.by_property + +Displays average precipitation by month for each ecoregion. Properties represent precipitation values for each month, allowing for visual comparison across regions by month. + +```{code-cell} ipython3 +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") +features = ecoregions.select("[0-9][0-9]_ppt|label") +``` + +```{code-cell} ipython3 +geemap.ee_to_df(features) +``` + +```{code-cell} ipython3 +keys = [str(x).zfill(2) + "_ppt" for x in range(1, 13)] +values = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...] +``` + +```{code-cell} ipython3 +x_properties = dict(zip(keys, values)) +series_property = "label" +title = "Average Ecoregion Precipitation by Month" +colors = ["#f0af07", "#0f8755", "#76b349"] +``` + +```{code-cell} ipython3 +fig = chart.feature_by_property( + features, + x_properties, + series_property, + title=title, + colors=colors, + x_label="Month", + y_label="Precipitation (mm)", + legend_location="top-left", +) +fig +``` + +![](https://i.imgur.com/6RhuUc7.png) + ++++ + +#### feature_groups + +Plots groups of features based on property values, showing average January temperature for ecoregions divided into "Warm" and "Cold" categories. + +```{code-cell} ipython3 +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") +features = ecoregions.select("[0-9][0-9]_ppt|label") +``` + +```{code-cell} ipython3 +features = ee.FeatureCollection("projects/google/charts_feature_example") +x_property = "label" +y_property = "01_tmean" +series_property = "warm" +title = "Average January Temperature by Ecoregion" +colors = ["#cf513e", "#1d6b99"] +labels = ["Warm", "Cold"] +``` + +```{code-cell} ipython3 +chart.feature_groups( + features, + x_property, + y_property, + series_property, + title=title, + colors=colors, + x_label="Ecoregion", + y_label="January Temperature (°C)", + legend_location="top-right", + labels=labels, +) +``` + +![](https://i.imgur.com/YFZlJtc.png) + ++++ + +#### feature_histogram + +Generates a histogram displaying July precipitation distribution across a region, showing pixel count for different precipitation levels, useful for understanding data spread. + +```{code-cell} ipython3 +source = ee.ImageCollection("OREGONSTATE/PRISM/Norm91m").toBands() +region = ee.Geometry.Rectangle(-123.41, 40.43, -116.38, 45.14) +features = source.sample(region, 5000) +``` + +```{code-cell} ipython3 +geemap.ee_to_df(features.limit(5).select(["07_ppt"])) +``` + +```{code-cell} ipython3 +property = "07_ppt" +title = "July Precipitation Distribution for NW USA" +``` + +```{code-cell} ipython3 +fig = chart.feature_histogram( + features, + property, + max_buckets=None, + title=title, + x_label="Precipitation (mm)", + y_label="Pixel Count", + colors=["#1d6b99"], +) +fig +``` + +![](https://i.imgur.com/ErIp7Oy.png) + +### Charting Images + +#### image_by_region + +Displays monthly temperature for each ecoregion using the `image_by_region` function, aggregating data to visualize average temperature by month across regions. + +```{code-cell} ipython3 +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") +image = ( + ee.ImageCollection("OREGONSTATE/PRISM/Norm91m").toBands().select("[0-9][0-9]_tmean") +) +``` + +```{code-cell} ipython3 +labels = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...] +title = "Average Monthly Temperature by Ecoregion" +``` + +```{code-cell} ipython3 +fig = chart.image_by_region( + image, + ecoregions, + reducer="mean", + scale=500, + x_property="label", + title=title, + x_label="Ecoregion", + y_label="Temperature", + labels=labels, +) +fig +``` + +![](https://i.imgur.com/y4rp3dK.png) + ++++ + +#### image_regions + + +Generates a chart showing average monthly precipitation across regions, using properties to represent months and comparing precipitation levels. + +```{code-cell} ipython3 +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") +image = ( + ee.ImageCollection("OREGONSTATE/PRISM/Norm91m").toBands().select("[0-9][0-9]_ppt") +) +``` + +```{code-cell} ipython3 +keys = [str(x).zfill(2) + "_ppt" for x in range(1, 13)] +values = calendar.month_abbr[1:] # a list of month labels, e.g. ['Jan', 'Feb', ...] +``` + +```{code-cell} ipython3 +x_properties = dict(zip(keys, values)) +title = "Average Ecoregion Precipitation by Month" +colors = ["#f0af07", "#0f8755", "#76b349"] +``` + +```{code-cell} ipython3 +fig = chart.image_regions( + image, + ecoregions, + reducer="mean", + scale=500, + series_property="label", + x_labels=x_properties, + title=title, + colors=colors, + x_label="Month", + y_label="Precipitation (mm)", + legend_location="top-left", +) +``` + +![](https://i.imgur.com/5WJVCNY.png) + ++++ + +#### image_by_class + +Displays spectral signatures of ecoregions by wavelength, showing reflectance values across spectral bands for comparison. + +```{code-cell} ipython3 +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") + +image = ( + ee.ImageCollection("MODIS/061/MOD09A1") + .filter(ee.Filter.date("2018-06-01", "2018-09-01")) + .select("sur_refl_b0[0-7]") + .mean() + .select([2, 3, 0, 1, 4, 5, 6]) +) + +wavelengths = [469, 555, 655, 858, 1240, 1640, 2130] +``` + +```{code-cell} ipython3 +fig = chart.image_by_class( + image, + class_band="label", + region=ecoregions, + reducer="MEAN", + scale=500, + x_labels=wavelengths, + title="Ecoregion Spectral Signatures", + x_label="Wavelength (nm)", + y_label="Reflectance (x1e4)", + colors=["#f0af07", "#0f8755", "#76b349"], + legend_location="top-left", + interpolation="basis", +) +fig +``` + +![](https://i.imgur.com/XqYHvBV.png) + ++++ + +#### image_histogram + +Generates histograms to visualize MODIS surface reflectance distribution across red, NIR, and SWIR bands, providing insights into data distribution by band. + +```{code-cell} ipython3 +image = ( + ee.ImageCollection("MODIS/061/MOD09A1") + .filter(ee.Filter.date("2018-06-01", "2018-09-01")) + .select(["sur_refl_b01", "sur_refl_b02", "sur_refl_b06"]) + .mean() +) + +region = ee.Geometry.Rectangle([-112.60, 40.60, -111.18, 41.22]) +``` + +```{code-cell} ipython3 +fig = chart.image_histogram( + image, + region, + scale=500, + max_buckets=200, + min_bucket_width=1.0, + max_raw=1000, + max_pixels=int(1e6), + title="MODIS SR Reflectance Histogram", + labels=["Red", "NIR", "SWIR"], + colors=["#cf513e", "#1d6b99", "#f0af07"], +) +fig +``` + +![](https://i.imgur.com/mY4yoYH.png) + +### Charting Image Collections + +#### image_series + +Creates a time series chart of vegetation indices (NDVI and EVI) for a forest region, helping to track vegetation health over time. + +```{code-cell} ipython3 +# Define the forest feature collection. +forest = ee.FeatureCollection("projects/google/charts_feature_example").filter( + ee.Filter.eq("label", "Forest") +) + +# Load MODIS vegetation indices data and subset a decade of images. +veg_indices = ( + ee.ImageCollection("MODIS/061/MOD13A1") + .filter(ee.Filter.date("2010-01-01", "2020-01-01")) + .select(["NDVI", "EVI"]) +) +``` + +```{code-cell} ipython3 +title = "Average Vegetation Index Value by Date for Forest" +x_label = "Year" +y_label = "Vegetation index (x1e4)" +colors = ["#e37d05", "#1d6b99"] +``` + +```{code-cell} ipython3 +fig = chart.image_series( + veg_indices, + region=forest, + reducer=ee.Reducer.mean(), + scale=500, + x_property="system:time_start", + chart_type="LineChart", + x_cols="date", + y_cols=["NDVI", "EVI"], + colors=colors, + title=title, + x_label=x_label, + y_label=y_label, + legend_location="right", +) +fig +``` + +![](https://i.imgur.com/r9zSJh6.png) + ++++ + +#### image_series_by_region + +Shows NDVI time series by region, comparing desert, forest, and grassland areas. Each region has a unique series for easy comparison. + +```{code-cell} ipython3 +# Import the example feature collection. +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") + +# Load MODIS vegetation indices data and subset a decade of images. +veg_indices = ( + ee.ImageCollection("MODIS/061/MOD13A1") + .filter(ee.Filter.date("2010-01-01", "2020-01-01")) + .select(["NDVI"]) +) +``` + +```{code-cell} ipython3 +title = "Average NDVI Value by Date" +x_label = "Date" +y_label = "NDVI (x1e4)" +x_cols = "index" +y_cols = ["Desert", "Forest", "Grassland"] +colors = ["#f0af07", "#0f8755", "#76b349"] +``` + +```{code-cell} ipython3 +fig = chart.image_series_by_region( + veg_indices, + regions=ecoregions, + reducer=ee.Reducer.mean(), + band="NDVI", + scale=500, + x_property="system:time_start", + series_property="label", + chart_type="LineChart", + x_cols=x_cols, + y_cols=y_cols, + title=title, + x_label=x_label, + y_label=y_label, + colors=colors, + stroke_width=3, + legend_location="bottom-left", +) +fig +``` + +![](https://i.imgur.com/rnILSfI.png) + ++++ + +#### image_doy_series + +Plots average vegetation index by day of year for a specific region, showing seasonal vegetation patterns over a decade. + +```{code-cell} ipython3 +# Import the example feature collection and subset the grassland feature. +grassland = ee.FeatureCollection("projects/google/charts_feature_example").filter( + ee.Filter.eq("label", "Grassland") +) + +# Load MODIS vegetation indices data and subset a decade of images. +veg_indices = ( + ee.ImageCollection("MODIS/061/MOD13A1") + .filter(ee.Filter.date("2010-01-01", "2020-01-01")) + .select(["NDVI", "EVI"]) +) +``` + +```{code-cell} ipython3 +title = "Average Vegetation Index Value by Day of Year for Grassland" +x_label = "Day of Year" +y_label = "Vegetation Index (x1e4)" +colors = ["#f0af07", "#0f8755"] +``` + +```{code-cell} ipython3 +fig = chart.image_doy_series( + image_collection=veg_indices, + region=grassland, + scale=500, + chart_type="LineChart", + title=title, + x_label=x_label, + y_label=y_label, + colors=colors, + stroke_width=5, +) +fig +``` + +![](https://i.imgur.com/F0z088e.png) + ++++ + +#### image_doy_series_by_year + +Compares NDVI by day of year across two different years, showing how vegetation patterns vary between 2012 and 2019. + +```{code-cell} ipython3 +# Import the example feature collection and subset the grassland feature. +grassland = ee.FeatureCollection("projects/google/charts_feature_example").filter( + ee.Filter.eq("label", "Grassland") +) + +# Load MODIS vegetation indices data and subset years 2012 and 2019. +veg_indices = ( + ee.ImageCollection("MODIS/061/MOD13A1") + .filter( + ee.Filter.Or( + ee.Filter.date("2012-01-01", "2013-01-01"), + ee.Filter.date("2019-01-01", "2020-01-01"), + ) + ) + .select(["NDVI", "EVI"]) +) +``` + +```{code-cell} ipython3 +title = "Average Vegetation Index Value by Day of Year for Grassland" +x_label = "Day of Year" +y_label = "Vegetation Index (x1e4)" +colors = ["#e37d05", "#1d6b99"] +``` + +```{code-cell} ipython3 +fig = chart.doy_series_by_year( + veg_indices, + band_name="NDVI", + region=grassland, + scale=500, + chart_type="LineChart", + colors=colors, + title=title, + x_label=x_label, + y_label=y_label, + stroke_width=5, +) +fig +``` + +![](https://i.imgur.com/ui6zpbl.png) + ++++ + +#### image_doy_series_by_region + +Visualizes average NDVI by day of year across regions, showcasing seasonal changes for desert, forest, and grassland ecoregions. + +```{code-cell} ipython3 +# Import the example feature collection and subset the grassland feature. +ecoregions = ee.FeatureCollection("projects/google/charts_feature_example") + +# Load MODIS vegetation indices data and subset a decade of images. +veg_indices = ( + ee.ImageCollection("MODIS/061/MOD13A1") + .filter(ee.Filter.date("2010-01-01", "2020-01-01")) + .select(["NDVI"]) +) +``` + +```{code-cell} ipython3 +title = "Average Vegetation Index Value by Day of Year for Grassland" +x_label = "Day of Year" +y_label = "Vegetation Index (x1e4)" +colors = ["#f0af07", "#0f8755", "#76b349"] +``` + +```{code-cell} ipython3 +fig = chart.image_doy_series_by_region( + veg_indices, + "NDVI", + ecoregions, + region_reducer="mean", + scale=500, + year_reducer=ee.Reducer.mean(), + start_day=1, + end_day=366, + series_property="label", + stroke_width=5, + chart_type="LineChart", + title=title, + x_label=x_label, + y_label=y_label, + colors=colors, + legend_location="right", +) +fig +``` + +![](https://i.imgur.com/eGqGoRs.png) + +### Charting Array and List + +#### Scatter Plot + +Displays relationships between spectral bands, plotting red reflectance against NIR and SWIR reflectance to examine band correlations. + +```{code-cell} ipython3 +# Import the example feature collection and subset the forest feature. +forest = ee.FeatureCollection("projects/google/charts_feature_example").filter( + ee.Filter.eq("label", "Forest") +) + +# Define a MODIS surface reflectance composite. +modisSr = ( + ee.ImageCollection("MODIS/061/MOD09A1") + .filter(ee.Filter.date("2018-06-01", "2018-09-01")) + .select("sur_refl_b0[0-7]") + .mean() +) + +# Reduce MODIS reflectance bands by forest region; get a dictionary with +# band names as keys, pixel values as lists. +pixel_vals = modisSr.reduceRegion( + **{"reducer": ee.Reducer.toList(), "geometry": forest.geometry(), "scale": 2000} +) + +# Convert NIR and SWIR value lists to an array to be plotted along the y-axis. +y_values = pixel_vals.toArray(["sur_refl_b02", "sur_refl_b06"]) + + +# Get the red band value list; to be plotted along the x-axis. +x_values = ee.List(pixel_vals.get("sur_refl_b01")) +``` + +```{code-cell} ipython3 +title = "Relationship Among Spectral Bands for Forest Pixels" +colors = ["rgba(29,107,153,0.4)", "rgba(207,81,62,0.4)"] +``` + +```{code-cell} ipython3 +fig = chart.array_values( + y_values, + axis=1, + x_labels=x_values, + series_names=["NIR", "SWIR"], + chart_type="ScatterChart", + colors=colors, + title=title, + x_label="Red reflectance (x1e4)", + y_label="NIR & SWIR reflectance (x1e4)", + default_size=15, + xlim=(0, 800), +) +fig +``` + +![](https://i.imgur.com/zkPlZIO.png) + +```{code-cell} ipython3 +x = ee.List(pixel_vals.get("sur_refl_b01")) +y = ee.List(pixel_vals.get("sur_refl_b06")) +``` + +```{code-cell} ipython3 +fig = chart.array_values( + y, + x_labels=x, + series_names=["SWIR"], + chart_type="ScatterChart", + colors=["rgba(207,81,62,0.4)"], + title=title, + x_label="Red reflectance (x1e4)", + y_label="SWIR reflectance (x1e4)", + default_size=15, + xlim=(0, 800), +) +fig +``` + +![](https://i.imgur.com/WHUHjH6.png) + ++++ + + #### Transect Line Plot + +Plots elevation along a transect line across a specific region, providing a profile view of terrain elevation. + +```{code-cell} ipython3 +# Define a line across the Olympic Peninsula, USA. +transect = ee.Geometry.LineString([[-122.8, 47.8], [-124.5, 47.8]]) + +# Define a pixel coordinate image. +lat_lon_img = ee.Image.pixelLonLat() + +# Import a digital surface model and add latitude and longitude bands. +elev_img = ee.Image("USGS/SRTMGL1_003").select("elevation").addBands(lat_lon_img) + +# Reduce elevation and coordinate bands by transect line; get a dictionary with +# band names as keys, pixel values as lists. +elev_transect = elev_img.reduceRegion( + reducer=ee.Reducer.toList(), + geometry=transect, + scale=1000, +) + +# Get longitude and elevation value lists from the reduction dictionary. +lon = ee.List(elev_transect.get("longitude")) +elev = ee.List(elev_transect.get("elevation")) + +# Sort the longitude and elevation values by ascending longitude. +lon_sort = lon.sort(lon) +elev_sort = elev.sort(lon) +``` + +```{code-cell} ipython3 +fig = chart.array_values( + elev_sort, + x_labels=lon_sort, + series_names=["Elevation"], + chart_type="AreaChart", + colors=["#1d6b99"], + title="Elevation Profile Across Longitude", + x_label="Longitude", + y_label="Elevation (m)", + stroke_width=5, + fill="bottom", + fill_opacities=[0.4], + ylim=(0, 2500), +) +fig +``` + +![](https://i.imgur.com/k3XRita.png) + ++++ + +#### Metadata Scatter Plot + +Visualizes Landsat image metadata by plotting cloud cover against geometric RMSE, useful for quality assessment of image collections. + +```{code-cell} ipython3 +# Import a Landsat 8 collection and filter to a single path/row. +col = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filter( + ee.Filter.expression("WRS_PATH == 45 && WRS_ROW == 30") +) + +# Reduce image properties to a series of lists; one for each selected property. +propVals = col.reduceColumns( + reducer=ee.Reducer.toList().repeat(2), + selectors=["CLOUD_COVER", "GEOMETRIC_RMSE_MODEL"], +).get("list") + +# Get selected image property value lists; to be plotted along x and y axes. +x = ee.List(ee.List(propVals).get(0)) +y = ee.List(ee.List(propVals).get(1)) +``` + +```{code-cell} ipython3 +colors = [geemap.hex_to_rgba("#96356f", 0.4)] +print(colors) +``` + +```{code-cell} ipython3 +fig = chart.array_values( + y, + x_labels=x, + series_names=["RMSE"], + chart_type="ScatterChart", + colors=colors, + title="Landsat 8 Image Collection Metadata (045030)", + x_label="Cloud cover (%)", + y_label="Geometric RMSE (m)", + default_size=15, +) +fig +``` + +![](https://i.imgur.com/3COY3xd.png) + ++++ + +#### Mapped Function Scatter & Line Plot + +Plots a sine function as a line chart, mapping mathematical functions onto chart axes for visualization. + +```{code-cell} ipython3 +import math + +start = -2 * math.pi +end = 2 * math.pi +points = ee.List.sequence(start, end, None, 50) + + +def sin_func(val): + return ee.Number(val).sin() + + +values = points.map(sin_func) +``` + +```{code-cell} ipython3 +fig = chart.array_values( + values, + points, + chart_type="LineChart", + colors=["#39a8a7"], + title="Sine Function", + x_label="radians", + y_label="sin(x)", + marker="circle", +) +fig +``` + +![](https://i.imgur.com/7qcxvey.png) + +### Charting Data Table + +#### Manual DataTable chart + +Creates a chart from a manually created DataTable, such as US state populations. + +```{code-cell} ipython3 +import pandas as pd +``` + +```{code-cell} ipython3 +data = { + "State": ["CA", "NY", "IL", "MI", "OR"], + "Population": [37253956, 19378102, 12830632, 9883640, 3831074], +} + +df = pd.DataFrame(data) +df +``` + +```{code-cell} ipython3 +fig = chart.Chart( + df, + x_cols=["State"], + y_cols=["Population"], + chart_type="ColumnChart", + colors=["#1d6b99"], + title="State Population (US census, 2010)", + x_label="State", + y_label="Population", +) +fig +``` + +![](https://i.imgur.com/vuxNmuh.png) + ++++ + +#### Computed DataTable chart + +Computes a DataTable from MODIS vegetation indices data for a forest area, then charts NDVI and EVI time series. + +```{code-cell} ipython3 +# Import the example feature collection and subset the forest feature. +forest = ee.FeatureCollection("projects/google/charts_feature_example").filter( + ee.Filter.eq("label", "Forest") +) + +# Load MODIS vegetation indices data and subset a decade of images. +veg_indices = ( + ee.ImageCollection("MODIS/061/MOD13A1") + .filter(ee.Filter.date("2010-01-01", "2020-01-01")) + .select(["NDVI", "EVI"]) +) + +# Build a feature collection where each feature has a property that represents +# a DataFrame row. + + +def aggregate(img): + # Reduce the image to the mean of pixels intersecting the forest ecoregion. + stat = img.reduceRegion( + **{"reducer": ee.Reducer.mean(), "geometry": forest, "scale": 500} + ) + + # Extract the reduction results along with the image date. + date = geemap.image_date(img) + evi = stat.get("EVI") + ndvi = stat.get("NDVI") + + # Make a list of observation attributes to define a row in the DataTable. + row = ee.List([date, evi, ndvi]) + + # Return the row as a property of an ee.Feature. + return ee.Feature(None, {"row": row}) + + +reduction_table = veg_indices.map(aggregate) + +# Aggregate the 'row' property from all features in the new feature collection +# to make a server-side 2-D list (DataTable). +data_table_server = reduction_table.aggregate_array("row") + +# Define column names and properties for the DataTable. The order should +# correspond to the order in the construction of the 'row' property above. +column_header = ee.List([["Date", "EVI", "NDVI"]]) + +# Concatenate the column header to the table. +data_table_server = column_header.cat(data_table_server) +``` + +```{code-cell} ipython3 +data_table = chart.DataTable(data_table_server, date_column="Date") +data_table.head() +``` + +```{code-cell} ipython3 +fig = chart.Chart( + data_table, + chart_type="LineChart", + x_cols="Date", + y_cols=["EVI", "NDVI"], + colors=["#e37d05", "#1d6b99"], + title="Average Vegetation Index Value by Date for Forest", + x_label="Date", + y_label="Vegetation index (x1e4)", + stroke_width=3, + legend_location="right", +) +fig +``` + +![](https://i.imgur.com/PWei7QC.png) + ++++ + +#### Interval chart + +Displays annual NDVI time series with variance, showing inter-annual vegetation change and variability across the year. + +```{code-cell} ipython3 +# Define a point to extract an NDVI time series for. +geometry = ee.Geometry.Point([-121.679, 36.479]) + +# Define a band of interest (NDVI), import the MODIS vegetation index dataset, +# and select the band. +band = "NDVI" +ndvi_col = ee.ImageCollection("MODIS/061/MOD13Q1").select(band) + +# Map over the collection to add a day of year (doy) property to each image. + + +def set_doy(img): + doy = ee.Date(img.get("system:time_start")).getRelative("day", "year") + # Add 8 to day of year number so that the doy label represents the middle of + # the 16-day MODIS NDVI composite. + return img.set("doy", ee.Number(doy).add(8)) + + +ndvi_col = ndvi_col.map(set_doy) + +# Join all coincident day of year observations into a set of image collections. +distinct_doy = ndvi_col.filterDate("2013-01-01", "2014-01-01") +filter = ee.Filter.equals(**{"leftField": "doy", "rightField": "doy"}) +join = ee.Join.saveAll("doy_matches") +join_col = ee.ImageCollection(join.apply(distinct_doy, ndvi_col, filter)) + +# Calculate the absolute range, interquartile range, and median for the set +# of images composing each coincident doy observation group. The result is +# an image collection with an image representative per unique doy observation +# with bands that describe the 0, 25, 50, 75, 100 percentiles for the set of +# coincident doy images. + + +def cal_percentiles(img): + doyCol = ee.ImageCollection.fromImages(img.get("doy_matches")) + + return doyCol.reduce( + ee.Reducer.percentile([0, 25, 50, 75, 100], ["p0", "p25", "p50", "p75", "p100"]) + ).set({"doy": img.get("doy")}) + + +comp = ee.ImageCollection(join_col.map(cal_percentiles)) + +# Extract the inter-annual NDVI doy percentile statistics for the +# point of interest per unique doy representative. The result is +# is a feature collection where each feature is a doy representative that +# contains a property (row) describing the respective inter-annual NDVI +# variance, formatted as a list of values. + + +def order_percentiles(img): + stats = ee.Dictionary( + img.reduceRegion( + **{"reducer": ee.Reducer.first(), "geometry": geometry, "scale": 250} + ) + ) + + # Order the percentile reduction elements according to how you want columns + # in the DataTable arranged (x-axis values need to be first). + row = ee.List( + [ + img.get("doy"), + stats.get(band + "_p50"), + stats.get(band + "_p0"), + stats.get(band + "_p25"), + stats.get(band + "_p75"), + stats.get(band + "_p100"), + ] + ) + + # Return the row as a property of an ee.Feature. + return ee.Feature(None, {"row": row}) + + +reduction_table = comp.map(order_percentiles) + +# Aggregate the 'row' properties to make a server-side 2-D array (DataTable). +data_table_server = reduction_table.aggregate_array("row") + +# Define column names and properties for the DataTable. The order should +# correspond to the order in the construction of the 'row' property above. +column_header = ee.List([["DOY", "median", "p0", "p25", "p75", "p100"]]) + +# Concatenate the column header to the table. +data_table_server = column_header.cat(data_table_server) +``` + +```{code-cell} ipython3 +df = chart.DataTable(data_table_server) +df.head() +``` + +```{code-cell} ipython3 +fig = chart.Chart( + df, + chart_type="IntervalChart", + x_cols="DOY", + y_cols=["p0", "p25", "median", "p75", "p100"], + title="Annual NDVI Time Series with Inter-Annual Variance", + x_label="Day of Year", + y_label="Vegetation index (x1e4)", + stroke_width=1, + fill="between", + fill_colors=["#b6d1c6", "#83b191", "#83b191", "#b6d1c6"], + fill_opacities=[0.6] * 4, + labels=["p0", "p25", "median", "p75", "p100"], + display_legend=True, + legend_location="top-right", + ylim=(0, 10000), +) +fig +``` + +![](https://i.imgur.com/i8ZrGPR.png) + ++++ + +## Summary + +This lecture introduced **geemap**, a Python library designed to streamline geospatial data visualization and analysis in the **Google Earth Engine (GEE)** environment. Geemap enables users to perform GIS and remote sensing tasks with GEE data interactively in Python notebooks. Key topics included setting up GEE with geemap, loading and manipulating data from the Earth Engine Data Catalog, and processing vector and raster data. Techniques for creating animated timelapses using data from Landsat, Sentinel, MODIS, and GOES illustrated dynamic landscape monitoring, while interactive charting tools, like scatter plots and time series, supported data exploration and trend analysis. The lecture also covered exporting images and features in multiple formats, such as GeoTIFF, Shapefile, and CSV, to facilitate data sharing. Overall, this session provided a foundational understanding of geemap's capabilities, enabling users to efficiently visualize, analyze, and derive insights from Earth observation data in a Python setting.