{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"<a href=\"https://githubtocolab.com/gee-community/geemap/blob/master/docs/notebooks/37_pydeck_3d.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open in Colab\"/></a>\n",
"\n",
"Uncomment the following line to install [geemap](https://geemap.org) if needed."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"# !pip install geemap"
]
},
{
"cell_type": "markdown",
"id": "2",
"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",
"id": "3",
"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",
"id": "4",
"metadata": {},
"source": [
"## 2D Visualization"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5",
"metadata": {},
"outputs": [],
"source": [
"import ee\n",
"import geemap"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6",
"metadata": {},
"outputs": [],
"source": [
"Map = geemap.Map()\n",
"Map"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"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",
"id": "8",
"metadata": {},
"source": [
"## 3D Visualization\n",
"\n",
"### Import libraries"
]
},
{
"cell_type": "markdown",
"id": "9",
"metadata": {},
"source": [
"First, import required packages. Note that here we import the `EarthEngineTerrainLayer` instead of the `EarthEngineLayer`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10",
"metadata": {},
"outputs": [],
"source": [
"import ee\n",
"import pydeck as pdk\n",
"from pydeck_earthengine_layers import EarthEngineTerrainLayer"
]
},
{
"cell_type": "markdown",
"id": "11",
"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,
"id": "12",
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" ee.Initialize()\n",
"except Exception as e:\n",
" ee.Authenticate()\n",
" ee.Initialize()"
]
},
{
"cell_type": "markdown",
"id": "13",
"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,
"id": "14",
"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",
"id": "15",
"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,
"id": "16",
"metadata": {},
"outputs": [],
"source": [
"vis_params = {\n",
" \"min\": 0,\n",
" \"max\": 4000,\n",
" \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "17",
"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",
"id": "18",
"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,
"id": "19",
"metadata": {},
"outputs": [],
"source": [
"ee_layer = EarthEngineTerrainLayer(image, terrain, vis_params, id=\"EETerrainLayer\")"
]
},
{
"cell_type": "markdown",
"id": "20",
"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,
"id": "21",
"metadata": {},
"outputs": [],
"source": [
"view_state = pdk.ViewState(\n",
" latitude=36.15, longitude=-111.96, zoom=10.5, bearing=-66.16, pitch=60\n",
")\n",
"\n",
"r = pdk.Deck(layers=[ee_layer], initial_view_state=view_state)\n",
"\n",
"r.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}