{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\"Open\n", "\n", "Uncomment the following line to install [geemap](https://geemap.org) if needed." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# !pip install geemap" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# How to use Earth Engine with pydeck for 3D terrain visualization\n", "Pydeck + Earth Engine: Terrain Visualization\n", "\n", "**Requirements**\n", "\n", "- [earthengine-api](https://github.com/google/earthengine-api): a Python client library for calling the Google Earth Engine API.\n", "- [pydeck](https://pydeck.gl/index.html): a WebGL-powered framework for visual exploratory data analysis of large datasets.\n", "- [pydeck-earthengine-layers](https://github.com/UnfoldedInc/earthengine-layers/tree/master/py): a pydekc wrapper for Google Earth Engine. For documentation please visit this [website](https://earthengine-layers.com/).\n", "- [Mapbox API key](https://pydeck.gl/installation.html#getting-a-mapbox-api-key): you will need this add basemap tiles to pydeck.\n", "\n", "**Installation**\n", "\n", "- conda create -n deck python\n", "- conda activate deck\n", "- conda install mamba -c conda-forge\n", "- mamba install earthengine-api pydeck pydeck-earthengine-layers -c conda-forge\n", "- jupyter nbextension install --sys-prefix --symlink --overwrite --py pydeck\n", "- jupyter nbextension enable --sys-prefix --py pydeck" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example is adopted from [here](https://github.com/UnfoldedInc/earthengine-layers/blob/master/py/examples/terrain.ipynb). Credits to the developers of the [pydeck-earthengine-layers](https://github.com/UnfoldedInc/earthengine-layers) package. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2D Visualization" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ee\n", "import geemap" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Map = geemap.Map()\n", "Map" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "image = ee.Image('USGS/NED').select('elevation')\n", "vis_params={\n", " \"min\": 0, \n", " \"max\": 4000,\n", " \"palette\": ['006633', 'E5FFCC', '662A00', 'D8D8D8', 'F5F5F5']\n", "}\n", "Map.addLayer(image, vis_params, 'NED')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3D Visualization\n", "\n", "### Import libraries" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, import required packages. Note that here we import the `EarthEngineTerrainLayer` instead of the `EarthEngineLayer`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ee\n", "import pydeck as pdk\n", "from pydeck_earthengine_layers import EarthEngineTerrainLayer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Authenticate with Earth Engine\n", "\n", "Using Earth Engine requires authentication. If you don't have a Google account approved for use with Earth Engine, you'll need to request access. For more information and to sign up, go to https://signup.earthengine.google.com/." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "try:\n", " ee.Initialize()\n", "except Exception as e:\n", " ee.Authenticate()\n", " ee.Initialize()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Terrain Example\n", "\n", "In contrast to the `EarthEngineLayer`, where you need to supply _one_ Earth Engine object to render, with the `EarthEngineTerrainLayer` you must supply **two** Earth Engine objects. The first is used to render the image in the same way as the `EarthEngineLayer`; the second supplies elevation values used to extrude terrain in 3D. Hence the former can be any `Image` object; the latter must be an `Image` object whose values represents terrain heights.\n", "\n", "It's important for the terrain source to have relatively high spatial resolution. In previous examples, we used [SRTM90][srtm90] as an elevation source, but that only has a resolution of 90 meters. When used as an elevation source, it looks very blocky/pixelated at high zoom levels. In this example we'll use [SRTM30][srtm30] (30-meter resolution) as the `Image` source and the [USGS's National Elevation Dataset][ned] (10-meter resolution, U.S. only) as the terrain source. SRTM30 is generally the best-resolution worldwide data source available.\n", "\n", "[srtm90]: https://developers.google.com/earth-engine/datasets/catalog/CGIAR_SRTM90_V4\n", "[srtm30]: https://developers.google.com/earth-engine/datasets/catalog/USGS_SRTMGL1_003\n", "[ned]: https://developers.google.com/earth-engine/datasets/catalog/USGS_NED" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# image = ee.Image('USGS/SRTMGL1_003')\n", "image = ee.Image('USGS/NED').select('elevation')\n", "terrain = ee.Image('USGS/NED').select('elevation')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here `vis_params` consists of parameters that will be passed to the Earth Engine [`visParams` argument][visparams]. Any parameters that you could pass directly to Earth Engine in the code editor, you can also pass here to the `EarthEngineLayer`.\n", "\n", "[visparams]: https://developers.google.com/earth-engine/image_visualization" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vis_params={\n", " \"min\": 0, \n", " \"max\": 4000,\n", " \"palette\": ['006633', 'E5FFCC', '662A00', 'D8D8D8', 'F5F5F5']\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we're ready to create the Pydeck layer. The `EarthEngineLayer` makes this simple. Just pass the Earth Engine object to the class." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Including the `id` argument isn't necessary when you only have one pydeck layer, but it is necessary to distinguish multiple layers, so it's good to get into the habit of including an `id` parameter." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ee_layer = EarthEngineTerrainLayer(\n", " image,\n", " terrain,\n", " vis_params,\n", " id=\"EETerrainLayer\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then just pass this layer to a `pydeck.Deck` instance, and call `.show()` to create a map:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "view_state = pdk.ViewState(\n", " latitude=36.15,\n", " longitude=-111.96,\n", " zoom=10.5, \n", " bearing=-66.16,\n", " pitch=60)\n", "\n", "r = pdk.Deck(\n", " layers=[ee_layer], \n", " initial_view_state=view_state\n", ")\n", "\n", "r.show()" ] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3", "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.8.5" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Table of Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }