From 09eadbf90ef3cbc4f6f9ff0cf05d447634b2067d Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 29 Jun 2024 01:14:26 -0400 Subject: [PATCH] Improve support for adding GEE layers (#795) * Improve support for adding GEE layers * Add GEE notebook example * Improve notebook --- docs/maplibre/google_earth_engine.ipynb | 227 ++++++++++++++++++++++++ docs/maplibre/overview.md | 6 + leafmap/maplibregl.py | 76 +++++--- mkdocs.yml | 1 + 4 files changed, 288 insertions(+), 22 deletions(-) create mode 100644 docs/maplibre/google_earth_engine.ipynb diff --git a/docs/maplibre/google_earth_engine.ipynb b/docs/maplibre/google_earth_engine.ipynb new file mode 100644 index 0000000000..225bbe7f54 --- /dev/null +++ b/docs/maplibre/google_earth_engine.ipynb @@ -0,0 +1,227 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/google_earth_engine.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/google_earth_engine.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Add Google Earth Engine data layers**\n", + "\n", + "This notebook demonstrates how to add [Google Earth Engine](https://earthengine.google.com) data layers to a map.\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\" geemap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To run this notebook, you will need an [API key](https://docs.maptiler.com/cloud/api/authentication-key/) from [MapTiler](https://www.maptiler.com/cloud/). Once you have the API key, you can set it as an environment variable in your notebook or script as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")\n", + "style = f\"https://api.maptiler.com/maps/streets/style.json?key={MAPTILER_KEY}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can find a list of Earth Engine asset ids from [here](https://github.com/opengeos/ee-tile-layers/blob/main/datasets.tsv), which does not require an Earth Engine account." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(\n", + " center=[-120.4482, 38.0399], zoom=13, pitch=60, bearing=30, style=\"3d-terrain\"\n", + ")\n", + "m.add_ee_layer(asset_id=\"ESA/WorldCover/v200\", opacity=0.5)\n", + "m.add_legend(builtin_legend=\"ESA_WorldCover\", title=\"ESA Landcover\")\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m.layer_interact()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/oHQDf79.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also overlay other data layers on top of Earth Engine data layers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(\n", + " center=[-74.012998, 40.70414], zoom=15.6, pitch=60, bearing=30, style=\"3d-terrain\"\n", + ")\n", + "m.add_ee_layer(asset_id=\"ESA/WorldCover/v200\", opacity=0.5)\n", + "m.add_legend(builtin_legend=\"ESA_WorldCover\", title=\"ESA Landcover\")\n", + "m.add_layer_control()\n", + "\n", + "source = {\n", + " \"url\": f\"https://api.maptiler.com/tiles/v3/tiles.json?key={MAPTILER_KEY}\",\n", + " \"type\": \"vector\",\n", + "}\n", + "\n", + "layer = {\n", + " \"id\": \"3d-buildings\",\n", + " \"source\": \"openmaptiles\",\n", + " \"source-layer\": \"building\",\n", + " \"type\": \"fill-extrusion\",\n", + " \"min-zoom\": 15,\n", + " \"paint\": {\n", + " \"fill-extrusion-color\": [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"get\", \"render_height\"],\n", + " 0,\n", + " \"lightgray\",\n", + " 200,\n", + " \"royalblue\",\n", + " 400,\n", + " \"lightblue\",\n", + " ],\n", + " \"fill-extrusion-height\": [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"zoom\"],\n", + " 15,\n", + " 0,\n", + " 16,\n", + " [\"get\", \"render_height\"],\n", + " ],\n", + " \"fill-extrusion-base\": [\n", + " \"case\",\n", + " [\">=\", [\"get\", \"zoom\"], 16],\n", + " [\"get\", \"render_min_height\"],\n", + " 0,\n", + " ],\n", + " },\n", + "}\n", + "m.add_source(\"openmaptiles\", source)\n", + "m.add_layer(layer)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/Y52jep5.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you have an Earth Engine, you can uncomment the first two code blocks to add any Earth Engine datasets. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# import ee\n", + "# ee.Initialize(project=\"YOUR-PROJECT-ID\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# m = leafmap.Map(center=[-120.4482, 38.03994], zoom=13, pitch=60, bearing=30, style=\"3d-terrain\")\n", + "# dataset = ee.ImageCollection(\"ESA/WorldCover/v200\").first()\n", + "# vis_params = {\"bands\": [\"Map\"]}\n", + "# m.add_ee_layer(dataset, vis_params, name=\"ESA Worldcover\", opacity=0.5)\n", + "# m.add_legend(builtin_legend=\"ESA_WorldCover\", title=\"ESA Landcover\")\n", + "# m.add_layer_control()\n", + "# m" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/overview.md b/docs/maplibre/overview.md index f9e1b41a89..68efab255c 100644 --- a/docs/maplibre/overview.md +++ b/docs/maplibre/overview.md @@ -254,6 +254,12 @@ Add a GeoPandas GeoDataFrame to a map. [![](https://i.imgur.com/CQHcD7N.png)](https://leafmap.org/maplibre/geopandas) +## Google Earth Engine + +Add Google Earth Engine data layers to a map. + +[![](https://i.imgur.com/oHQDf79.png)](https://leafmap.org/maplibre/google_earth_engine) + ## Create a heatmap layer Visualize earthquake frequency by location using a heatmap layer. diff --git a/leafmap/maplibregl.py b/leafmap/maplibregl.py index ec6294089d..273adc6c03 100644 --- a/leafmap/maplibregl.py +++ b/leafmap/maplibregl.py @@ -841,12 +841,15 @@ def add_wms_layer( def add_ee_layer( self, - asset_id: str, + ee_object=None, + vis_params={}, + asset_id: str = None, name: str = None, opacity: float = 1.0, attribution: str = "Google Earth Engine", visible: bool = True, before_id: Optional[str] = None, + ee_initialize: bool = False, **kwargs, ) -> None: """ @@ -854,6 +857,8 @@ def add_ee_layer( https://github.com/opengeos/ee-tile-layers/blob/main/datasets.tsv. Args: + ee_object (object): The Earth Engine object to display. + vis_params (dict): Visualization parameters. For example, {'min': 0, 'max': 100}. asset_id (str): The ID of the Earth Engine asset. name (str, optional): The name of the tile layer. If not provided, the asset ID will be used. Default is None. @@ -865,6 +870,7 @@ def add_ee_layer( the map. Default is True. before_id (str, optional): The ID of an existing layer before which the new layer should be inserted. + ee_initialize (bool, optional): Whether to initialize the Earth Engine **kwargs: Additional keyword arguments to be passed to the underlying `add_tile_layer` method. @@ -873,28 +879,54 @@ def add_ee_layer( """ import pandas as pd - df = pd.read_csv( - "https://raw.githubusercontent.com/opengeos/ee-tile-layers/main/datasets.tsv", - sep="\t", - ) - - asset_id = asset_id.strip() - if name is None: - name = asset_id - - if asset_id in df["id"].values: - url = df.loc[df["id"] == asset_id, "url"].values[0] - self.add_tile_layer( - url, - name, - attribution=attribution, - opacity=opacity, - visible=visible, - before_id=before_id, - **kwargs, + if isinstance(asset_id, str): + df = pd.read_csv( + "https://raw.githubusercontent.com/opengeos/ee-tile-layers/main/datasets.tsv", + sep="\t", ) - else: - print(f"The provided EE tile layer {asset_id} does not exist.") + + asset_id = asset_id.strip() + if name is None: + name = asset_id + + if asset_id in df["id"].values: + url = df.loc[df["id"] == asset_id, "url"].values[0] + self.add_tile_layer( + url, + name, + attribution=attribution, + opacity=opacity, + visible=visible, + before_id=before_id, + **kwargs, + ) + else: + print(f"The provided EE tile layer {asset_id} does not exist.") + elif ee_object is not None: + try: + import geemap + from geemap.ee_tile_layers import _get_tile_url_format + + if ee_initialize: + geemap.ee_initialize() + url = _get_tile_url_format(ee_object, vis_params) + if name is None: + name = "EE Layer" + self.add_tile_layer( + url, + name, + attribution=attribution, + opacity=opacity, + visible=visible, + before_id=before_id, + **kwargs, + ) + except Exception as e: + print(e) + print( + "Please install the `geemap` package to use the `add_ee_layer` function." + ) + return def add_cog_layer( self, diff --git a/mkdocs.yml b/mkdocs.yml index 3e6866fe84..93be6c7aac 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -180,6 +180,7 @@ nav: - maplibre/geojson_points.ipynb - maplibre/geojson_polygon.ipynb - maplibre/geopandas.ipynb + - maplibre/google_earth_engine.ipynb - maplibre/heatmap_layer.ipynb - maplibre/interactive_false.ipynb - maplibre/jump_to.ipynb