{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/workshops/Taiwan_2024.ipynb)\n", "\n", "**3D Mapping with Leafmap and MapLibre**\n", "\n", "- Registration: https://shorturl.at/4J0HW\n", "- Notebook: https://leafmap.org/workshops/Taiwan_2024\n", "- Leafmap: https://leafmap.org\n", "\n", "## Introduction\n", "\n", "This notebook is designed for workshop presented at the [Center for GIS, RCHSS, Academia Sinica](https://gis.rchss.sinica.edu.tw/), Taiwan, on August 7, 2024. Part 1 of the workshop will introduce the Earth Engine Python API and the geemap Python package. Part 2 will focus on 3D mapping with Leafmap and MapLibre.\n", "\n", "### Prerequisites\n", "\n", "- A Google account to access Google Colab\n", "- A [MapTiler API key](https://docs.maptiler.com/cloud/api/authentication-key/) to access MapTiler vector tiles\n", "\n", "### Agenda\n", "\n", "The main topics to be covered in this workshop include:\n", "\n", "- Create interactive 3D maps\n", "- 3D terrain, 3D buildings, and 3D indoor mapping\n", "- Visualize local vector and raster datasets\n", "- Visualize geospatial data in the cloud (COG, STAC, PMTiles)\n", "- Add custom components to the map\n", "- Export 3D maps as HTML files for website hosting" ] }, { "cell_type": "markdown", "id": "1", "metadata": {}, "source": [ "## Installation\n", "\n", "Uncomment the following line to install the required Python packages." ] }, { "cell_type": "code", "execution_count": null, "id": "2", "metadata": {}, "outputs": [], "source": [ "# %pip install -U \"leafmap[maplibre]\" geemap" ] }, { "cell_type": "markdown", "id": "3", "metadata": {}, "source": [ "Import the maplibre mapping backend." ] }, { "cell_type": "code", "execution_count": null, "id": "4", "metadata": {}, "outputs": [], "source": [ "import leafmap.maplibregl as leafmap" ] }, { "cell_type": "markdown", "id": "5", "metadata": {}, "source": [ "## Set up API Key\n", "\n", "To run this notebook, you need to set up a MapTiler API key. You can get a free API key by signing up at [https://cloud.maptiler.com/](https://cloud.maptiler.com/)." ] }, { "cell_type": "code", "execution_count": null, "id": "6", "metadata": {}, "outputs": [], "source": [ "# import os\n", "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" ] }, { "cell_type": "markdown", "id": "7", "metadata": {}, "source": [ "## Create interactive maps\n", "\n", "### Create a simple map\n", "\n", "Let's create a simple interactive map using Leafmap." ] }, { "cell_type": "code", "execution_count": null, "id": "8", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "m" ] }, { "cell_type": "markdown", "id": "9", "metadata": {}, "source": [ "You can customize the map by specifying map center [lon, lat], zoom level, pitch, and bearing." ] }, { "cell_type": "code", "execution_count": null, "id": "10", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, pitch=0, bearing=0)\n", "m" ] }, { "cell_type": "markdown", "id": "11", "metadata": {}, "source": [ "To customize the basemap, you can specify the `style` parameter. It can be an URL or a string, such as `dark-matter`, `positron`, `voyager`, `demotiles`." ] }, { "cell_type": "code", "execution_count": null, "id": "12", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"positron\")\n", "m" ] }, { "cell_type": "markdown", "id": "13", "metadata": {}, "source": [ "To create a map with a background color, use `style=\"background-\"`, such as `background-lightgray` and `background-green`." ] }, { "cell_type": "code", "execution_count": null, "id": "14", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"background-lightgray\")\n", "m" ] }, { "cell_type": "markdown", "id": "15", "metadata": {}, "source": [ "Alternatively, you can provide a URL to a vector style." ] }, { "cell_type": "code", "execution_count": null, "id": "16", "metadata": {}, "outputs": [], "source": [ "style = \"https://demotiles.maplibre.org/style.json\"\n", "m = leafmap.Map(style=style)\n", "m" ] }, { "cell_type": "markdown", "id": "17", "metadata": {}, "source": [ "### Add map controls\n", "\n", "The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`." ] }, { "cell_type": "code", "execution_count": null, "id": "18", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "m.add_control(\"geolocate\", position=\"top-left\")\n", "m" ] }, { "cell_type": "markdown", "id": "19", "metadata": {}, "source": [ "### Add basemaps\n", "\n", "You can add basemaps to the map using the `add_basemap` method. " ] }, { "cell_type": "code", "execution_count": null, "id": "20", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "m.add_basemap(\"OpenTopoMap\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "21", "metadata": {}, "outputs": [], "source": [ "m.add_basemap(\"Esri.WorldImagery\")" ] }, { "cell_type": "code", "execution_count": null, "id": "22", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "m" ] }, { "cell_type": "markdown", "id": "23", "metadata": {}, "source": [ "To add basemaps interactively, use the `add_basemap` method without specifying the `basemap` parameter." ] }, { "cell_type": "code", "execution_count": null, "id": "24", "metadata": {}, "outputs": [], "source": [ "m.add_basemap()" ] }, { "cell_type": "markdown", "id": "25", "metadata": {}, "source": [ "### Add XYZ tile layer\n", "\n", "You can add XYZ tile layers to the map using the `add_tile_layer` method." ] }, { "cell_type": "code", "execution_count": null, "id": "26", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "url = \"https://tile.openstreetmap.org/{z}/{x}/{y}.png\"\n", "m.add_tile_layer(\n", " url, name=\"OpenStreetMap\", attribution=\"OpenStreetMap\", opacity=1.0, visible=True\n", ")\n", "m" ] }, { "cell_type": "markdown", "id": "27", "metadata": {}, "source": [ "### Add WMS layer\n", "\n", "You can add WMS layers to the map using the `add_wms_layer` method." ] }, { "cell_type": "code", "execution_count": null, "id": "28", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style=\"streets\")\n", "url = \"https://img.nj.gov/imagerywms/Natural2015\"\n", "layers = \"Natural2015\"\n", "m.add_wms_layer(url, layers=layers, before_id=\"aeroway_fill\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "29", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100.307965, 46.98692], zoom=13, pitch=45, style=\"3d-hybrid\")\n", "url = \"https://fwspublicservices.wim.usgs.gov/wetlandsmapservice/services/Wetlands/MapServer/WMSServer\"\n", "m.add_wms_layer(url, layers=\"1\", name=\"NWI\", opacity=0.6)\n", "m.add_layer_control(bg_layers=True)\n", "m.add_legend(builtin_legend=\"NWI\", title=\"Wetland Type\")\n", "m" ] }, { "cell_type": "markdown", "id": "30", "metadata": {}, "source": [ "### MapTiler styles\n", "\n", "You can use any named style from MapTiler by setting the style parameter to the name of the style.\n", "\n", "![](https://i.imgur.com/dp2HxR2.png)" ] }, { "cell_type": "code", "execution_count": null, "id": "31", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"streets\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "32", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"satellite\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "33", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"hybrid\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "34", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"topo\")\n", "m" ] }, { "cell_type": "markdown", "id": "35", "metadata": {}, "source": [ "## 3D mapping\n", "\n", "### 3D terrain\n", "\n", "MapTiler provides a variety of basemaps and styles that can be used to create 3D maps. You can use any styles from the MapTiler basemap gallery and prefix the style name with `3d-`. For example, `3d-hybrid`, `3d-satellite`, or `3d-topo`. To use the hillshade only, you can use the `3d-hillshade` style." ] }, { "cell_type": "code", "execution_count": null, "id": "36", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"3d-hybrid\")\n", "m.add_layer_control(bg_layers=True)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "37", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"3d-satellite\")\n", "m.add_layer_control(bg_layers=True)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "38", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"3d-topo\", exaggeration=1.5, hillshade=False)\n", "m.add_layer_control(bg_layers=True)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "39", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"3d-ocean\", exaggeration=1.5, hillshade=True)\n", "m.add_layer_control(bg_layers=True)\n", "m" ] }, { "cell_type": "markdown", "id": "40", "metadata": {}, "source": [ "### 3D buildings\n", "\n", "You can add 3D buildings to the map using the `add_3d_buildings` method. " ] }, { "cell_type": "code", "execution_count": null, "id": "41", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=[-74.0066, 40.7135], zoom=16, pitch=45, bearing=-17, style=\"basic-v2\"\n", ")\n", "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")\n", "m.add_basemap(\"Esri.WorldImagery\", visible=False)\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.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "42", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=[-74.0066, 40.7135], zoom=16, pitch=45, bearing=-17, style=\"basic-v2\"\n", ")\n", "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", "m.add_3d_buildings(min_zoom=15)\n", "m.add_layer_control()\n", "m" ] }, { "cell_type": "markdown", "id": "43", "metadata": {}, "source": [ "### 3D indoor mapping\n", "\n", "Let's visualize indoor mapping data using the `add_geojson` method." ] }, { "cell_type": "code", "execution_count": null, "id": "44", "metadata": {}, "outputs": [], "source": [ "data = \"https://maplibre.org/maplibre-gl-js/docs/assets/indoor-3d-map.geojson\"\n", "gdf = leafmap.geojson_to_gdf(data)\n", "gdf.explore()" ] }, { "cell_type": "code", "execution_count": null, "id": "45", "metadata": {}, "outputs": [], "source": [ "gdf.head()" ] }, { "cell_type": "code", "execution_count": null, "id": "46", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=(-87.61694, 41.86625), zoom=17, pitch=40, bearing=20, style=\"positron\"\n", ")\n", "m.add_basemap(\"OpenStreetMap.Mapnik\")\n", "m.add_geojson(\n", " data,\n", " layer_type=\"fill-extrusion\",\n", " name=\"floorplan\",\n", " paint={\n", " \"fill-extrusion-color\": [\"get\", \"color\"],\n", " \"fill-extrusion-height\": [\"get\", \"height\"],\n", " \"fill-extrusion-base\": [\"get\", \"base_height\"],\n", " \"fill-extrusion-opacity\": 0.5,\n", " },\n", ")\n", "m.add_layer_control()\n", "m" ] }, { "cell_type": "markdown", "id": "47", "metadata": {}, "source": [ "## Visualize vector data\n", "\n", "Leafmap provides a variety of methods to visualize vector data on the map.\n", "\n", "### Point data" ] }, { "cell_type": "code", "execution_count": null, "id": "48", "metadata": {}, "outputs": [], "source": [ "import requests" ] }, { "cell_type": "code", "execution_count": null, "id": "49", "metadata": {}, "outputs": [], "source": [ "url = (\n", " \"https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson\"\n", ")\n", "geojson = requests.get(url).json()" ] }, { "cell_type": "code", "execution_count": null, "id": "50", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"streets\")\n", "m.add_geojson(geojson, name=\"cities\")\n", "m.add_popup(\"cities\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "51", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"streets\")\n", "source = {\"type\": \"geojson\", \"data\": geojson}\n", "\n", "layer = {\n", " \"id\": \"cities\",\n", " \"type\": \"symbol\",\n", " \"source\": \"point\",\n", " \"layout\": {\n", " \"icon-image\": \"marker_15\",\n", " \"icon-size\": 1,\n", " },\n", "}\n", "m.add_source(\"point\", source)\n", "m.add_layer(layer)\n", "m.add_popup(\"cities\")\n", "m" ] }, { "cell_type": "markdown", "id": "52", "metadata": {}, "source": [ "### Line data" ] }, { "cell_type": "code", "execution_count": null, "id": "53", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-122.486052, 37.830348], zoom=15, style=\"streets\")\n", "\n", "source = {\n", " \"type\": \"geojson\",\n", " \"data\": {\n", " \"type\": \"Feature\",\n", " \"properties\": {},\n", " \"geometry\": {\n", " \"type\": \"LineString\",\n", " \"coordinates\": [\n", " [-122.48369693756104, 37.83381888486939],\n", " [-122.48348236083984, 37.83317489144141],\n", " [-122.48339653015138, 37.83270036637107],\n", " [-122.48356819152832, 37.832056363179625],\n", " [-122.48404026031496, 37.83114119107971],\n", " [-122.48404026031496, 37.83049717427869],\n", " [-122.48348236083984, 37.829920943955045],\n", " [-122.48356819152832, 37.82954808664175],\n", " [-122.48507022857666, 37.82944639795659],\n", " [-122.48610019683838, 37.82880236636284],\n", " [-122.48695850372314, 37.82931081282506],\n", " [-122.48700141906738, 37.83080223556934],\n", " [-122.48751640319824, 37.83168351665737],\n", " [-122.48803138732912, 37.832158048267786],\n", " [-122.48888969421387, 37.83297152392784],\n", " [-122.48987674713133, 37.83263257682617],\n", " [-122.49043464660643, 37.832937629287755],\n", " [-122.49125003814696, 37.832429207817725],\n", " [-122.49163627624512, 37.832564787218985],\n", " [-122.49223709106445, 37.83337825839438],\n", " [-122.49378204345702, 37.83368330777276],\n", " ],\n", " },\n", " },\n", "}\n", "\n", "layer = {\n", " \"id\": \"route\",\n", " \"type\": \"line\",\n", " \"source\": \"route\",\n", " \"layout\": {\"line-join\": \"round\", \"line-cap\": \"round\"},\n", " \"paint\": {\"line-color\": \"#888\", \"line-width\": 8},\n", "}\n", "m.add_source(\"route\", source)\n", "m.add_layer(layer)\n", "m" ] }, { "cell_type": "markdown", "id": "54", "metadata": {}, "source": [ "### Polygon data" ] }, { "cell_type": "code", "execution_count": null, "id": "55", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-68.137343, 45.137451], zoom=5, style=\"streets\")\n", "geojson = {\n", " \"type\": \"Feature\",\n", " \"geometry\": {\n", " \"type\": \"Polygon\",\n", " \"coordinates\": [\n", " [\n", " [-67.13734351262877, 45.137451890638886],\n", " [-66.96466, 44.8097],\n", " [-68.03252, 44.3252],\n", " [-69.06, 43.98],\n", " [-70.11617, 43.68405],\n", " [-70.64573401557249, 43.090083319667144],\n", " [-70.75102474636725, 43.08003225358635],\n", " [-70.79761105007827, 43.21973948828747],\n", " [-70.98176001655037, 43.36789581966826],\n", " [-70.94416541205806, 43.46633942318431],\n", " [-71.08482, 45.3052400000002],\n", " [-70.6600225491012, 45.46022288673396],\n", " [-70.30495378282376, 45.914794623389355],\n", " [-70.00014034695016, 46.69317088478567],\n", " [-69.23708614772835, 47.44777598732787],\n", " [-68.90478084987546, 47.184794623394396],\n", " [-68.23430497910454, 47.35462921812177],\n", " [-67.79035274928509, 47.066248887716995],\n", " [-67.79141211614706, 45.702585354182816],\n", " [-67.13734351262877, 45.137451890638886],\n", " ]\n", " ],\n", " },\n", "}\n", "source = {\"type\": \"geojson\", \"data\": geojson}\n", "m.add_source(\"maine\", source)\n", "layer = {\n", " \"id\": \"maine\",\n", " \"type\": \"fill\",\n", " \"source\": \"maine\",\n", " \"layout\": {},\n", " \"paint\": {\"fill-color\": \"#088\", \"fill-opacity\": 0.8},\n", "}\n", "m.add_layer(layer)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "56", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-68.137343, 45.137451], zoom=5, style=\"streets\")\n", "paint = {\"fill-color\": \"#088\", \"fill-opacity\": 0.8}\n", "m.add_geojson(geojson, layer_type=\"fill\", paint=paint)\n", "m" ] }, { "cell_type": "markdown", "id": "57", "metadata": {}, "source": [ "### Multiple geometries" ] }, { "cell_type": "code", "execution_count": null, "id": "58", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=[-123.13, 49.254], zoom=11, style=\"dark-matter\", pitch=45, bearing=0\n", ")\n", "url = \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json\"\n", "paint_line = {\n", " \"line-color\": \"white\",\n", " \"line-width\": 2,\n", "}\n", "paint_fill = {\n", " \"fill-extrusion-color\": {\n", " \"property\": \"valuePerSqm\",\n", " \"stops\": [\n", " [0, \"grey\"],\n", " [1000, \"yellow\"],\n", " [5000, \"orange\"],\n", " [10000, \"darkred\"],\n", " [50000, \"lightblue\"],\n", " ],\n", " },\n", " \"fill-extrusion-height\": [\"*\", 10, [\"sqrt\", [\"get\", \"valuePerSqm\"]]],\n", " \"fill-extrusion-opacity\": 0.9,\n", "}\n", "m.add_geojson(url, layer_type=\"line\", paint=paint_line, name=\"blocks-line\")\n", "m.add_geojson(url, layer_type=\"fill-extrusion\", paint=paint_fill, name=\"blocks-fill\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "59", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "markdown", "id": "60", "metadata": {}, "source": [ "### Marker cluster\n", "\n", "Create a marker cluster layer." ] }, { "cell_type": "code", "execution_count": null, "id": "61", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-103.59179, 40.66995], zoom=3, style=\"streets\")\n", "data = \"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n", "source_args = {\n", " \"cluster\": True,\n", " \"cluster_radius\": 50,\n", " \"cluster_min_points\": 2,\n", " \"cluster_max_zoom\": 14,\n", " \"cluster_properties\": {\n", " \"maxMag\": [\"max\", [\"get\", \"mag\"]],\n", " \"minMag\": [\"min\", [\"get\", \"mag\"]],\n", " },\n", "}\n", "\n", "m.add_geojson(\n", " data,\n", " layer_type=\"circle\",\n", " name=\"earthquake-circles\",\n", " filter=[\"!\", [\"has\", \"point_count\"]],\n", " paint={\"circle-color\": \"darkblue\"},\n", " source_args=source_args,\n", ")\n", "\n", "m.add_geojson(\n", " data,\n", " layer_type=\"circle\",\n", " name=\"earthquake-clusters\",\n", " filter=[\"has\", \"point_count\"],\n", " paint={\n", " \"circle-color\": [\n", " \"step\",\n", " [\"get\", \"point_count\"],\n", " \"#51bbd6\",\n", " 100,\n", " \"#f1f075\",\n", " 750,\n", " \"#f28cb1\",\n", " ],\n", " \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 20, 100, 30, 750, 40],\n", " },\n", " source_args=source_args,\n", ")\n", "\n", "m.add_geojson(\n", " data,\n", " layer_type=\"symbol\",\n", " name=\"earthquake-labels\",\n", " filter=[\"has\", \"point_count\"],\n", " layout={\n", " \"text-field\": [\"get\", \"point_count_abbreviated\"],\n", " \"text-size\": 12,\n", " },\n", " source_args=source_args,\n", ")\n", "m" ] }, { "cell_type": "markdown", "id": "62", "metadata": {}, "source": [ "### Local vector data\n", "\n", "You can load local vector data interactively using the `open_geojson` method." ] }, { "cell_type": "code", "execution_count": null, "id": "63", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "64", "metadata": {}, "outputs": [], "source": [ "url = \"https://github.com/opengeos/datasets/releases/download/us/us_states.geojson\"\n", "filepath = \"data/us_states.geojson\"\n", "leafmap.download_file(url, filepath, quiet=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "65", "metadata": {}, "outputs": [], "source": [ "m.open_geojson()" ] }, { "cell_type": "markdown", "id": "66", "metadata": {}, "source": [ "### Live feature update\n", "\n", "#### Animate a point along a route" ] }, { "cell_type": "code", "execution_count": null, "id": "67", "metadata": {}, "outputs": [], "source": [ "import time" ] }, { "cell_type": "code", "execution_count": null, "id": "68", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"streets\")\n", "url = \"https://github.com/opengeos/datasets/releases/download/us/arc_with_bearings.geojson\"\n", "geojson = requests.get(url).json()\n", "bearings = geojson[\"features\"][0][\"properties\"][\"bearings\"]\n", "coordinates = geojson[\"features\"][0][\"geometry\"][\"coordinates\"][:-1]\n", "m.add_geojson(geojson, name=\"route\")\n", "\n", "origin = [-122.414, 37.776]\n", "destination = [-77.032, 38.913]\n", "\n", "point = {\n", " \"type\": \"FeatureCollection\",\n", " \"features\": [\n", " {\n", " \"type\": \"Feature\",\n", " \"properties\": {},\n", " \"geometry\": {\"type\": \"Point\", \"coordinates\": origin},\n", " }\n", " ],\n", "}\n", "source = {\"type\": \"geojson\", \"data\": point}\n", "m.add_source(\"point\", source)\n", "layer = {\n", " \"id\": \"point\",\n", " \"source\": \"point\",\n", " \"type\": \"symbol\",\n", " \"layout\": {\n", " \"icon-image\": \"airport_15\",\n", " \"icon-rotate\": [\"get\", \"bearing\"],\n", " \"icon-rotation-alignment\": \"map\",\n", " \"icon-overlap\": \"always\",\n", " \"icon-ignore-placement\": True,\n", " },\n", "}\n", "m.add_layer(layer)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "69", "metadata": {}, "outputs": [], "source": [ "for index, coordinate in enumerate(coordinates):\n", " point[\"features\"][0][\"geometry\"][\"coordinates\"] = coordinate\n", " point[\"features\"][0][\"properties\"][\"bearing\"] = bearings[index]\n", " m.set_data(\"point\", point)\n", " time.sleep(0.05)" ] }, { "cell_type": "markdown", "id": "70", "metadata": {}, "source": [ "#### Update a feature in realtime" ] }, { "cell_type": "code", "execution_count": null, "id": "71", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style=\"3d-terrain\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "72", "metadata": {}, "outputs": [], "source": [ "import geopandas as gpd" ] }, { "cell_type": "code", "execution_count": null, "id": "73", "metadata": {}, "outputs": [], "source": [ "url = \"https://maplibre.org/maplibre-gl-js/docs/assets/hike.geojson\"\n", "gdf = gpd.read_file(url)\n", "coordinates = list(gdf.geometry[0].coords)\n", "print(coordinates[:5])" ] }, { "cell_type": "code", "execution_count": null, "id": "74", "metadata": {}, "outputs": [], "source": [ "source = {\n", " \"type\": \"geojson\",\n", " \"data\": {\n", " \"type\": \"Feature\",\n", " \"geometry\": {\"type\": \"LineString\", \"coordinates\": [coordinates[0]]},\n", " },\n", "}\n", "m.add_source(\"trace\", source)\n", "layer = {\n", " \"id\": \"trace\",\n", " \"type\": \"line\",\n", " \"source\": \"trace\",\n", " \"paint\": {\"line-color\": \"yellow\", \"line-opacity\": 0.75, \"line-width\": 5},\n", "}\n", "m.add_layer(layer)\n", "m.jump_to({\"center\": coordinates[0], \"zoom\": 14})\n", "m.set_pitch(30)" ] }, { "cell_type": "code", "execution_count": null, "id": "75", "metadata": {}, "outputs": [], "source": [ "for coord in coordinates:\n", " time.sleep(0.005)\n", " source[\"data\"][\"geometry\"][\"coordinates\"].append(coord)\n", " m.set_data(\"trace\", source[\"data\"])\n", " m.pan_to(coord)" ] }, { "cell_type": "markdown", "id": "76", "metadata": {}, "source": [ "## Visualize raster data\n", "\n", "### Local raster data\n", "\n", "You can load local raster data using the `add_raster` method." ] }, { "cell_type": "code", "execution_count": null, "id": "77", "metadata": {}, "outputs": [], "source": [ "url = \"https://github.com/opengeos/datasets/releases/download/raster/landsat.tif\"\n", "filepath = \"landsat.tif\"\n", "leafmap.download_file(url, filepath)" ] }, { "cell_type": "code", "execution_count": null, "id": "78", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"streets\")\n", "m.add_raster(filepath, indexes=[3, 2, 1], vmin=0, vmax=100, name=\"Landsat-321\")\n", "m.add_raster(filepath, indexes=[4, 3, 2], vmin=0, vmax=100, name=\"Landsat-432\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "79", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "code", "execution_count": null, "id": "80", "metadata": {}, "outputs": [], "source": [ "url = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"\n", "filepath = \"srtm90.tif\"\n", "leafmap.download_file(url, filepath)" ] }, { "cell_type": "code", "execution_count": null, "id": "81", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"satellite\")\n", "m.add_raster(filepath, colormap=\"terrain\", name=\"DEM\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "82", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "markdown", "id": "83", "metadata": {}, "source": [ "### Cloud Optimized GeoTIFF (COG)\n", "\n", "You can load Cloud Optimized GeoTIFF (COG) data using the `add_cog_layer` method." ] }, { "cell_type": "code", "execution_count": null, "id": "84", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "m.add_basemap(\"Esri.WorldImagery\")\n", "url = (\n", " \"https://github.com/opengeos/datasets/releases/download/raster/Libya-2023-09-13.tif\"\n", ")\n", "m.add_cog_layer(url, name=\"COG\", attribution=\"Maxar\", fit_bounds=True, nodata=0)\n", "m.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "85", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "markdown", "id": "86", "metadata": {}, "source": [ "### STAC layer\n", "\n", "You can load SpatioTemporal Asset Catalog (STAC) data using the `add_stac_layer` method." ] }, { "cell_type": "code", "execution_count": null, "id": "87", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "url = \"https://canada-spot-ortho.s3.amazonaws.com/canada_spot_orthoimages/canada_spot5_orthoimages/S5_2007/S5_11055_6057_20070622/S5_11055_6057_20070622.json\"\n", "m.add_stac_layer(url, bands=[\"B4\", \"B3\", \"B2\"], name=\"SPOT\", vmin=0, vmax=150, nodata=0)\n", "m" ] }, { "cell_type": "markdown", "id": "88", "metadata": {}, "source": [ "## PMTiles\n", "\n", "Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format for fast and efficient rendering of vector tiles.\n", "\n", "### Protomaps sample data" ] }, { "cell_type": "code", "execution_count": null, "id": "89", "metadata": {}, "outputs": [], "source": [ "url = \"https://opengeos.org/data/pmtiles/protomaps_firenze.pmtiles\"\n", "metadata = leafmap.pmtiles_metadata(url)\n", "print(f\"layer names: {metadata['layer_names']}\")\n", "print(f\"bounds: {metadata['bounds']}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "90", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "\n", "style = {\n", " \"version\": 8,\n", " \"sources\": {\n", " \"example_source\": {\n", " \"type\": \"vector\",\n", " \"url\": \"pmtiles://\" + url,\n", " \"attribution\": \"PMTiles\",\n", " }\n", " },\n", " \"layers\": [\n", " {\n", " \"id\": \"buildings\",\n", " \"source\": \"example_source\",\n", " \"source-layer\": \"landuse\",\n", " \"type\": \"fill\",\n", " \"paint\": {\"fill-color\": \"steelblue\"},\n", " },\n", " {\n", " \"id\": \"roads\",\n", " \"source\": \"example_source\",\n", " \"source-layer\": \"roads\",\n", " \"type\": \"line\",\n", " \"paint\": {\"line-color\": \"black\"},\n", " },\n", " ],\n", "}\n", "\n", "# style = leafmap.pmtiles_style(url) # Use default style\n", "\n", "m.add_pmtiles(\n", " url,\n", " style=style,\n", " visible=True,\n", " opacity=1.0,\n", " tooltip=True,\n", ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "91", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "markdown", "id": "92", "metadata": {}, "source": [ "### Overture data\n", "\n", "You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps)." ] }, { "cell_type": "code", "execution_count": null, "id": "93", "metadata": {}, "outputs": [], "source": [ "url = \"https://storage.googleapis.com/ahp-research/overture/pmtiles/overture.pmtiles\"\n", "metadata = leafmap.pmtiles_metadata(url)\n", "print(f\"layer names: {metadata['layer_names']}\")\n", "print(f\"bounds: {metadata['bounds']}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "94", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(height=\"800px\")\n", "m.add_basemap(\"Esri.WorldImagery\")\n", "\n", "style = {\n", " \"version\": 8,\n", " \"sources\": {\n", " \"example_source\": {\n", " \"type\": \"vector\",\n", " \"url\": \"pmtiles://\" + url,\n", " \"attribution\": \"PMTiles\",\n", " }\n", " },\n", " \"layers\": [\n", " # {\n", " # \"id\": \"admins\",\n", " # \"source\": \"example_source\",\n", " # \"source-layer\": \"admins\",\n", " # \"type\": \"fill\",\n", " # \"paint\": {\"fill-color\": \"#BDD3C7\", \"fill-opacity\": 0.1},\n", " # },\n", " {\n", " \"id\": \"buildings\",\n", " \"source\": \"example_source\",\n", " \"source-layer\": \"buildings\",\n", " \"type\": \"fill\",\n", " \"paint\": {\"fill-color\": \"#FFFFB3\", \"fill-opacity\": 0.5},\n", " },\n", " {\n", " \"id\": \"places\",\n", " \"source\": \"example_source\",\n", " \"source-layer\": \"places\",\n", " \"type\": \"fill\",\n", " \"paint\": {\"fill-color\": \"#BEBADA\", \"fill-opacity\": 0.5},\n", " },\n", " {\n", " \"id\": \"roads\",\n", " \"source\": \"example_source\",\n", " \"source-layer\": \"roads\",\n", " \"type\": \"line\",\n", " \"paint\": {\"line-color\": \"#FB8072\"},\n", " },\n", " ],\n", "}\n", "\n", "# style = leafmap.pmtiles_style(url) # Use default style\n", "\n", "m.add_pmtiles(\n", " url,\n", " style=style,\n", " visible=True,\n", " opacity=1.0,\n", " tooltip=True,\n", ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "95", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "markdown", "id": "96", "metadata": {}, "source": [ "### Source Cooperative\n", "\n", "Let's visualize the [Google-Microsoft Open Buildings - combined by VIDA](https://beta.source.coop/repositories/vida/google-microsoft-open-buildings/description)." ] }, { "cell_type": "code", "execution_count": null, "id": "97", "metadata": {}, "outputs": [], "source": [ "url = \"https://data.source.coop/vida/google-microsoft-open-buildings/pmtiles/go_ms_building_footprints.pmtiles\"\n", "metadata = leafmap.pmtiles_metadata(url)\n", "print(f\"layer names: {metadata['layer_names']}\")\n", "print(f\"bounds: {metadata['bounds']}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "98", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[0, 20], zoom=2, height=\"800px\")\n", "m.add_basemap(\"Google Hybrid\", visible=False)\n", "\n", "style = {\n", " \"version\": 8,\n", " \"sources\": {\n", " \"example_source\": {\n", " \"type\": \"vector\",\n", " \"url\": \"pmtiles://\" + url,\n", " \"attribution\": \"PMTiles\",\n", " }\n", " },\n", " \"layers\": [\n", " {\n", " \"id\": \"buildings\",\n", " \"source\": \"example_source\",\n", " \"source-layer\": \"building_footprints\",\n", " \"type\": \"fill\",\n", " \"paint\": {\"fill-color\": \"#3388ff\", \"fill-opacity\": 0.5},\n", " },\n", " ],\n", "}\n", "\n", "# style = leafmap.pmtiles_style(url) # Use default style\n", "\n", "m.add_pmtiles(\n", " url,\n", " style=style,\n", " visible=True,\n", " opacity=1.0,\n", " tooltip=True,\n", ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "99", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "markdown", "id": "100", "metadata": {}, "source": [ "### 3D PMTiles\n", "\n", "Visualize the global building data in 3D." ] }, { "cell_type": "code", "execution_count": null, "id": "101", "metadata": {}, "outputs": [], "source": [ "url = \"https://data.source.coop/cholmes/overture/overture-buildings.pmtiles\"\n", "metadata = leafmap.pmtiles_metadata(url)\n", "print(f\"layer names: {metadata['layer_names']}\")\n", "print(f\"bounds: {metadata['bounds']}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "102", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=[-74.0095, 40.7046], zoom=16, pitch=60, bearing=-17, style=\"positron\"\n", ")\n", "m.add_basemap(\"OpenStreetMap.Mapnik\")\n", "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", "\n", "style = {\n", " \"layers\": [\n", " {\n", " \"id\": \"buildings\",\n", " \"source\": \"example_source\",\n", " \"source-layer\": \"buildings\",\n", " \"type\": \"fill-extrusion\",\n", " \"filter\": [\n", " \">\",\n", " [\"get\", \"height\"],\n", " 0,\n", " ], # only show buildings with height info\n", " \"paint\": {\n", " \"fill-extrusion-color\": [\n", " \"interpolate\",\n", " [\"linear\"],\n", " [\"get\", \"height\"],\n", " 0,\n", " \"lightgray\",\n", " 200,\n", " \"royalblue\",\n", " 400,\n", " \"lightblue\",\n", " ],\n", " \"fill-extrusion-height\": [\"*\", [\"get\", \"height\"], 1],\n", " },\n", " },\n", " ],\n", "}\n", "\n", "m.add_pmtiles(\n", " url,\n", " style=style,\n", " visible=True,\n", " opacity=1.0,\n", " tooltip=True,\n", " template=\"Height: {{height}}
Country: {{country_iso}}\",\n", " fit_bounds=False,\n", ")\n", "m.add_layer_control()\n", "m" ] }, { "cell_type": "markdown", "id": "103", "metadata": {}, "source": [ "## Add custom components\n", "\n", "You can add custom components to the map, including images, videos, text, color bar, and legend.\n", "\n", "### Add image" ] }, { "cell_type": "code", "execution_count": null, "id": "104", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[0.349419, -1.80921], zoom=3, style=\"streets\")\n", "image = \"https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.png\"\n", "source = {\n", " \"type\": \"geojson\",\n", " \"data\": {\n", " \"type\": \"FeatureCollection\",\n", " \"features\": [\n", " {\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [0, 0]}}\n", " ],\n", " },\n", "}\n", "\n", "layer = {\n", " \"id\": \"points\",\n", " \"type\": \"symbol\",\n", " \"source\": \"point\",\n", " \"layout\": {\n", " \"icon-image\": \"cat\",\n", " \"icon-size\": 0.25,\n", " \"text-field\": \"I love kitty!\",\n", " \"text-font\": [\"Open Sans Regular\"],\n", " \"text-offset\": [0, 3],\n", " \"text-anchor\": \"top\",\n", " },\n", "}\n", "m.add_image(\"cat\", image)\n", "m.add_source(\"point\", source)\n", "m.add_layer(layer)\n", "m" ] }, { "cell_type": "markdown", "id": "105", "metadata": {}, "source": [ "### Add text" ] }, { "cell_type": "code", "execution_count": null, "id": "106", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"streets\")\n", "text = \"Hello World\"\n", "m.add_text(text, fontsize=20, position=\"bottom-right\")\n", "text2 = \"Awesome Text!\"\n", "m.add_text(text2, fontsize=25, bg_color=\"rgba(255, 255, 255, 0.8)\", position=\"top-left\")\n", "m" ] }, { "cell_type": "markdown", "id": "107", "metadata": {}, "source": [ "### Add GIF" ] }, { "cell_type": "code", "execution_count": null, "id": "108", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", "image = \"https://i.imgur.com/KeiAsTv.gif\"\n", "m.add_image(image=image, width=250, height=250, position=\"bottom-right\")\n", "text = \"I love sloth!🦥\"\n", "m.add_text(text, fontsize=35, padding=\"20px\")\n", "image2 = \"https://i.imgur.com/kZC2tpr.gif\"\n", "m.add_image(image=image2, bg_color=\"transparent\", position=\"bottom-left\")\n", "m" ] }, { "cell_type": "markdown", "id": "109", "metadata": {}, "source": [ "### Add HTML" ] }, { "cell_type": "code", "execution_count": null, "id": "110", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", "html = \"\"\"\n", "\n", "\n", "\n", "\n", "🚀\n", "

I will display 🚁

\n", "

I will display 🚂

\n", "\n", "\n", "\n", "\"\"\"\n", "m.add_html(html, bg_color=\"transparent\")\n", "m" ] }, { "cell_type": "markdown", "id": "111", "metadata": {}, "source": [ "### Add colorbar" ] }, { "cell_type": "code", "execution_count": null, "id": "112", "metadata": {}, "outputs": [], "source": [ "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"" ] }, { "cell_type": "code", "execution_count": null, "id": "113", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"streets\")\n", "m.add_cog_layer(\n", " dem,\n", " name=\"DEM\",\n", " colormap_name=\"terrain\",\n", " rescale=\"0, 4000\",\n", " fit_bounds=True,\n", " nodata=0,\n", ")\n", "m.add_colorbar(\n", " cmap=\"terrain\", vmin=0, vmax=4000, label=\"Elevation (m)\", position=\"bottom-right\"\n", ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "114", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"streets\")\n", "m.add_cog_layer(\n", " dem,\n", " name=\"DEM\",\n", " colormap_name=\"terrain\",\n", " rescale=\"0, 4000\",\n", " nodata=0,\n", " fit_bounds=True,\n", ")\n", "m.add_colorbar(\n", " cmap=\"terrain\",\n", " vmin=0,\n", " vmax=4000,\n", " label=\"Elevation (m)\",\n", " position=\"bottom-right\",\n", " transparent=True,\n", ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "115", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(style=\"streets\")\n", "m.add_cog_layer(\n", " dem,\n", " name=\"DEM\",\n", " colormap_name=\"terrain\",\n", " rescale=\"0, 4000\",\n", " nodata=0,\n", " fit_bounds=True,\n", ")\n", "m.add_colorbar(\n", " cmap=\"terrain\",\n", " vmin=0,\n", " vmax=4000,\n", " label=\"Elevation (m)\",\n", " position=\"bottom-right\",\n", " width=0.2,\n", " height=3,\n", " orientation=\"vertical\",\n", ")\n", "m" ] }, { "cell_type": "markdown", "id": "116", "metadata": {}, "source": [ "### Add legend" ] }, { "cell_type": "code", "execution_count": null, "id": "117", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", "m.add_basemap(\"Esri.WorldImagery\")\n", "url = \"https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2021_Land_Cover_L48/wms\"\n", "layers = \"NLCD_2021_Land_Cover_L48\"\n", "m.add_wms_layer(url, layers=layers, name=\"NLCD 2021\")\n", "m.add_legend(\n", " title=\"NLCD Land Cover Type\",\n", " builtin_legend=\"NLCD\",\n", " bg_color=\"rgba(255, 255, 255, 0.5)\",\n", " position=\"bottom-left\",\n", ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "118", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", "m.add_basemap(\"Esri.WorldImagery\")\n", "url = \"https://fwspublicservices.wim.usgs.gov/wetlandsmapservice/services/Wetlands/MapServer/WMSServer\"\n", "m.add_wms_layer(url, layers=\"1\", name=\"NWI\", opacity=0.6)\n", "m.add_layer_control()\n", "m.add_legend(builtin_legend=\"NWI\", title=\"Wetland Type\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "119", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", "m.add_basemap(\"Esri.WorldImagery\")\n", "url = \"https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2021_Land_Cover_L48/wms\"\n", "layers = \"NLCD_2021_Land_Cover_L48\"\n", "m.add_wms_layer(url, layers=layers, name=\"NLCD 2021\")\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", "m.add_legend(\n", " title=\"NLCD Land Cover Type\",\n", " legend_dict=legend_dict,\n", " bg_color=\"rgba(255, 255, 255, 0.5)\",\n", " position=\"bottom-left\",\n", ")\n", "m" ] }, { "cell_type": "markdown", "id": "120", "metadata": {}, "source": [ "### Add video\n", "\n", "The `urls` value is an array. For each URL in the array, a video element source will be created. To support the video across browsers, supply URLs in multiple formats.\n", "The `coordinates` array contains [longitude, latitude] pairs for the video corners listed in clockwise order: top left, top right, bottom right, bottom left." ] }, { "cell_type": "code", "execution_count": null, "id": "121", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=[-122.514426, 37.562984], zoom=17, bearing=-96, style=\"satellite\"\n", ")\n", "urls = [\n", " \"https://static-assets.mapbox.com/mapbox-gl-js/drone.mp4\",\n", " \"https://static-assets.mapbox.com/mapbox-gl-js/drone.webm\",\n", "]\n", "coordinates = [\n", " [-122.51596391201019, 37.56238816766053],\n", " [-122.51467645168304, 37.56410183312965],\n", " [-122.51309394836426, 37.563391708549425],\n", " [-122.51423120498657, 37.56161849366671],\n", "]\n", "m.add_video(urls, coordinates)\n", "m.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "122", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-115, 25], zoom=4, style=\"satellite\")\n", "urls = [\n", " \"https://data.opengeos.org/patricia_nasa.mp4\",\n", " \"https://data.opengeos.org/patricia_nasa.webm\",\n", "]\n", "coordinates = [\n", " [-130, 32],\n", " [-100, 32],\n", " [-100, 13],\n", " [-130, 13],\n", "]\n", "m.add_video(urls, coordinates)\n", "m.add_layer_control()\n", "m" ] }, { "cell_type": "markdown", "id": "123", "metadata": {}, "source": [ "## Deck.GL layers\n", "\n", "Deck.GL layers can be added to the map using the `add_deck_layer` method.\n", "\n", "### Single Deck.GL layer" ] }, { "cell_type": "code", "execution_count": null, "id": "124", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " style=\"positron\",\n", " center=(-122.4, 37.74),\n", " zoom=12,\n", " pitch=40,\n", ")\n", "deck_grid_layer = {\n", " \"@@type\": \"GridLayer\",\n", " \"id\": \"GridLayer\",\n", " \"data\": \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json\",\n", " \"extruded\": True,\n", " \"getPosition\": \"@@=COORDINATES\",\n", " \"getColorWeight\": \"@@=SPACES\",\n", " \"getElevationWeight\": \"@@=SPACES\",\n", " \"elevationScale\": 4,\n", " \"cellSize\": 200,\n", " \"pickable\": True,\n", "}\n", "\n", "m.add_deck_layers([deck_grid_layer], tooltip=\"Number of points: {{ count }}\")\n", "m" ] }, { "cell_type": "markdown", "id": "125", "metadata": {}, "source": [ "### Multiple Deck.GL layers" ] }, { "cell_type": "code", "execution_count": null, "id": "126", "metadata": {}, "outputs": [], "source": [ "import requests" ] }, { "cell_type": "code", "execution_count": null, "id": "127", "metadata": {}, "outputs": [], "source": [ "data = requests.get(\n", " \"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson\"\n", ").json()" ] }, { "cell_type": "code", "execution_count": null, "id": "128", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " style=\"positron\",\n", " center=(0.45, 51.47),\n", " zoom=4,\n", " pitch=30,\n", ")\n", "deck_geojson_layer = {\n", " \"@@type\": \"GeoJsonLayer\",\n", " \"id\": \"airports\",\n", " \"data\": data,\n", " \"filled\": True,\n", " \"pointRadiusMinPixels\": 2,\n", " \"pointRadiusScale\": 2000,\n", " \"getPointRadius\": \"@@=11 - properties.scalerank\",\n", " \"getFillColor\": [200, 0, 80, 180],\n", " \"autoHighlight\": True,\n", " \"pickable\": True,\n", "}\n", "\n", "deck_arc_layer = {\n", " \"@@type\": \"ArcLayer\",\n", " \"id\": \"arcs\",\n", " \"data\": [\n", " feature\n", " for feature in data[\"features\"]\n", " if feature[\"properties\"][\"scalerank\"] < 4\n", " ],\n", " \"getSourcePosition\": [-0.4531566, 51.4709959], # London\n", " \"getTargetPosition\": \"@@=geometry.coordinates\",\n", " \"getSourceColor\": [0, 128, 200],\n", " \"getTargetColor\": [200, 0, 80],\n", " \"getWidth\": 2,\n", " \"pickable\": True,\n", "}\n", "\n", "m.add_deck_layers(\n", " [deck_geojson_layer, deck_arc_layer],\n", " tooltip={\n", " \"airports\": \"{{ &properties.name }}\",\n", " \"arcs\": \"gps_code: {{ properties.gps_code }}\",\n", " },\n", ")\n", "m" ] }, { "cell_type": "markdown", "id": "129", "metadata": {}, "source": [ "## Google Earth Engine\n", "\n", "You can use the Earth Engine Python API to load and visualize Earth Engine data." ] }, { "cell_type": "code", "execution_count": null, "id": "130", "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, "id": "131", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, { "cell_type": "markdown", "id": "132", "metadata": {}, "source": [ "We can also overlay other data layers on top of Earth Engine data layers." ] }, { "cell_type": "code", "execution_count": null, "id": "133", "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_3d_buildings()\n", "m.add_legend(builtin_legend=\"ESA_WorldCover\", title=\"ESA Landcover\")\n", "m" ] }, { "cell_type": "markdown", "id": "134", "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, "id": "135", "metadata": {}, "outputs": [], "source": [ "# import ee\n", "# ee.Initialize(project=\"YOUR-PROJECT-ID\")" ] }, { "cell_type": "code", "execution_count": null, "id": "136", "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" ] }, { "cell_type": "markdown", "id": "137", "metadata": {}, "source": [ "## To HTML\n", "\n", "To export the map as an HTML file, use the `to_html` method. To avoid exposing your private API key, you should create a public API key and restrict it to your website domain." ] }, { "cell_type": "code", "execution_count": null, "id": "138", "metadata": {}, "outputs": [], "source": [ "# import os\n", "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_PRIVATE_API_KEY\"\n", "# os.environ[\"MAPTILER_KEY_PUBLIC\"] = \"YOUR_PUBLIC_API_KEY\"" ] }, { "cell_type": "code", "execution_count": null, "id": "139", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=[-122.19861, 46.21168], zoom=13, pitch=60, bearing=150, style=\"3d-terrain\"\n", ")\n", "m.add_layer_control(bg_layers=True)\n", "m.to_html(\n", " \"terrain.html\",\n", " title=\"Awesome 3D Map\",\n", " width=\"100%\",\n", " height=\"100%\",\n", " replace_key=True,\n", ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "140", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", " center=[-74.0066, 40.7135], zoom=16, pitch=45, bearing=-17, style=\"basic-v2\"\n", ")\n", "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", "m.add_3d_buildings(min_zoom=15)\n", "m.add_layer_control()\n", "m.to_html(\n", " \"buildings.html\",\n", " title=\"Awesome 3D Map\",\n", " width=\"100%\",\n", " height=\"100%\",\n", " replace_key=True,\n", ")\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": 5 }