From 25e64433aa3906e420a2aa1537dc7ca2f1fcb52f Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Tue, 23 Jun 2026 15:10:47 -0700 Subject: [PATCH 1/8] Create README.md for ee_data_exploration Create README.md for ee_data_exploration and associated folder structure. --- .../notebooks/ee_data_exploration/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 custom_satellite_embeddings/notebooks/ee_data_exploration/README.md diff --git a/custom_satellite_embeddings/notebooks/ee_data_exploration/README.md b/custom_satellite_embeddings/notebooks/ee_data_exploration/README.md new file mode 100644 index 0000000..4b7ea5e --- /dev/null +++ b/custom_satellite_embeddings/notebooks/ee_data_exploration/README.md @@ -0,0 +1,12 @@ +In this guide, you can explore a time series of six monthly satellite embeddings images in an agricultural area of the California, USA central valley. +Like the [annual embeddings](https://developers.google.com/earth-engine/datasets/catalog/GOOGLE_SATELLITE_EMBEDDING_V1_ANNUAL), +each custom satellite embeddings image has 64 information-dense bands. This notebook demonstrates how to: + +- Examine a single image +- Make a spatial mosaic +- Make a multi-temporal RGB visualization of one band +- Make a multi-temporal RGB visualization of change +- Make a multi-temporal RGB visualization of similarity + +For more information about Custom Satellite Embeddings, see +[the documentation](https://developers.google.com/maps/documentation/custom-satellite-embeddings). From dfdd3ccd9704c49f457693e055c300bd3e8627b3 Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Tue, 23 Jun 2026 15:15:46 -0700 Subject: [PATCH 2/8] Upload data exploration notebook. Upload Custom_Satellite_Embeddings_Data_Exploration.ipynb --- ...atellite_Embeddings_Data_Exploration.ipynb | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb diff --git a/custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb b/custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb new file mode 100644 index 0000000..0569f62 --- /dev/null +++ b/custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb @@ -0,0 +1,375 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "96k7JUiHDEv2" + }, + "outputs": [], + "source": [ + "# Copyright 2026 Google LLC\n", + "#\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + " \n", + "
\n", + " \n", + " \"Google
Open in Colab Enterprise\n", + "
\n", + "
" + ], + "metadata": { + "id": "Kmf1K3lUDnPr" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Custom Satellite Embeddings Data Exploration\n", + "\n", + "Custom satellite embeddings imagery is delivered as [Cloud Optimized Geotiffs](https://cogeo.org/) (COGs) in [Google Cloud Storage](https://cloud.google.com/storage). COGs in Cloud Storage can be loaded into [Google Earth Engine](https://earthengine.google.com) for visualization and analysis. They can be loaded directly using [ee.Image.loadGeoTIFF](https://developers.google.com/earth-engine/apidocs/ee-image-loadgeotiff). Or you can create Earth Engine `Image` assets backed by COGs in Cloud Storage. The advantage of creating COG-backed assets is that indices on spatial and metadata properties make collections of COG-backed assets performant. See [this guide](https://developers.google.com/earth-engine/Earth_Engine_asset_from_cloud_geotiff) for information on creating COG-backed Earth Engine assets. This guide assumes that Earth Engine `ImageCollection`s of COG-backed images are already created.\n", + "\n", + "In this guide, you can explore a time series of six monthly satellite embeddings images in an agricultural area of the California, USA central valley. Like the [annual embeddings](https://developers.google.com/earth-engine/datasets/catalog/GOOGLE_SATELLITE_EMBEDDING_V1_ANNUAL), each image has 64 information-dense bands." + ], + "metadata": { + "id": "NKzgZyG8K-Se" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Earth Engine Authentication and Initialization\n", + "\n", + "To use Earth Engine, you must have a Cloud Project with the Earth Engine API activated." + ], + "metadata": { + "id": "HGoBIB4GN9F-" + } + }, + { + "cell_type": "code", + "source": [ + "import ee\n", + "import geemap\n", + "\n", + "from IPython.display import Image, display\n", + "from pprint import pprint" + ], + "metadata": { + "id": "LSoNkZrkWoCC" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Your project with the Earth Engine API activated.\n", + "PROJECT = \"YOUR-PROJECT\"" + ], + "metadata": { + "id": "JpJ-w6dDHYCL" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Initialize Earth Engine (Assumes you have already authenticated via gcloud)\n", + "try:\n", + " ee.Initialize(project=PROJECT)\n", + "except Exception:\n", + " ee.Authenticate()\n", + " ee.Initialize(project=PROJECT)" + ], + "metadata": { + "id": "0XT8DsPROapQ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Load the satellite embeddings `ImageCollection`\n", + "\n", + "This sample is a time series of monthly images, centered on the 15th of the month, and spanning six months between 2025-11-15 and 2026-05-15. The data covers an area of ~18,000 hectares of mostly cropland in California." + ], + "metadata": { + "id": "6e1dQHpoOnoI" + } + }, + { + "cell_type": "code", + "source": [ + "meloland_california_2026_crop_cycles = ee.ImageCollection(\n", + " \"projects/mldp-trusted-testers-external/assets/alphaearth_foundations/preview/demos/meloland_california_2026_crop_cycles\")" + ], + "metadata": { + "id": "-6tnd_TzOmt2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Examine a single image\n", + "\n", + "Use `getInfo()` to request information about the Earth Engine object to the client (this notebook). This will print information about the properties (metadata) of the image, bands of the image and backing COGs." + ], + "metadata": { + "id": "-iZ9_doRdJBM" + } + }, + { + "cell_type": "code", + "source": [ + "pprint(meloland_california_2026_crop_cycles.first().getInfo())" + ], + "metadata": { + "id": "_km51o8idN2v" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Spatial mosaicking\n", + "\n", + "Since Satellite embeddings are tiled, first aggregate the collection spatially, such that each image in the collection represents one time. Here `mosaic()` is used to spatially aggregate the imagery." + ], + "metadata": { + "id": "CBEBYfRrS9Yu" + } + }, + { + "cell_type": "code", + "source": [ + "# Spatial mosaicking.\n", + "time_starts = meloland_california_2026_crop_cycles.aggregate_histogram('system:time_start')\n", + "\n", + "def mosaic_function(time):\n", + " time_start = ee.Number.parse(time)\n", + " date = ee.Date(time_start)\n", + " filtered = meloland_california_2026_crop_cycles.filterDate(date)\n", + " # Transfer time and geographic properties.\n", + " return filtered.mosaic().set('system:time_start', time_start).clip(filtered.geometry(100))\n", + "\n", + "c = ee.ImageCollection.fromImages(time_starts.keys().map(mosaic_function))" + ], + "metadata": { + "id": "15F9O5vZT3R1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Make a multi-temporal RGB visualization of one band\n", + "\n", + "Each satellite embeddings image has 64 bands and each band may or may not have a physical interpretation. The following is just a visualization to illustrate change over the six month period. Use the `geemap` library for display." + ], + "metadata": { + "id": "1E_5ChrkQvHQ" + } + }, + { + "cell_type": "code", + "source": [ + "# Three 64D temporal slices.\n", + "r = c.filterDate('2025-11-01', '2025-12-31').mean()\n", + "g = c.filterDate('2026-01-01', '2026-02-28').mean()\n", + "b = c.filterDate('2026-03-01', '2026-04-30').mean()\n", + "# Band 45 RGB.\n", + "b_index = 45\n", + "rgb1 = ee.Image.cat(r.select(b_index), g.select(b_index), b.select(b_index))" + ], + "metadata": { + "id": "Cj5VTspSXArV" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Display the image interactively with `geemap`." + ], + "metadata": { + "id": "eSJpbY-RiRqV" + } + }, + { + "cell_type": "code", + "source": [ + "Map = geemap.Map(center=(32.8287, -115.45131), zoom=13)\n", + "Map.addLayer(rgb1, {\"min\": -0.36, \"max\": 0.03}, \"rgb1\")\n", + "Map" + ], + "metadata": { + "id": "RAp4Ch2mXUXN" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Or request a thumbnail URL, which you can display using an IPython widget, download with `curl`, copy/paste, etc. These URLs are temporary and should not be used for long term access." + ], + "metadata": { + "id": "cIQ4daz1iWwu" + } + }, + { + "cell_type": "code", + "source": [ + "thumb_url = rgb1.clip(c.first().geometry()).getThumbUrl({\"min\": -0.36, \"max\": 0.03, \"dimensions\": \"512x512\"})\n", + "display(Image(url=thumb_url))" + ], + "metadata": { + "id": "YwLDKVLvgtBY" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Make a multi-temporal RGB visualization of change\n", + "\n", + "Here we'll represent change as the dot product (similarity) between mean slices of the time series and a reference year. To create a visualization, use the three dot products (from different times) as three bands in an RGB image. Redder pixels indicate greater similarity to reference conditions at the start of the period, greener pixels indicate greater similarity to reference conditions in the middle of the period, etc.\n", + "\n", + "Note that the 64D satellite embeddings are length-1 until you take the mean of a bunch of vectors. The functions needed to compute those dot products are defined here." + ], + "metadata": { + "id": "JSdbl0cOZizs" + } + }, + { + "cell_type": "code", + "source": [ + "def dot(im1: ee.Image, im2: ee.Image) -> ee.Image:\n", + " \"\"\"Dot product of length-1 images.\"\"\"\n", + " return im1.multiply(im2).reduce(\"sum\")\n", + "\n", + "\n", + "def scaled_dot(im1: ee.Image, im2: ee.Image) -> ee.Image:\n", + " \"\"\"Dot product of images that might not be length 1.\"\"\"\n", + " return dot(im1, im2).divide(dot(im1, im1).sqrt()).divide(dot(im2, im2).sqrt())\n", + "\n", + "\n", + "# Similarities of the means to a reference year (2024).\n", + "aef = ee.ImageCollection(\"GOOGLE/SATELLITE_EMBEDDING/V1/ANNUAL\")\n", + "aef_2024 = aef.filter(ee.Filter.calendarRange(2024, 2024, 'year')).mean()\n", + "rgb2 = ee.Image.cat(\n", + " scaled_dot(aef_2024, r),\n", + " scaled_dot(aef_2024, g),\n", + " scaled_dot(aef_2024, b)\n", + ")\n", + "Map.addLayer(rgb2, {\"min\": 0.42, \"max\": 0.95}, \"rgb2\")\n", + "Map" + ], + "metadata": { + "id": "SkXbjwXWa7t1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Make a multi-temporal RGB visualization of similarity\n", + "\n", + "To find the pixels most similar to a single location over time, first compute the reference embedding at a single location. Then compute the dot product between that one reference and the pixels in the three temporal slices. Use a point in an arbitrary field in the image." + ], + "metadata": { + "id": "ss0RmRIQaI-1" + } + }, + { + "cell_type": "code", + "source": [ + "point = ee.Geometry.Point([-115.47235642112162, 32.83620978393891])\n", + "Map.addLayer(point, {}, \"point\")" + ], + "metadata": { + "id": "1C1izmrWaI-1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# One (user specified) point overlaid on the first image.\n", + "mean_aef = c.first().reduceRegion(\n", + " reducer=\"mean\",\n", + " geometry=point,\n", + " scale=10\n", + ")\n", + "\n", + "# Mean similarities to the one point.\n", + "similarities = c.map(\n", + " lambda image: scaled_dot(image, mean_aef.toImage()).set('system:time_start', image.date().millis()))\n", + "rgb3 = ee.Image.cat(\n", + " similarities.filterDate('2025-11-01', '2025-12-31').mean(),\n", + " similarities.filterDate('2026-01-01', '2026-02-28').mean(),\n", + " similarities.filterDate('2026-03-01', '2026-04-30').mean())\n", + "Map.addLayer(rgb3, {\"min\": 0.2, \"max\": 0.8}, \"rgb3\")\n", + "Map" + ], + "metadata": { + "id": "vEVWcV5zaPeF" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## What's next?\n", + "\n", + "Explore [other Custom Satellite Embeddings samples](https://developers.devsite.corp.google.com/maps/documentation/custom-satellite-embeddings/samples)." + ], + "metadata": { + "id": "tt2vtzYSjvFN" + } + } + ] +} \ No newline at end of file From b11f020cec190df919780d06f1fcbb419d279f65 Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Tue, 23 Jun 2026 15:18:01 -0700 Subject: [PATCH 3/8] Update README.md Add title and fix formatting. --- .../notebooks/ee_data_exploration/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/custom_satellite_embeddings/notebooks/ee_data_exploration/README.md b/custom_satellite_embeddings/notebooks/ee_data_exploration/README.md index 4b7ea5e..c2085aa 100644 --- a/custom_satellite_embeddings/notebooks/ee_data_exploration/README.md +++ b/custom_satellite_embeddings/notebooks/ee_data_exploration/README.md @@ -1,6 +1,9 @@ -In this guide, you can explore a time series of six monthly satellite embeddings images in an agricultural area of the California, USA central valley. -Like the [annual embeddings](https://developers.google.com/earth-engine/datasets/catalog/GOOGLE_SATELLITE_EMBEDDING_V1_ANNUAL), -each custom satellite embeddings image has 64 information-dense bands. This notebook demonstrates how to: +# Custom Satellite Embeddings Data Exploration in Earth Engine + +In this guide, you can explore a time series of six monthly satellite embeddings images in an agricultural area of the California, USA central valley. Like the [annual embeddings](https://developers.google.com/earth-engine/datasets/catalog/GOOGLE_SATELLITE_EMBEDDING_V1_ANNUAL), +each custom satellite embeddings image has 64 information-dense bands. + +This notebook demonstrates how to: - Examine a single image - Make a spatial mosaic From f78b45f230b1f402c9ad8c53df22647371d5ec1e Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Tue, 23 Jun 2026 15:23:26 -0700 Subject: [PATCH 4/8] Create classification README.md Create classification README.md and associate directory structure. --- .../notebooks/ee_classification/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 custom_satellite_embeddings/notebooks/ee_classification/README.md diff --git a/custom_satellite_embeddings/notebooks/ee_classification/README.md b/custom_satellite_embeddings/notebooks/ee_classification/README.md new file mode 100644 index 0000000..0e97ee9 --- /dev/null +++ b/custom_satellite_embeddings/notebooks/ee_classification/README.md @@ -0,0 +1,12 @@ +# Custom Satellite Embeddings Supervised and Unsupervised Classification in Earth Engine + +In this guide, supervised and unsupervised classifications are fitted to a time series of +six monthly satellite embeddings images in an agricultural area of the California, USA central valley. +Use these methods to indentify areas of agricultural change or temporal trajectories of +particular crop types. An animated visualizion is produced to illustrate change over time. + +This guide demonstrates: + +- Unsupervised classification in Earth Engine +- Supervised Classification (Random Forest) in Earth Engine +- Making an animation of a time series of classifications From 4fc3ff0705b7dc0c08cb965fde47c3f17fbfea5f Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Tue, 23 Jun 2026 15:28:11 -0700 Subject: [PATCH 5/8] Upload classification notebook. Upload Custom_Satellite_Embeddings_Classification.ipynb --- ..._Satellite_Embeddings_Classification.ipynb | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb diff --git a/custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb b/custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb new file mode 100644 index 0000000..c6823bb --- /dev/null +++ b/custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb @@ -0,0 +1,350 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "96k7JUiHDEv2" + }, + "outputs": [], + "source": [ + "# Copyright 2026 Google LLC\n", + "#\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "source": [ + "\n", + " \n", + "
\n", + " \n", + " \"Google
Open in Colab Enterprise\n", + "
\n", + "
" + ], + "metadata": { + "id": "Kmf1K3lUDnPr" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Custom Satellite Embeddings Supervised and Unsupervised Classification\n", + "\n", + "Custom satellite embeddings imagery is delivered as [Cloud Optimized Geotiffs](https://cogeo.org/) (COGs) in [Google Cloud Storage](https://cloud.google.com/storage). This guide assumes that [Google Earth Engine](https://earthengine.google.com) `ImageCollection`s of COG-backed images are already created. See the Custom Satellite Embeddings Data Exploration guide for more information about the embeddings and COGs in Earth Engine.\n", + "\n", + "In this guide, supervised and unsupervised classifications are fitted to a time series of six monthly satellite embeddings images in an agricultural area of the California, USA central valley. Use these methods to indentify areas of agricultural change or temporal trajectories of particular crop types. An animated visualizion is produced to illustrate change over time." + ], + "metadata": { + "id": "NKzgZyG8K-Se" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Earth Engine Authentication and Initialization\n", + "\n", + "To use Earth Engine, you must have a Cloud Project with the Earth Engine API activated." + ], + "metadata": { + "id": "HGoBIB4GN9F-" + } + }, + { + "cell_type": "code", + "source": [ + "import ee\n", + "import geemap\n", + "\n", + "from IPython.display import Image, display\n", + "from pprint import pprint" + ], + "metadata": { + "id": "LSoNkZrkWoCC" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Your project with the Earth Engine API activated.\n", + "PROJECT = \"YOUR-PROJECT\"" + ], + "metadata": { + "id": "JpJ-w6dDHYCL" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Initialize Earth Engine (Assumes you have already authenticated via gcloud)\n", + "try:\n", + " ee.Initialize(project=PROJECT)\n", + "except Exception:\n", + " ee.Authenticate()\n", + " ee.Initialize(project=PROJECT)" + ], + "metadata": { + "id": "0XT8DsPROapQ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Load the satellite embeddings `ImageCollection`\n", + "\n", + "This sample is a time series of monthly images, centered on the 15th of the month, and spanning six months between 2025-11-15 and 2026-05-15. The data covers an area of ~18,000 hectares of mostly cropland in California." + ], + "metadata": { + "id": "6e1dQHpoOnoI" + } + }, + { + "cell_type": "code", + "source": [ + "meloland_california_2026_crop_cycles = ee.ImageCollection(\n", + " \"projects/mldp-trusted-testers-external/assets/alphaearth_foundations/preview/demos/meloland_california_2026_crop_cycles\")" + ], + "metadata": { + "id": "-6tnd_TzOmt2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Spatial mosaicking\n", + "\n", + "Since Satellite embeddings are tiled, first aggregate the collection spatially, such that each image in the collection represents one time. Here `mosaic()` is used to spatially aggregate the imagery." + ], + "metadata": { + "id": "CBEBYfRrS9Yu" + } + }, + { + "cell_type": "code", + "source": [ + "# Spatial mosaicking.\n", + "time_starts = meloland_california_2026_crop_cycles.aggregate_histogram('system:time_start')\n", + "\n", + "def mosaic_function(time):\n", + " time_start = ee.Number.parse(time)\n", + " date = ee.Date(time_start)\n", + " filtered = meloland_california_2026_crop_cycles.filterDate(date)\n", + " # Transfer time and geographic properties.\n", + " return filtered.mosaic().set('system:time_start', time_start).clip(filtered.geometry(100))\n", + "\n", + "c = ee.ImageCollection.fromImages(time_starts.keys().map(mosaic_function))" + ], + "metadata": { + "id": "15F9O5vZT3R1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Unsupervised Classification\n", + "\n", + "### Stack the entire time series in a single image\n", + "\n", + "Stack each of the six 64-band images into a single image. Use clusters in this space to represent temporal trajectories of different crops through time. Earth Engine has native methods for sampling and clustering. Here Weka's k-means clusterer with seven clusters is used." + ], + "metadata": { + "id": "1E_5ChrkQvHQ" + } + }, + { + "cell_type": "code", + "source": [ + "# K-means on the 6x64D stack.\n", + "k = 7\n", + "n = 1000\n", + "\n", + "# Temporal trajectory clusters.\n", + "big_stack = c.toBands()\n", + "training = big_stack.sample(\n", + " region=c.first().geometry(),\n", + " numPixels=n,\n", + " scale=10,\n", + ")\n", + "training = training.filter(ee.Filter.notNull(big_stack.bandNames()))\n", + "\n", + "clusterer = ee.Clusterer.wekaKMeans(k).train(training, big_stack.bandNames())\n", + "clustered = big_stack.cluster(clusterer)" + ], + "metadata": { + "id": "Cj5VTspSXArV" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Use `geemap` to visualize the results interactively." + ], + "metadata": { + "id": "oqNYdrp1rDLZ" + } + }, + { + "cell_type": "code", + "source": [ + "Map = geemap.Map(center=(32.8287, -115.45131), zoom=13)\n", + "roygbiv = [\"FFFFFF\", \"#F60000\", \"#FF8C00\", \"#FFEE00\", \"#4DE94C\", \"#3783FF\", \"#4815AA\"]\n", + "cluster_vis = {\"min\": 0, \"max\": k-1, \"palette\": roygbiv}\n", + "Map.addLayer(clustered, cluster_vis, 'clustered')\n", + "Map" + ], + "metadata": { + "id": "-FGSbmU3opE9" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Or request a thumbnail URL, which you can display using an IPython widget, download with `curl`, copy/paste, etc. These URLs are temporary and should not be used for long term access." + ], + "metadata": { + "id": "cIQ4daz1iWwu" + } + }, + { + "cell_type": "code", + "source": [ + "thumb_clusters_vis = cluster_vis.copy()\n", + "thumb_clusters_vis[\"dimensions\"] = \"512x512\"\n", + "thumb_url = clustered.clip(c.first().geometry()).getThumbUrl(thumb_clusters_vis)\n", + "display(Image(url=thumb_url))" + ], + "metadata": { + "id": "YwLDKVLvgtBY" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Supervised Classification\n", + "\n", + "### Use the annual USDA Cropland Data Layer (CDL) as reference labels\n", + "\n", + "For reference labels, overlay the annual CDL crop type map with annual satellite embeddings to get a training dataset. Train a Random Forest classifier to predict the top five crops in the specified region. Classify every image in the custom satellite embeddings time series, then make an animated thumbnail of the time series of classifications." + ], + "metadata": { + "id": "JSdbl0cOZizs" + } + }, + { + "cell_type": "code", + "source": [ + "# CDL classification for 2025.\n", + "cdl = ee.ImageCollection(\"USDA/NASS/CDL\")\n", + "cdl_2025 = cdl.filter(ee.Filter.calendarRange(2025, 2025, 'year')).first()\n", + "\n", + "# Sample the CDL and annual embeddings for the same year.\n", + "aef = ee.ImageCollection(\"GOOGLE/SATELLITE_EMBEDDING/V1/ANNUAL\")\n", + "aef_2025 = aef.filter(ee.Filter.calendarRange(2025, 2025, 'year')).mosaic()\n", + "sample = aef_2025.addBands(cdl_2025).sample(\n", + " region=c.first().geometry(),\n", + " numPixels=n,\n", + " scale=10,\n", + ")\n", + "\n", + "# Dominant classes only, the top five.\n", + "cropland = 'cropland'\n", + "histo = ee.Dictionary(sample.aggregate_histogram(cropland))\n", + "keys = histo.keys().sort(histo.values())\n", + "top_classes = keys.slice(keys.length().subtract(5))\n", + "\n", + "def remap_class(f: ee.Feature) -> ee.Feature:\n", + " \"\"\"Remap the top five classes to consecutive integers, all else to 0.\"\"\"\n", + " old_class = f.get(cropland)\n", + " new_class = top_classes.indexOf(ee.String(old_class)).add(1)\n", + " return f.set(cropland, new_class)\n", + "\n", + "training = sample.map(remap_class)\n", + "rf = ee.Classifier.smileRandomForest(10).train(training, cropland, aef_2025.bandNames())" + ], + "metadata": { + "id": "xW8MWoMovF2A" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Display an animation of the classificatons." + ], + "metadata": { + "id": "RxSK8VMU3Otq" + } + }, + { + "cell_type": "code", + "source": [ + "rf_vis = {\"min\": 0, \"max\": 5, \"palette\": roygbiv[0:6]}\n", + "# Classify and visualize each image in the time series.\n", + "classified = c.map(lambda image: image.classify(rf).visualize(**rf_vis))\n", + "# Get a URL for the animation and use an IPython widget to display it.\n", + "anim_url = classified.getVideoThumbURL({ \"framesPerSecond\": 3, \"dimensions\": \"512x512\" })\n", + "display(Image(url=anim_url))" + ], + "metadata": { + "id": "3pP7IXU-0KOl" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## What's next?\n", + "\n", + "Explore [other Custom Satellite Embeddings samples](https://developers.devsite.corp.google.com/maps/documentation/custom-satellite-embeddings/samples)." + ], + "metadata": { + "id": "tt2vtzYSjvFN" + } + } + ] +} \ No newline at end of file From 8bdfdbaa639b605af234617cff93b79f55edfa0a Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Tue, 23 Jun 2026 15:31:10 -0700 Subject: [PATCH 6/8] Update insights README.md Update insights README.md to include links to Custom Satellite Embeddings docs. --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b894880..b6f1184 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ You can learn more about the products on the [product page](https://mapsplatform [Docs](https://developers.google.com/maps/documentation/population-dynamics-insights) +### Custom Satellite Embeddings + +[Docs](https://developers.google.com/maps/documentation/custom-satellite-embeddings) + ## Available Recipe Collections * [`places_insights/`](places_insights/): Samples for analyzing the Places Insights BigQuery dataset. @@ -48,4 +52,4 @@ See [`SECURITY.md`](SECURITY.md) for details on how to report security vulnerabi ## Contributing -See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on how to contribute to this project. \ No newline at end of file +See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on how to contribute to this project. From eda4b8bcf158fe536a8a6b4b8cfb4459aee2548f Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Fri, 26 Jun 2026 12:34:39 -0700 Subject: [PATCH 7/8] Update Custom_Satellite_Embeddings_Classification.ipynb Delete old asset ID. --- .../Custom_Satellite_Embeddings_Classification.ipynb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb b/custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb index c6823bb..09242db 100644 --- a/custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb +++ b/custom_satellite_embeddings/notebooks/ee_classification/Custom_Satellite_Embeddings_Classification.ipynb @@ -134,8 +134,7 @@ { "cell_type": "code", "source": [ - "meloland_california_2026_crop_cycles = ee.ImageCollection(\n", - " \"projects/mldp-trusted-testers-external/assets/alphaearth_foundations/preview/demos/meloland_california_2026_crop_cycles\")" + "meloland_california_2026_crop_cycles = ee.ImageCollection()" ], "metadata": { "id": "-6tnd_TzOmt2" @@ -347,4 +346,4 @@ } } ] -} \ No newline at end of file +} From d037e5233b95dbc601b8d77209e6dfe100664331 Mon Sep 17 00:00:00 2001 From: Nicholas Clinton Date: Fri, 26 Jun 2026 12:35:29 -0700 Subject: [PATCH 8/8] Update Custom_Satellite_Embeddings_Data_Exploration.ipynb Delete old asset IDs --- .../Custom_Satellite_Embeddings_Data_Exploration.ipynb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb b/custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb index 0569f62..4a9dd0e 100644 --- a/custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb +++ b/custom_satellite_embeddings/notebooks/ee_data_exploration/Custom_Satellite_Embeddings_Data_Exploration.ipynb @@ -134,8 +134,7 @@ { "cell_type": "code", "source": [ - "meloland_california_2026_crop_cycles = ee.ImageCollection(\n", - " \"projects/mldp-trusted-testers-external/assets/alphaearth_foundations/preview/demos/meloland_california_2026_crop_cycles\")" + "meloland_california_2026_crop_cycles = ee.ImageCollection()" ], "metadata": { "id": "-6tnd_TzOmt2" @@ -372,4 +371,4 @@ } } ] -} \ No newline at end of file +}