From 77e5c1568939182e9ca0734d2ef73a050ff7ae62 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 26 Oct 2024 12:13:06 -0400 Subject: [PATCH 1/6] Update Maplibre chapter --- .github/workflows/build.yml | 1 + .github/workflows/deploy.yml | 1 + _config.yml | 3 - book/geospatial/maplibre.ipynb | 3353 +++++++++++++++++++++++--------- book/geospatial/maplibre.md | 1173 +++++++++-- 5 files changed, 3469 insertions(+), 1062 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6fe4d54..366de92 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,6 +49,7 @@ jobs: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + MAPTILER_KEY: ${{ secrets.MAPTILER_KEY }} - name: Deploy to Netlify uses: nwtgck/actions-netlify@v3.0 with: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bc264d0..73fccde 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -49,6 +49,7 @@ jobs: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + MAPTILER_KEY: ${{ secrets.MAPTILER_KEY }} # Deploy the book's HTML to gh-pages branch - name: GitHub Pages action uses: peaceiris/actions-gh-pages@v4 diff --git a/_config.yml b/_config.yml index 401399f..480636b 100644 --- a/_config.yml +++ b/_config.yml @@ -20,9 +20,6 @@ execute: execute_notebooks: "auto" # Whether to execute notebooks at build time. Must be one of ("auto", "force", "cache", "off") cache: "" # A path to the jupyter cache that will be used to store execution artifacts. Defaults to `_build/.jupyter_cache/` exclude_patterns: [ - "book/geospatial/vector_viz.ipynb", - "book/geospatial/raster_viz.ipynb", - "book/geospatial/maplibre.ipynb", "book/geospatial/geemap.ipynb", "book/geospatial/samgeo.ipynb", "book/geospatial/hypercoast.ipynb", diff --git a/book/geospatial/maplibre.ipynb b/book/geospatial/maplibre.ipynb index e2262e0..4ab3a2b 100644 --- a/book/geospatial/maplibre.ipynb +++ b/book/geospatial/maplibre.ipynb @@ -61,9 +61,11 @@ "id": "4", "metadata": {}, "source": [ - "## Set up API Key\n", + "## Create interactive maps\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/)." + "### Create a simple map\n", + "\n", + "Let's create a simple interactive map using Leafmap." ] }, { @@ -73,8 +75,8 @@ "metadata": {}, "outputs": [], "source": [ - "# import os\n", - "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" + "m = leafmap.Map()\n", + "m" ] }, { @@ -82,11 +84,7 @@ "id": "6", "metadata": {}, "source": [ - "## Create interactive maps\n", - "\n", - "### Create a simple map\n", - "\n", - "Let's create a simple interactive map using Leafmap." + "You can customize the map by specifying map center [lon, lat], zoom level, pitch, and bearing." ] }, { @@ -96,7 +94,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", + "m = leafmap.Map(center=[-100, 40], zoom=3, pitch=0, bearing=0)\n", "m" ] }, @@ -105,7 +103,7 @@ "id": "8", "metadata": {}, "source": [ - "You can customize the map by specifying map center [lon, lat], zoom level, pitch, and bearing." + "To customize the basemap, you can specify the `style` parameter. It can be an URL or a string, such as `dark-matter`, `positron`, `voyager`, `liberty`, `demotiles`." ] }, { @@ -115,7 +113,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3, pitch=0, bearing=0)\n", + "m = leafmap.Map(style=\"positron\")\n", "m" ] }, @@ -124,7 +122,7 @@ "id": "10", "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`, `liberty`, `demotiles`." + "To create a map with a background color, use `style=\"background-\"`, such as `background-lightgray` and `background-green`." ] }, { @@ -134,7 +132,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"positron\")\n", + "m = leafmap.Map(style=\"background-lightgray\")\n", "m" ] }, @@ -143,7 +141,7 @@ "id": "12", "metadata": {}, "source": [ - "To create a map with a background color, use `style=\"background-\"`, such as `background-lightgray` and `background-green`." + "Alternatively, you can provide a URL to a vector style." ] }, { @@ -153,44 +151,38 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"background-lightgray\")\n", + "style = \"https://demotiles.maplibre.org/style.json\"\n", + "m = leafmap.Map(style=style)\n", "m" ] }, - { - "cell_type": "markdown", - "id": "14", - "metadata": {}, - "source": [ - "Alternatively, you can provide a URL to a vector style." - ] - }, { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ - "style = \"https://demotiles.maplibre.org/style.json\"\n", - "m = leafmap.Map(style=style)\n", + "m = leafmap.Map(style=\"liberty\")\n", "m" ] }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "### Add map controls\n", "\n", - "The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`." + "The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`.\n", + "\n", + "#### Geolocate control" ] }, { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -201,73 +193,82 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ - "### Add basemaps\n", - "\n", - "You can add basemaps to the map using the `add_basemap` method." + "#### Fullscreen control" ] }, { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", - "m.add_basemap(\"OpenTopoMap\")\n", + "m = leafmap.Map(center=[11.255, 43.77], zoom=13, style=\"streets\", controls={})\n", + "m.add_control(\"fullscreen\", position=\"top-right\")\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "20", + "cell_type": "markdown", + "id": "19", "metadata": {}, - "outputs": [], "source": [ - "m.add_basemap(\"Esri.WorldImagery\")" + "#### Navigation control" ] }, { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", + "m = leafmap.Map(center=[11.255, 43.77], zoom=13, style=\"streets\", controls={})\n", + "m.add_control(\"navigation\", position=\"top-left\")\n", "m" ] }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ - "To add basemaps interactively, use the `add_basemap` method without specifying the `basemap` parameter." + "#### Draw control \n", + "\n", + "Add the default draw control." ] }, { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ - "m.add_basemap()" + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "m.add_draw_control(position=\"top-left\")\n", + "m" ] }, { "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "Only activate a give set of control." + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "24", "metadata": {}, + "outputs": [], "source": [ - "### Add XYZ tile layer\n", - "\n", - "You can add XYZ tile layers to the map using the `add_tile_layer` method." + "from maplibre.plugins import MapboxDrawControls, MapboxDrawOptions" ] }, { @@ -277,9 +278,12 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", - "url = \"https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}\"\n", - "m.add_tile_layer(url, name=\"USGS TOpo\", attribution=\"USGS\", opacity=1.0, visible=True)\n", + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "draw_options = MapboxDrawOptions(\n", + " display_controls_default=False,\n", + " controls=MapboxDrawControls(polygon=True, line_string=True, point=True, trash=True),\n", + ")\n", + "m.add_draw_control(draw_options)\n", "m" ] }, @@ -288,9 +292,7 @@ "id": "26", "metadata": {}, "source": [ - "### Add WMS layer\n", - "\n", - "You can add WMS layers to the map using the `add_wms_layer` method." + "Load a GeoJSON FeatureCollection to the draw control." ] }, { @@ -300,180 +302,515 @@ "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 = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "geojson = {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", + " {\n", + " \"id\": \"abc\",\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\n", + " \"coordinates\": [\n", + " [\n", + " [-119.08, 45.95],\n", + " [-119.79, 42.08],\n", + " [-107.28, 41.43],\n", + " [-108.15, 46.44],\n", + " [-119.08, 45.95],\n", + " ]\n", + " ],\n", + " \"type\": \"Polygon\",\n", + " },\n", + " },\n", + " {\n", + " \"id\": \"xyz\",\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\n", + " \"coordinates\": [\n", + " [\n", + " [-103.87, 38.08],\n", + " [-108.54, 36.44],\n", + " [-106.25, 33.00],\n", + " [-99.91, 31.79],\n", + " [-96.82, 35.48],\n", + " [-98.80, 37.77],\n", + " [-103.87, 38.08],\n", + " ]\n", + " ],\n", + " \"type\": \"Polygon\",\n", + " },\n", + " },\n", + " ],\n", + "}\n", + "m.add_draw_control(position=\"top-left\", geojson=geojson)\n", "m" ] }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "Retrieve the draw features." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "28", + "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" + "m.draw_features_selected" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "m.draw_feature_collection_all" ] }, { "cell_type": "markdown", - "id": "29", + "id": "31", "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)" + "### Disable scroll zoom" ] }, { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "32", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"streets\")\n", + "m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style=\"liberty\")\n", "m" ] }, + { + "cell_type": "markdown", + "id": "33", + "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": "31", + "id": "34", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"satellite\")\n", + "m = leafmap.Map()\n", + "m.add_basemap(\"OpenTopoMap\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "35", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"hybrid\")\n", - "m" + "m.add_basemap(\"Esri.WorldImagery\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "36", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"topo\")\n", + "m = leafmap.Map()\n", "m" ] }, { "cell_type": "markdown", - "id": "34", + "id": "37", "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." + "To add basemaps interactively, use the `add_basemap` method without specifying the `basemap` parameter." ] }, { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "38", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-hybrid\")\n", - "m.add_layer_control(bg_layers=True)\n", - "m" + "m.add_basemap()" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "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": "36", + "id": "40", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-satellite\")\n", - "m.add_layer_control(bg_layers=True)\n", + "m = leafmap.Map()\n", + "url = \"https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}\"\n", + "m.add_tile_layer(url, name=\"USGS TOpo\", attribution=\"USGS\", opacity=1.0, visible=True)\n", "m" ] }, + { + "cell_type": "markdown", + "id": "41", + "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": "37", + "id": "42", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-topo\", exaggeration=1.5, hillshade=False)\n", - "m.add_layer_control(bg_layers=True)\n", + "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": "38", + "id": "43", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-ocean\", exaggeration=1.5, hillshade=True)\n", + "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": "code", "execution_count": null, - "id": "39", + "id": "44", "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 = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style=\"streets\")\n", + "\n", + "source = {\n", + " \"type\": \"raster\",\n", + " \"tiles\": [\n", + " \"https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015\"\n", + " ],\n", + " \"tileSize\": 256,\n", + "}\n", + "layer = {\n", + " \"id\": \"wms-test-layer\",\n", + " \"type\": \"raster\",\n", + " \"source\": \"wms-test-source\",\n", + " \"paint\": {},\n", + "}\n", + "m.add_source(\"wms-test-source\", source)\n", + "m.add_layer(layer, before_id=\"aeroway_fill\")\n", "m" ] }, { "cell_type": "markdown", - "id": "40", + "id": "45", "metadata": {}, "source": [ - "### 3D buildings\n", - "\n", - "You can add 3D buildings to the map using the `add_3d_buildings` method." + "### Add raster tile" ] }, { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "46", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(\n", - " center=[-74.01201, 40.70473], zoom=16, pitch=60, bearing=35, style=\"basic-v2\"\n", - ")\n", - "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "style = {\n", + " \"version\": 8,\n", + " \"sources\": {\n", + " \"raster-tiles\": {\n", + " \"type\": \"raster\",\n", + " \"tiles\": [\n", + " \"https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg\"\n", + " ],\n", + " \"tileSize\": 256,\n", + " \"attribution\": \"Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors\",\n", + " }\n", + " },\n", + " \"layers\": [\n", + " {\n", + " \"id\": \"simple-tiles\",\n", + " \"type\": \"raster\",\n", + " \"source\": \"raster-tiles\",\n", + " \"minzoom\": 0,\n", + " \"maxzoom\": 22,\n", + " }\n", + " ],\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-74.5, 40], zoom=2, style=style)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "48", + "metadata": {}, + "source": [ + "### Add a vector tile source" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49", + "metadata": {}, + "outputs": [], + "source": [ + "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-122.447303, 37.753574], zoom=13, style=\"streets\")\n", + "source = {\n", + " \"type\": \"vector\",\n", + " \"url\": f\"https://api.maptiler.com/tiles/contours/tiles.json?key={MAPTILER_KEY}\",\n", + "}\n", + "layer = {\n", + " \"id\": \"terrain-data\",\n", + " \"type\": \"line\",\n", + " \"source\": \"contours\",\n", + " \"source-layer\": \"contour\",\n", + " \"layout\": {\"line-join\": \"round\", \"line-cap\": \"round\"},\n", + " \"paint\": {\"line-color\": \"#ff69b4\", \"line-width\": 1},\n", + "}\n", + "m.add_source(\"contours\", source)\n", + "m.add_layer(layer)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "51", + "metadata": {}, + "source": [ + "## MapTiler\n", + "\n", + "## 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": "52", + "metadata": {}, + "outputs": [], + "source": [ + "# import os\n", + "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" + ] + }, + { + "cell_type": "markdown", + "id": "53", + "metadata": {}, + "source": [ + "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": "54", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=\"streets\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=\"satellite\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=\"hybrid\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=\"topo\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "58", + "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": "59", + "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": "60", + "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": "61", + "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": "62", + "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": "code", + "execution_count": null, + "id": "63", + "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" + ] + }, + { + "cell_type": "markdown", + "id": "64", + "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": "65", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(\n", + " center=[-74.01201, 40.70473], zoom=16, pitch=60, bearing=35, 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", @@ -523,7 +860,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "66", "metadata": {}, "outputs": [], "source": [ @@ -538,7 +875,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "67", "metadata": {}, "source": [ "### 3D indoor mapping\n", @@ -549,7 +886,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "68", "metadata": {}, "outputs": [], "source": [ @@ -561,7 +898,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -571,7 +908,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "70", "metadata": {}, "outputs": [], "source": [ @@ -596,7 +933,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "71", "metadata": {}, "source": [ "### 3D choropleth map" @@ -605,7 +942,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -654,7 +991,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "73", "metadata": {}, "outputs": [], "source": [ @@ -694,7 +1031,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "74", "metadata": {}, "source": [ "## Visualize vector data\n", @@ -707,7 +1044,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -719,22 +1056,34 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "76", "metadata": {}, "outputs": [], "source": [ - "import requests" + "m = leafmap.Map(center=[0, 0], zoom=2, style=\"streets\")\n", + "m.add_marker(lng_lat=[0, 0], options={\"draggable\": True})\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "77", "metadata": {}, "outputs": [], "source": [ - "url = (\n", - " \"https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson\"\n", + "import requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78", + "metadata": {}, + "outputs": [], + "source": [ + "url = (\n", + " \"https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson\"\n", ")\n", "geojson = requests.get(url).json()" ] @@ -742,7 +1091,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "79", "metadata": {}, "outputs": [], "source": [ @@ -755,7 +1104,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "80", "metadata": {}, "outputs": [], "source": [ @@ -779,7 +1128,165 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "81", + "metadata": {}, + "source": [ + "### Customize marker icon image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82", + "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": "83", + "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": "code", + "execution_count": null, + "id": "84", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[0, 0], zoom=1, style=\"positron\")\n", + "image = \"https://maplibre.org/maplibre-gl-js/docs/assets/osgeo-logo.png\"\n", + "m.add_image(\"custom-marker\", image)\n", + "source = {\n", + " \"type\": \"geojson\",\n", + " \"data\": {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [100.4933, 13.7551]},\n", + " \"properties\": {\"year\": \"2004\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [6.6523, 46.5535]},\n", + " \"properties\": {\"year\": \"2006\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-123.3596, 48.4268]},\n", + " \"properties\": {\"year\": \"2007\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [18.4264, -33.9224]},\n", + " \"properties\": {\"year\": \"2008\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [151.195, -33.8552]},\n", + " \"properties\": {\"year\": \"2009\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [2.1404, 41.3925]},\n", + " \"properties\": {\"year\": \"2010\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-104.8548, 39.7644]},\n", + " \"properties\": {\"year\": \"2011\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-1.1665, 52.9539]},\n", + " \"properties\": {\"year\": \"2013\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-122.6544, 45.5428]},\n", + " \"properties\": {\"year\": \"2014\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [126.974, 37.5651]},\n", + " \"properties\": {\"year\": \"2015\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [7.1112, 50.7255]},\n", + " \"properties\": {\"year\": \"2016\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-71.0314, 42.3539]},\n", + " \"properties\": {\"year\": \"2017\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [39.2794, -6.8173]},\n", + " \"properties\": {\"year\": \"2018\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [26.0961, 44.4379]},\n", + " \"properties\": {\"year\": \"2019\"},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-114.0879, 51.0279]},\n", + " \"properties\": {\"year\": \"2020\"},\n", + " },\n", + " ],\n", + " },\n", + "}\n", + "\n", + "m.add_source(\"conferences\", source)\n", + "layer = {\n", + " \"id\": \"conferences\",\n", + " \"type\": \"symbol\",\n", + " \"source\": \"conferences\",\n", + " \"layout\": {\n", + " \"icon-image\": \"custom-marker\",\n", + " \"text-field\": [\"get\", \"year\"],\n", + " \"text-font\": [\"Open Sans Semibold\", \"Arial Unicode MS Bold\"],\n", + " \"text-offset\": [0, 1.25],\n", + " \"text-anchor\": \"top\",\n", + " },\n", + "}\n", + "\n", + "m.add_layer(layer)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "85", "metadata": {}, "source": [ "### Line data" @@ -788,7 +1295,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "86", "metadata": {}, "outputs": [], "source": [ @@ -842,7 +1349,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "87", "metadata": {}, "source": [ "### Polygon data" @@ -851,7 +1358,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "88", "metadata": {}, "outputs": [], "source": [ @@ -902,7 +1409,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "89", "metadata": {}, "outputs": [], "source": [ @@ -914,7 +1421,7 @@ }, { "cell_type": "markdown", - "id": "61", + "id": "90", "metadata": {}, "source": [ "### Multiple geometries" @@ -923,7 +1430,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "91", "metadata": {}, "outputs": [], "source": [ @@ -957,7 +1464,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "92", "metadata": {}, "outputs": [], "source": [ @@ -966,7 +1473,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "93", "metadata": {}, "source": [ "### Marker cluster\n", @@ -977,7 +1484,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "94", "metadata": {}, "outputs": [], "source": [ @@ -1039,7 +1546,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "95", "metadata": {}, "source": [ "### Local vector data\n", @@ -1050,7 +1557,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "96", "metadata": {}, "outputs": [], "source": [ @@ -1061,7 +1568,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68", + "id": "97", "metadata": {}, "outputs": [], "source": [ @@ -1073,7 +1580,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "98", "metadata": {}, "outputs": [], "source": [ @@ -1082,207 +1589,283 @@ }, { "cell_type": "markdown", - "id": "70", + "id": "99", "metadata": {}, "source": [ - "### Change building color" + "### GeoPandas" ] }, { "cell_type": "code", "execution_count": null, - "id": "71", + "id": "100", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-90.73414, 14.55524], zoom=13, style=\"basic\")\n", - "m.set_paint_property(\n", - " \"building\",\n", - " \"fill-color\",\n", - " [\"interpolate\", [\"exponential\", 0.5], [\"zoom\"], 15, \"#e2714b\", 22, \"#eee695\"],\n", - ")\n", - "m.set_paint_property(\n", - " \"building\",\n", - " \"fill-opacity\",\n", - " [\"interpolate\", [\"exponential\", 0.5], [\"zoom\"], 15, 0, 22, 1],\n", - ")\n", - "m" + "import geopandas as gpd" ] }, { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "101", "metadata": {}, "outputs": [], "source": [ - "m.add_call(\"zoomTo\", 19, {\"duration\": 9000})" + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"streets\")\n", + "url = \"https://github.com/opengeos/datasets/releases/download/us/us_states.geojson\"\n", + "gdf = gpd.read_file(url)\n", + "paint = {\n", + " \"fill-color\": \"#3388ff\",\n", + " \"fill-opacity\": 0.8,\n", + " \"fill-outline-color\": \"#ffffff\",\n", + "}\n", + "m.add_gdf(gdf, layer_type=\"fill\", name=\"States\", paint=paint)\n", + "m" ] }, { "cell_type": "markdown", - "id": "73", - "metadata": {}, - "source": [ - "### Live feature update\n", - "\n", - "#### Animate a point along a route" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "74", + "id": "102", "metadata": {}, - "outputs": [], "source": [ - "import time" + "### Change building color" ] }, { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "103", "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 = leafmap.Map(center=[-90.73414, 14.55524], zoom=13, style=\"basic\")\n", + "m.set_paint_property(\n", + " \"building\",\n", + " \"fill-color\",\n", + " [\"interpolate\", [\"exponential\", 0.5], [\"zoom\"], 15, \"#e2714b\", 22, \"#eee695\"],\n", + ")\n", + "m.set_paint_property(\n", + " \"building\",\n", + " \"fill-opacity\",\n", + " [\"interpolate\", [\"exponential\", 0.5], [\"zoom\"], 15, 0, 22, 1],\n", + ")\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "76", + "id": "104", "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)" + "m.add_call(\"zoomTo\", 19, {\"duration\": 9000})" ] }, { "cell_type": "markdown", - "id": "77", + "id": "105", "metadata": {}, "source": [ - "#### Update a feature in realtime" + "### Add a new layer below labels" ] }, { "cell_type": "code", "execution_count": null, - "id": "78", + "id": "106", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style=\"3d-terrain\")\n", + "m = leafmap.Map(center=[-88.137343, 35.137451], zoom=4, style=\"streets\")\n", + "source = {\n", + " \"type\": \"geojson\",\n", + " \"data\": \"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson\",\n", + "}\n", + "m.add_source(\"urban-areas\", source)\n", + "first_symbol_layer = m.find_first_symbol_layer()\n", + "layer = {\n", + " \"id\": \"urban-areas-fill\",\n", + " \"type\": \"fill\",\n", + " \"source\": \"urban-areas\",\n", + " \"layout\": {},\n", + " \"paint\": {\"fill-color\": \"#f08\", \"fill-opacity\": 0.4},\n", + "}\n", + "m.add_layer(layer, before_id=first_symbol_layer[\"id\"])\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "79", + "cell_type": "markdown", + "id": "107", "metadata": {}, - "outputs": [], "source": [ - "import geopandas as gpd" + "### Heat map" ] }, { "cell_type": "code", "execution_count": null, - "id": "80", - "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": "81", + "id": "108", "metadata": {}, "outputs": [], "source": [ + "m = leafmap.Map(center=[-120, 50], zoom=2, style=\"basic\")\n", "source = {\n", " \"type\": \"geojson\",\n", - " \"data\": {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"LineString\", \"coordinates\": [coordinates[0]]},\n", - " },\n", + " \"data\": \"https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson\",\n", "}\n", - "m.add_source(\"trace\", source)\n", + "m.add_source(\"earthquakes\", 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", + " \"id\": \"earthquakes-heat\",\n", + " \"type\": \"heatmap\",\n", + " \"source\": \"earthquakes\",\n", + " \"maxzoom\": 9,\n", + " \"paint\": {\n", + " # Increase the heatmap weight based on frequency and property magnitude\n", + " \"heatmap-weight\": [\"interpolate\", [\"linear\"], [\"get\", \"mag\"], 0, 0, 6, 1],\n", + " # Increase the heatmap color weight weight by zoom level\n", + " # heatmap-intensity is a multiplier on top of heatmap-weight\n", + " \"heatmap-intensity\": [\"interpolate\", [\"linear\"], [\"zoom\"], 0, 1, 9, 3],\n", + " # Color ramp for heatmap. Domain is 0 (low) to 1 (high).\n", + " # Begin color ramp at 0-stop with a 0-transparency color\n", + " # to create a blur-like effect.\n", + " \"heatmap-color\": [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"heatmap-density\"],\n", + " 0,\n", + " \"rgba(33,102,172,0)\",\n", + " 0.2,\n", + " \"rgb(103,169,207)\",\n", + " 0.4,\n", + " \"rgb(209,229,240)\",\n", + " 0.6,\n", + " \"rgb(253,219,199)\",\n", + " 0.8,\n", + " \"rgb(239,138,98)\",\n", + " 1,\n", + " \"rgb(178,24,43)\",\n", + " ],\n", + " # Adjust the heatmap radius by zoom level\n", + " \"heatmap-radius\": [\"interpolate\", [\"linear\"], [\"zoom\"], 0, 2, 9, 20],\n", + " # Transition from heatmap to circle layer by zoom level\n", + " \"heatmap-opacity\": [\"interpolate\", [\"linear\"], [\"zoom\"], 7, 1, 9, 0],\n", + " },\n", "}\n", - "m.add_layer(layer)\n", - "m.jump_to({\"center\": coordinates[0], \"zoom\": 14})\n", - "m.set_pitch(30)" + "m.add_layer(layer, before_id=\"waterway\")\n", + "layer2 = {\n", + " \"id\": \"earthquakes-point\",\n", + " \"type\": \"circle\",\n", + " \"source\": \"earthquakes\",\n", + " \"minzoom\": 7,\n", + " \"paint\": {\n", + " # Size circle radius by earthquake magnitude and zoom level\n", + " \"circle-radius\": [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"zoom\"],\n", + " 7,\n", + " [\"interpolate\", [\"linear\"], [\"get\", \"mag\"], 1, 1, 6, 4],\n", + " 16,\n", + " [\"interpolate\", [\"linear\"], [\"get\", \"mag\"], 1, 5, 6, 50],\n", + " ],\n", + " # Color circle by earthquake magnitude\n", + " \"circle-color\": [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"get\", \"mag\"],\n", + " 1,\n", + " \"rgba(33,102,172,0)\",\n", + " 2,\n", + " \"rgb(103,169,207)\",\n", + " 3,\n", + " \"rgb(209,229,240)\",\n", + " 4,\n", + " \"rgb(253,219,199)\",\n", + " 5,\n", + " \"rgb(239,138,98)\",\n", + " 6,\n", + " \"rgb(178,24,43)\",\n", + " ],\n", + " \"circle-stroke-color\": \"white\",\n", + " \"circle-stroke-width\": 1,\n", + " # Transition from heatmap to circle layer by zoom level\n", + " \"circle-opacity\": [\"interpolate\", [\"linear\"], [\"zoom\"], 7, 0, 8, 1],\n", + " },\n", + "}\n", + "m.add_layer(layer2, before_id=\"waterway\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "109", + "metadata": {}, + "source": [ + "### Visualize population density" ] }, { "cell_type": "code", "execution_count": null, - "id": "82", + "id": "110", "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)" + "m = leafmap.Map(center=[30.0222, -1.9596], zoom=7, style=\"streets\")\n", + "source = {\n", + " \"type\": \"geojson\",\n", + " \"data\": \"https://maplibre.org/maplibre-gl-js/docs/assets/rwanda-provinces.geojson\",\n", + "}\n", + "m.add_source(\"rwanda-provinces\", source)\n", + "layer = {\n", + " \"id\": \"rwanda-provinces\",\n", + " \"type\": \"fill\",\n", + " \"source\": \"rwanda-provinces\",\n", + " \"layout\": {},\n", + " \"paint\": {\n", + " \"fill-color\": [\n", + " \"let\",\n", + " \"density\",\n", + " [\"/\", [\"get\", \"population\"], [\"get\", \"sq-km\"]],\n", + " [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"zoom\"],\n", + " 8,\n", + " [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"var\", \"density\"],\n", + " 274,\n", + " [\"to-color\", \"#edf8e9\"],\n", + " 1551,\n", + " [\"to-color\", \"#006d2c\"],\n", + " ],\n", + " 10,\n", + " [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"var\", \"density\"],\n", + " 274,\n", + " [\"to-color\", \"#eff3ff\"],\n", + " 1551,\n", + " [\"to-color\", \"#08519c\"],\n", + " ],\n", + " ],\n", + " ],\n", + " \"fill-opacity\": 0.7,\n", + " },\n", + "}\n", + "m.add_layer(layer)\n", + "m" ] }, { "cell_type": "markdown", - "id": "83", + "id": "111", "metadata": {}, "source": [ "## Visualize raster data\n", @@ -1295,7 +1878,7 @@ { "cell_type": "code", "execution_count": null, - "id": "84", + "id": "112", "metadata": {}, "outputs": [], "source": [ @@ -1307,7 +1890,7 @@ { "cell_type": "code", "execution_count": null, - "id": "85", + "id": "113", "metadata": {}, "outputs": [], "source": [ @@ -1320,7 +1903,7 @@ { "cell_type": "code", "execution_count": null, - "id": "86", + "id": "114", "metadata": {}, "outputs": [], "source": [ @@ -1330,7 +1913,7 @@ { "cell_type": "code", "execution_count": null, - "id": "87", + "id": "115", "metadata": {}, "outputs": [], "source": [ @@ -1342,7 +1925,7 @@ { "cell_type": "code", "execution_count": null, - "id": "88", + "id": "116", "metadata": {}, "outputs": [], "source": [ @@ -1354,7 +1937,7 @@ { "cell_type": "code", "execution_count": null, - "id": "89", + "id": "117", "metadata": {}, "outputs": [], "source": [ @@ -1363,7 +1946,7 @@ }, { "cell_type": "markdown", - "id": "90", + "id": "118", "metadata": {}, "source": [ "### Cloud Optimized GeoTIFF (COG)\n", @@ -1374,7 +1957,7 @@ { "cell_type": "code", "execution_count": null, - "id": "91", + "id": "119", "metadata": {}, "outputs": [], "source": [ @@ -1393,7 +1976,7 @@ { "cell_type": "code", "execution_count": null, - "id": "92", + "id": "120", "metadata": {}, "outputs": [], "source": [ @@ -1402,7 +1985,7 @@ }, { "cell_type": "markdown", - "id": "93", + "id": "121", "metadata": {}, "source": [ "### STAC layer\n", @@ -1413,1208 +1996,2084 @@ { "cell_type": "code", "execution_count": null, - "id": "94", + "id": "122", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", + "m = leafmap.Map(style=\"streets\")\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.add_stac_layer(url, bands=[\"pan\"], name=\"Panchromatic\", vmin=0, vmax=150)\n", + "m.add_stac_layer(url, bands=[\"B4\", \"B3\", \"B2\"], name=\"RGB\", vmin=0, vmax=150)\n", "m" ] }, { - "cell_type": "markdown", - "id": "95", + "cell_type": "code", + "execution_count": null, + "id": "123", "metadata": {}, + "outputs": [], "source": [ - "## Customize layer styles" + "m.layer_interact()" ] }, { - "cell_type": "markdown", - "id": "96", + "cell_type": "code", + "execution_count": null, + "id": "124", "metadata": {}, + "outputs": [], "source": [ - "### Change layer color" + "collection = \"landsat-8-c2-l2\"\n", + "item = \"LC08_L2SP_047027_20201204_02_T1\"" ] }, { "cell_type": "code", "execution_count": null, - "id": "97", + "id": "125", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[12.338, 45.4385], zoom=17, style=\"streets\")\n", - "m" + "leafmap.stac_assets(collection=collection, item=item, titiler_endpoint=\"pc\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "98", + "id": "126", "metadata": {}, "outputs": [], "source": [ - "m.style_layer_interact(id=\"water\")" + "m = leafmap.Map(style=\"streets\")\n", + "m.add_stac_layer(\n", + " collection=collection,\n", + " item=item,\n", + " assets=[\"SR_B5\", \"SR_B4\", \"SR_B3\"],\n", + " name=\"Color infrared\",\n", + ")\n", + "m" ] }, { "cell_type": "markdown", - "id": "99", + "id": "127", "metadata": {}, "source": [ - "### Change case of labels" + "## Interact with the map" + ] + }, + { + "cell_type": "markdown", + "id": "128", + "metadata": {}, + "source": [ + "### Display a non-interactive map" ] }, { "cell_type": "code", "execution_count": null, - "id": "100", + "id": "129", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-116.231, 43.604], zoom=11, style=\"streets\")\n", - "geojson = {\n", - " \"type\": \"geojson\",\n", - " \"data\": \"https://maplibre.org/maplibre-gl-js/docs/assets/boise.geojson\",\n", - "}\n", - "m.add_source(\"off-leash-areas\", geojson)\n", - "layer = {\n", - " \"id\": \"off-leash-areas\",\n", - " \"type\": \"symbol\",\n", - " \"source\": \"off-leash-areas\",\n", - " \"layout\": {\n", - " \"icon-image\": \"dog-park-11\",\n", - " \"text-field\": [\n", - " \"format\",\n", - " [\"upcase\", [\"get\", \"FacilityName\"]],\n", - " {\"font-scale\": 0.8},\n", - " \"\\n\",\n", - " {},\n", - " [\"downcase\", [\"get\", \"Comments\"]],\n", - " {\"font-scale\": 0.6},\n", - " ],\n", - " \"text-font\": [\"Open Sans Semibold\", \"Arial Unicode MS Bold\"],\n", - " \"text-offset\": [0, 0.6],\n", - " \"text-anchor\": \"top\",\n", - " },\n", - "}\n", - "m.add_layer(layer)\n", + "m = leafmap.Map(\n", + " center=[-74.5, 40], zoom=9, interactive=False, style=\"streets\", controls={}\n", + ")\n", "m" ] }, + { + "cell_type": "markdown", + "id": "130", + "metadata": {}, + "source": [ + "### Fit bounds" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "101", + "id": "131", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "m = leafmap.Map(center=[-74.5, 40], zoom=9, style=\"streets\")\n", + "m" + ] }, { "cell_type": "markdown", - "id": "102", + "id": "132", "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" + "Fit to Kenya." ] }, { "cell_type": "code", "execution_count": null, - "id": "103", + "id": "133", "metadata": {}, "outputs": [], "source": [ - "url = \"https://open.gishub.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']}\")" + "bounds = [[32.958984, -5.353521], [43.50585, 5.615985]]\n", + "m.fit_bounds(bounds)" ] }, { "cell_type": "code", "execution_count": null, - "id": "104", + "id": "134", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", + "m = leafmap.Map(center=[-77.0214, 38.897], zoom=12, style=\"streets\")\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", + "geojson = {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", " {\n", - " \"id\": \"roads\",\n", - " \"source\": \"example_source\",\n", - " \"source-layer\": \"roads\",\n", - " \"type\": \"line\",\n", - " \"paint\": {\"line-color\": \"black\"},\n", - " },\n", + " \"type\": \"Feature\",\n", + " \"geometry\": {\n", + " \"type\": \"LineString\",\n", + " \"properties\": {},\n", + " \"coordinates\": [\n", + " [-77.0366048812866, 38.89873175227713],\n", + " [-77.03364372253417, 38.89876515143842],\n", + " [-77.03364372253417, 38.89549195896866],\n", + " [-77.02982425689697, 38.89549195896866],\n", + " [-77.02400922775269, 38.89387200688839],\n", + " [-77.01519012451172, 38.891416957534204],\n", + " [-77.01521158218382, 38.892068305429156],\n", + " [-77.00813055038452, 38.892051604275686],\n", + " [-77.00832366943358, 38.89143365883688],\n", + " [-77.00818419456482, 38.89082405874451],\n", + " [-77.00815200805664, 38.88989712255097],\n", + " ],\n", + " },\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.add_source(\"LineString\", {\"type\": \"geojson\", \"data\": geojson})\n", + "layer = {\n", + " \"id\": \"LineString\",\n", + " \"type\": \"line\",\n", + " \"source\": \"LineString\",\n", + " \"layout\": {\"line-join\": \"round\", \"line-cap\": \"round\"},\n", + " \"paint\": {\"line-color\": \"#BF93E4\", \"line-width\": 5},\n", + "}\n", + "m.add_layer(layer)\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "105", + "id": "135", "metadata": {}, "outputs": [], "source": [ - "m.layer_interact()" + "bounds = leafmap.geojson_bounds(geojson)\n", + "bounds" ] }, { - "cell_type": "markdown", - "id": "106", + "cell_type": "code", + "execution_count": null, + "id": "136", "metadata": {}, + "outputs": [], "source": [ - "### Overture data\n", - "\n", - "You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps)." + "m.fit_bounds(bounds)" ] }, { "cell_type": "markdown", - "id": "107", + "id": "137", "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)." + "### Restrict map panning to an area" ] }, { "cell_type": "code", "execution_count": null, - "id": "108", + "id": "138", "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']}\")" + "bounds = [\n", + " [-74.04728500751165, 40.68392799015035],\n", + " [-73.91058699000139, 40.87764500765852],\n", + "]" ] }, { "cell_type": "code", "execution_count": null, - "id": "109", + "id": "139", "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 = leafmap.Map(center=[-73.9978, 40.7209], zoom=13, max_bounds=bounds, style=\"streets\")\n", "m" ] }, + { + "cell_type": "markdown", + "id": "140", + "metadata": {}, + "source": [ + "### Fly to" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "110", + "id": "141", "metadata": {}, "outputs": [], "source": [ - "m.layer_interact()" + "m = leafmap.Map(center=[-2.242467, 53.478122], zoom=9, style=\"streets\")\n", + "m" ] }, { - "cell_type": "markdown", - "id": "111", + "cell_type": "code", + "execution_count": null, + "id": "142", "metadata": {}, + "outputs": [], "source": [ - "### 3D PMTiles\n", - "\n", - "Visualize the global building data in 3D." + "m.fly_to(lon=-73.983609, lat=40.754368, zoom=12)" ] }, { "cell_type": "code", "execution_count": null, - "id": "112", + "id": "143", "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']}\")" + "m = leafmap.Map(center=[-74.5, 40], zoom=9, style=\"streets\")\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "113", + "id": "144", "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", + "options = {\n", + " \"lon\": 74.5,\n", + " \"lat\": 40,\n", + " \"zoom\": 9,\n", + " \"bearing\": 0,\n", + " \"speed\": 0.2,\n", + " \"curve\": 1,\n", + " \"essential\": True,\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" + "m.fly_to(**options)" ] }, { "cell_type": "markdown", - "id": "114", + "id": "145", "metadata": {}, "source": [ - "### 3D buildings" + "### Jump to a series of locations" ] }, { "cell_type": "code", "execution_count": null, - "id": "115", + "id": "146", "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(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_3d_buildings(release=\"2024-09-18\", template=\"simple\")\n", - "m.add_layer_control()\n", - "m" + "import time" ] }, { - "cell_type": "markdown", - "id": "116", + "cell_type": "code", + "execution_count": null, + "id": "147", "metadata": {}, + "outputs": [], "source": [ - "### 2D buildings" + "m = leafmap.Map(center=[100.507, 13.745], zoom=9, style=\"streets\")\n", + "\n", + "cities = {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [100.507, 13.745]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [98.993, 18.793]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [99.838, 19.924]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [102.812, 17.408]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [100.458, 7.001]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [100.905, 12.935]},\n", + " },\n", + " ],\n", + "}\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "117", + "id": "148", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"buildings\", opacity=0.8)\n", - "m.add_layer_control()\n", - "m" + "for index, city in enumerate(cities[\"features\"]):\n", + " time.sleep(2)\n", + " coords = city[\"geometry\"][\"coordinates\"]\n", + " m.jump_to({\"center\": coords})" ] }, { "cell_type": "markdown", - "id": "118", + "id": "149", "metadata": {}, "source": [ - "### Transportation" + "### Get coordinates of the mouse pointer" ] }, { "cell_type": "code", "execution_count": null, - "id": "119", + "id": "150", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"transportation\", opacity=0.8)\n", - "m.add_layer_control()\n", - "m" - ] - }, - { - "cell_type": "markdown", - "id": "120", - "metadata": {}, - "source": [ - "### Places" + "import ipywidgets as widgets" ] }, { "cell_type": "code", "execution_count": null, - "id": "121", + "id": "151", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"places\", opacity=0.8)\n", - "m.add_layer_control()\n", + "m = leafmap.Map(center=[-74.5, 40], zoom=9, style=\"streets\")\n", "m" ] }, { - "cell_type": "markdown", - "id": "122", + "cell_type": "code", + "execution_count": null, + "id": "152", "metadata": {}, + "outputs": [], "source": [ - "### Addresses" + "m.clicked" ] }, { "cell_type": "code", "execution_count": null, - "id": "123", + "id": "153", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"addresses\", opacity=0.8)\n", - "m.add_layer_control()\n", - "m" + "output = widgets.Output()\n", + "\n", + "\n", + "def log_lng_lat(lng_lat):\n", + " with output:\n", + " output.clear_output()\n", + " print(lng_lat.new)\n", + "\n", + "\n", + "m.observe(log_lng_lat, names=\"clicked\")\n", + "output" ] }, { "cell_type": "markdown", - "id": "124", + "id": "154", "metadata": {}, "source": [ - "### Base" + "## Customize layer styles" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "125", + "cell_type": "markdown", + "id": "155", "metadata": {}, - "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"base\", opacity=0.8)\n", - "m.add_layer_control()\n", - "m" + "### Change layer color" ] }, { - "cell_type": "markdown", - "id": "126", + "cell_type": "code", + "execution_count": null, + "id": "156", "metadata": {}, + "outputs": [], "source": [ - "### Divisions" + "m = leafmap.Map(center=[12.338, 45.4385], zoom=17, style=\"streets\")\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "127", + "id": "157", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"divisions\", opacity=0.8)\n", - "m.add_layer_control()\n", - "m" + "m.style_layer_interact(id=\"water\")" ] }, { "cell_type": "markdown", - "id": "128", + "id": "158", "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" + "### Change case of labels" ] }, { "cell_type": "code", "execution_count": null, - "id": "129", + "id": "159", "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", + "m = leafmap.Map(center=[-116.231, 43.604], zoom=11, style=\"streets\")\n", + "geojson = {\n", " \"type\": \"geojson\",\n", - " \"data\": {\n", - " \"type\": \"FeatureCollection\",\n", - " \"features\": [\n", - " {\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [0, 0]}}\n", - " ],\n", - " },\n", + " \"data\": \"https://maplibre.org/maplibre-gl-js/docs/assets/boise.geojson\",\n", "}\n", - "\n", + "m.add_source(\"off-leash-areas\", geojson)\n", "layer = {\n", - " \"id\": \"points\",\n", + " \"id\": \"off-leash-areas\",\n", " \"type\": \"symbol\",\n", - " \"source\": \"point\",\n", + " \"source\": \"off-leash-areas\",\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", + " \"icon-image\": \"dog-park-11\",\n", + " \"text-field\": [\n", + " \"format\",\n", + " [\"upcase\", [\"get\", \"FacilityName\"]],\n", + " {\"font-scale\": 0.8},\n", + " \"\\n\",\n", + " {},\n", + " [\"downcase\", [\"get\", \"Comments\"]],\n", + " {\"font-scale\": 0.6},\n", + " ],\n", + " \"text-font\": [\"Open Sans Semibold\", \"Arial Unicode MS Bold\"],\n", + " \"text-offset\": [0, 0.6],\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": "160", + "metadata": {}, + "source": [ + "### Variable label placement" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "130", + "id": "161", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", - "image = \"https://i.imgur.com/LmTETPX.png\"\n", - "m.add_image(image=image, position=\"bottom-right\")\n", + "m = leafmap.Map(center=[-77.04, 38.907], zoom=11, style=\"streets\")\n", + "\n", + "places = {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"description\": \"Ford's Theater\", \"icon\": \"theatre\"},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-77.038659, 38.931567]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"description\": \"The Gaslight\", \"icon\": \"theatre\"},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-77.003168, 38.894651]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"description\": \"Horrible Harry's\", \"icon\": \"bar\"},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-77.090372, 38.881189]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"description\": \"Bike Party\", \"icon\": \"bicycle\"},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-77.052477, 38.943951]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"description\": \"Rockabilly Rockstars\", \"icon\": \"music\"},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-77.031706, 38.914581]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"description\": \"District Drum Tribe\", \"icon\": \"music\"},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-77.020945, 38.878241]},\n", + " },\n", + " {\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"description\": \"Motown Memories\", \"icon\": \"music\"},\n", + " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-77.007481, 38.876516]},\n", + " },\n", + " ],\n", + "}\n", + "source = {\"type\": \"geojson\", \"data\": places}\n", + "m.add_source(\"places\", source)\n", + "\n", + "layer = {\n", + " \"id\": \"poi-labels\",\n", + " \"type\": \"symbol\",\n", + " \"source\": \"places\",\n", + " \"layout\": {\n", + " \"text-field\": [\"get\", \"description\"],\n", + " \"text-variable-anchor\": [\"top\", \"bottom\", \"left\", \"right\"],\n", + " \"text-radial-offset\": 0.5,\n", + " \"text-justify\": \"auto\",\n", + " \"icon-image\": [\"concat\", [\"get\", \"icon\"], \"_15\"],\n", + " },\n", + "}\n", + "m.add_layer(layer)\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "131", + "id": "162", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", - "content = ''\n", - "m.add_html(content, bg_color=\"transparent\", position=\"bottom-right\")\n", - "m" + "m.rotate_to(bearing=180, options={\"duration\": 10000})" + ] + }, + { + "cell_type": "markdown", + "id": "163", + "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": "132", + "id": "164", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "url = \"https://open.gishub.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": "133", + "id": "165", "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", + "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", - "# Generate the icon data\n", - "width = 64 # The image will be 64 pixels square\n", - "height = 64\n", - "bytes_per_pixel = 4 # Each pixel is represented by 4 bytes: red, green, blue, and alpha\n", - "data = np.zeros((width, width, bytes_per_pixel), dtype=np.uint8)\n", + "# style = leafmap.pmtiles_style(url) # Use default style\n", "\n", - "for x in range(width):\n", - " for y in range(width):\n", - " data[y, x, 0] = int((y / width) * 255) # red\n", - " data[y, x, 1] = int((x / width) * 255) # green\n", - " data[y, x, 2] = 128 # blue\n", - " data[y, x, 3] = 255 # alpha\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": "166", + "metadata": {}, + "outputs": [], + "source": [ + "m.layer_interact()" + ] + }, + { + "cell_type": "markdown", + "id": "167", + "metadata": {}, + "source": [ + "### Overture data\n", "\n", - "# Flatten the data array\n", - "flat_data = data.flatten()\n", + "You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps)." + ] + }, + { + "cell_type": "markdown", + "id": "168", + "metadata": {}, + "source": [ + "### Source Cooperative\n", "\n", - "# Create the image dictionary\n", - "image_dict = {\n", - " \"width\": width,\n", - " \"height\": height,\n", - " \"data\": flat_data.tolist(),\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": "169", + "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": "170", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[0, 20], zoom=2, height=\"800px\")\n", + "m.add_basemap(\"Google Hybrid\", visible=False)\n", "\n", - "m = leafmap.Map(center=[0, 0], zoom=1, style=\"streets\")\n", - "m.add_image(\"gradient\", image_dict)\n", - "source = {\n", - " \"type\": \"geojson\",\n", - " \"data\": {\n", - " \"type\": \"FeatureCollection\",\n", - " \"features\": [\n", - " {\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [0, 0]}}\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", - "layer = {\n", - " \"id\": \"points\",\n", - " \"type\": \"symbol\",\n", - " \"source\": \"point\",\n", - " \"layout\": {\"icon-image\": \"gradient\"},\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": "171", + "metadata": {}, + "outputs": [], + "source": [ + "m.layer_interact()" + ] + }, + { + "cell_type": "markdown", + "id": "172", + "metadata": {}, + "source": [ + "### 3D PMTiles\n", + "\n", + "Visualize the global building data in 3D." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "173", + "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": "174", + "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": "175", + "metadata": {}, + "source": [ + "### 3D buildings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "176", + "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(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_3d_buildings(release=\"2024-09-18\", template=\"simple\")\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "177", + "metadata": {}, + "source": [ + "### 2D buildings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "178", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"buildings\", opacity=0.8)\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "179", + "metadata": {}, + "source": [ + "### Transportation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "180", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"transportation\", opacity=0.8)\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "181", + "metadata": {}, + "source": [ + "### Places" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "182", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"places\", opacity=0.8)\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "183", + "metadata": {}, + "source": [ + "### Addresses" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "184", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"addresses\", opacity=0.8)\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "185", + "metadata": {}, + "source": [ + "### Base" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "186", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"base\", opacity=0.8)\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "187", + "metadata": {}, + "source": [ + "### Divisions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "188", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map()\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"divisions\", opacity=0.8)\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "189", + "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": "190", + "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": "code", + "execution_count": null, + "id": "191", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "image = \"https://i.imgur.com/LmTETPX.png\"\n", + "m.add_image(image=image, position=\"bottom-right\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "192", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "content = ''\n", + "m.add_html(content, bg_color=\"transparent\", position=\"bottom-right\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "193", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "194", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "\n", + "# Generate the icon data\n", + "width = 64 # The image will be 64 pixels square\n", + "height = 64\n", + "bytes_per_pixel = 4 # Each pixel is represented by 4 bytes: red, green, blue, and alpha\n", + "data = np.zeros((width, width, bytes_per_pixel), dtype=np.uint8)\n", + "\n", + "for x in range(width):\n", + " for y in range(width):\n", + " data[y, x, 0] = int((y / width) * 255) # red\n", + " data[y, x, 1] = int((x / width) * 255) # green\n", + " data[y, x, 2] = 128 # blue\n", + " data[y, x, 3] = 255 # alpha\n", + "\n", + "# Flatten the data array\n", + "flat_data = data.flatten()\n", + "\n", + "# Create the image dictionary\n", + "image_dict = {\n", + " \"width\": width,\n", + " \"height\": height,\n", + " \"data\": flat_data.tolist(),\n", + "}\n", + "\n", + "m = leafmap.Map(center=[0, 0], zoom=1, style=\"streets\")\n", + "m.add_image(\"gradient\", image_dict)\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\": {\"icon-image\": \"gradient\"},\n", + "}\n", + "\n", + "m.add_source(\"point\", source)\n", + "m.add_layer(layer)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "195", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "196", + "metadata": {}, + "source": [ + "### Add text" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "197", + "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": "198", + "metadata": {}, + "source": [ + "### Add GIF" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "199", + "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": "200", + "metadata": {}, + "source": [ + "### Add HTML" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "201", + "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": "202", + "metadata": {}, + "source": [ + "### Add colorbar" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "203", + "metadata": {}, + "outputs": [], + "source": [ + "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "204", + "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": "205", + "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": "206", + "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": "207", + "metadata": {}, + "source": [ + "### Add legend" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "208", + "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": "209", + "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": "210", + "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": "211", + "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": "212", + "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": "213", + "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": "214", + "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": "215", + "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": "216", + "metadata": {}, + "source": [ + "### Multiple Deck.GL layers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "217", + "metadata": {}, + "outputs": [], + "source": [ + "import requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "218", + "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": "219", + "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_source(\"point\", source)\n", - "m.add_layer(layer)\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": "code", - "execution_count": null, - "id": "134", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", - "id": "135", + "id": "220", "metadata": {}, "source": [ - "### Add text" + "## 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": "136", + "id": "221", "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 = 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": "markdown", - "id": "137", - "metadata": {}, - "source": [ - "### Add GIF" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "138", + "id": "222", "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" + "m.layer_interact()" ] }, { "cell_type": "markdown", - "id": "139", + "id": "223", "metadata": {}, "source": [ - "### Add HTML" + "We can also overlay other data layers on top of Earth Engine data layers." ] }, { "cell_type": "code", "execution_count": null, - "id": "140", + "id": "224", "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 = 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": "141", + "id": "225", "metadata": {}, "source": [ - "### Add colorbar" + "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": "142", + "id": "226", "metadata": {}, "outputs": [], "source": [ - "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"" + "# import ee\n", + "# ee.Initialize(project=\"YOUR-PROJECT-ID\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "143", + "id": "227", "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" + "# 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": "code", - "execution_count": null, - "id": "144", + "cell_type": "markdown", + "id": "228", "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" + "## Animations\n", + "\n", + "### Animate a line" ] }, { "cell_type": "code", "execution_count": null, - "id": "145", + "id": "229", "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" + "import time\n", + "import pandas as pd" ] }, { - "cell_type": "markdown", - "id": "146", + "cell_type": "code", + "execution_count": null, + "id": "230", "metadata": {}, + "outputs": [], "source": [ - "### Add legend" + "url = \"https://github.com/opengeos/datasets/releases/download/world/animated_line_data.csv\"\n", + "df = pd.read_csv(url)\n", + "df_sample = df.sample(n=1000, random_state=1).sort_index()\n", + "df_sample.loc[len(df_sample)] = df.iloc[-1]\n", + "df_sample.head()" ] }, { "cell_type": "code", "execution_count": null, - "id": "147", + "id": "231", "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 = leafmap.Map(center=[0, 0], zoom=0.5, style=\"streets\")\n", + "geojson = {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", + " {\"type\": \"Feature\", \"geometry\": {\"type\": \"LineString\", \"coordinates\": [[0, 0]]}}\n", + " ],\n", + "}\n", + "source = {\"type\": \"geojson\", \"data\": geojson}\n", + "m.add_source(\"line\", source)\n", + "layer = {\n", + " \"id\": \"line-animation\",\n", + " \"type\": \"line\",\n", + " \"source\": \"line\",\n", + " \"layout\": {\"line-cap\": \"round\", \"line-join\": \"round\"},\n", + " \"paint\": {\"line-color\": \"#ed6498\", \"line-width\": 5, \"line-opacity\": 0.8},\n", + "}\n", + "m.add_layer(layer)\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "148", + "id": "232", "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" + "run_times = 2\n", + "for i in range(run_times):\n", + " geojson[\"features\"][0][\"geometry\"][\"coordinates\"] = [[0, 0]]\n", + " for row in df_sample.itertuples():\n", + " time.sleep(0.005)\n", + " geojson[\"features\"][0][\"geometry\"][\"coordinates\"].append([row.x, row.y])\n", + " m.set_data(\"line\", geojson)" + ] + }, + { + "cell_type": "markdown", + "id": "233", + "metadata": {}, + "source": [ + "### Animate map camera around a point" ] }, { "cell_type": "code", "execution_count": null, - "id": "149", + "id": "234", "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", + "m = leafmap.Map(center=[-87.62712, 41.89033], zoom=15, pitch=45, style=\"streets\")\n", + "layers = m.get_style_layers()\n", + "for layer in layers:\n", + " if layer[\"type\"] == \"symbol\" and (\"text-field\" in layer[\"layout\"]):\n", + " m.remove_layer(layer[\"id\"])\n", + "layer = {\n", + " \"id\": \"3d-buildings\",\n", + " \"source\": \"composite\",\n", + " \"source-layer\": \"building\",\n", + " \"filter\": [\"==\", \"extrude\", \"true\"],\n", + " \"type\": \"fill-extrusion\",\n", + " \"min_zoom\": 15,\n", + " \"paint\": {\n", + " \"fill-extrusion-color\": \"#aaa\",\n", + " \"fill-extrusion-height\": [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"zoom\"],\n", + " 15,\n", + " 0,\n", + " 15.05,\n", + " [\"get\", \"height\"],\n", + " ],\n", + " \"fill-extrusion-base\": [\n", + " \"interpolate\",\n", + " [\"linear\"],\n", + " [\"zoom\"],\n", + " 15,\n", + " 0,\n", + " 15.05,\n", + " [\"get\", \"min_height\"],\n", + " ],\n", + " \"fill-extrusion-opacity\": 0.6,\n", + " },\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.add_layer(layer)\n", "m" ] }, { "cell_type": "markdown", - "id": "150", + "id": "235", "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." + "### Animate a point" ] }, { "cell_type": "code", "execution_count": null, - "id": "151", + "id": "236", "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" + "for degree in range(0, 360, 1):\n", + " m.rotate_to(degree, {\"duration\": 0})\n", + " time.sleep(0.1)" ] }, { "cell_type": "code", "execution_count": null, - "id": "152", + "id": "237", "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" + "import math" ] }, { - "cell_type": "markdown", - "id": "153", + "cell_type": "code", + "execution_count": null, + "id": "238", "metadata": {}, + "outputs": [], "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" + "def point_on_circle(angle, radius):\n", + " return {\n", + " \"type\": \"Point\",\n", + " \"coordinates\": [math.cos(angle) * radius, math.sin(angle) * radius],\n", + " }" ] }, { "cell_type": "code", "execution_count": null, - "id": "154", + "id": "239", "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", + "m = leafmap.Map(center=[0, 0], zoom=2, style=\"streets\")\n", + "radius = 20\n", + "source = {\"type\": \"geojson\", \"data\": point_on_circle(0, radius)}\n", + "m.add_source(\"point\", source)\n", + "layer = {\n", + " \"id\": \"point\",\n", + " \"source\": \"point\",\n", + " \"type\": \"circle\",\n", + " \"paint\": {\"circle-radius\": 10, \"circle-color\": \"#007cbf\"},\n", "}\n", - "\n", - "m.add_deck_layers([deck_grid_layer], tooltip=\"Number of points: {{ count }}\")\n", + "m.add_layer(layer)\n", "m" ] }, - { - "cell_type": "markdown", - "id": "155", - "metadata": {}, - "source": [ - "### Multiple Deck.GL layers" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "156", + "id": "240", "metadata": {}, "outputs": [], "source": [ - "import requests" + "def animate_marker(duration, frame_rate, radius):\n", + " start_time = time.time()\n", + " while (time.time() - start_time) < duration:\n", + " timestamp = (time.time() - start_time) * 1000 # Convert to milliseconds\n", + " angle = timestamp / 1000 # Divisor controls the animation speed\n", + " point = point_on_circle(angle, radius)\n", + " m.set_data(\"point\", point)\n", + " # Wait for the next frame\n", + " time.sleep(1 / frame_rate)" ] }, { "cell_type": "code", "execution_count": null, - "id": "157", + "id": "241", "metadata": {}, "outputs": [], "source": [ - "data = requests.get(\n", - " \"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson\"\n", - ").json()" + "duration = 10 # Duration of the animation in seconds\n", + "frame_rate = 30 # Frames per second" ] }, { "cell_type": "code", "execution_count": null, - "id": "158", + "id": "242", "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" + "animate_marker(duration, frame_rate, radius)" ] }, { "cell_type": "markdown", - "id": "159", + "id": "243", "metadata": {}, "source": [ - "## Google Earth Engine\n", - "\n", - "You can use the Earth Engine Python API to load and visualize Earth Engine data." + "### Animate a point along a route" ] }, { "cell_type": "code", "execution_count": null, - "id": "160", + "id": "244", "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 = 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": "161", + "id": "245", "metadata": {}, "outputs": [], "source": [ - "m.layer_interact()" + "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": "162", + "id": "246", "metadata": {}, "source": [ - "We can also overlay other data layers on top of Earth Engine data layers." + "### Update a feature in realtime" ] }, { "cell_type": "code", "execution_count": null, - "id": "163", + "id": "247", "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 = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style=\"3d-terrain\")\n", "m" ] }, { - "cell_type": "markdown", - "id": "164", + "cell_type": "code", + "execution_count": null, + "id": "248", "metadata": {}, + "outputs": [], "source": [ - "If you have an Earth Engine, you can uncomment the first two code blocks to add any Earth Engine datasets." + "import geopandas as gpd" ] }, { "cell_type": "code", "execution_count": null, - "id": "165", + "id": "249", "metadata": {}, "outputs": [], "source": [ - "# import ee\n", - "# ee.Initialize(project=\"YOUR-PROJECT-ID\")" + "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": "166", + "id": "250", "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" + "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": "251", + "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": "167", + "id": "252", "metadata": {}, "source": [ "## To HTML\n", @@ -2625,7 +4084,7 @@ { "cell_type": "code", "execution_count": null, - "id": "168", + "id": "253", "metadata": {}, "outputs": [], "source": [ @@ -2637,7 +4096,7 @@ { "cell_type": "code", "execution_count": null, - "id": "169", + "id": "254", "metadata": {}, "outputs": [], "source": [ @@ -2658,7 +4117,7 @@ { "cell_type": "code", "execution_count": null, - "id": "170", + "id": "255", "metadata": {}, "outputs": [], "source": [ diff --git a/book/geospatial/maplibre.md b/book/geospatial/maplibre.md index 2ce81db..86aec98 100644 --- a/book/geospatial/maplibre.md +++ b/book/geospatial/maplibre.md @@ -43,15 +43,6 @@ Import the maplibre mapping backend. import leafmap.maplibregl as leafmap ``` -## Set up API Key - -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/). - -```{code-cell} ipython3 -# import os -# os.environ["MAPTILER_KEY"] = "YOUR_API_KEY" -``` - ## Create interactive maps ### Create a simple map @@ -92,16 +83,131 @@ m = leafmap.Map(style=style) m ``` +```{code-cell} ipython3 +m = leafmap.Map(style="liberty") +m +``` + ### Add map controls The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`. +#### Geolocate control + ```{code-cell} ipython3 m = leafmap.Map() m.add_control("geolocate", position="top-left") m ``` +#### Fullscreen control + +```{code-cell} ipython3 +m = leafmap.Map(center=[11.255, 43.77], zoom=13, style="streets", controls={}) +m.add_control("fullscreen", position="top-right") +m +``` + +#### Navigation control + +```{code-cell} ipython3 +m = leafmap.Map(center=[11.255, 43.77], zoom=13, style="streets", controls={}) +m.add_control("navigation", position="top-left") +m +``` + +#### Draw control + +Add the default draw control. + +```{code-cell} ipython3 +m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") +m.add_draw_control(position="top-left") +m +``` + +Only activate a give set of control. + +```{code-cell} ipython3 +from maplibre.plugins import MapboxDrawControls, MapboxDrawOptions +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") +draw_options = MapboxDrawOptions( + display_controls_default=False, + controls=MapboxDrawControls(polygon=True, line_string=True, point=True, trash=True), +) +m.add_draw_control(draw_options) +m +``` + +Load a GeoJSON FeatureCollection to the draw control. + +```{code-cell} ipython3 +m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") +geojson = { + "type": "FeatureCollection", + "features": [ + { + "id": "abc", + "type": "Feature", + "properties": {}, + "geometry": { + "coordinates": [ + [ + [-119.08, 45.95], + [-119.79, 42.08], + [-107.28, 41.43], + [-108.15, 46.44], + [-119.08, 45.95], + ] + ], + "type": "Polygon", + }, + }, + { + "id": "xyz", + "type": "Feature", + "properties": {}, + "geometry": { + "coordinates": [ + [ + [-103.87, 38.08], + [-108.54, 36.44], + [-106.25, 33.00], + [-99.91, 31.79], + [-96.82, 35.48], + [-98.80, 37.77], + [-103.87, 38.08], + ] + ], + "type": "Polygon", + }, + }, + ], +} +m.add_draw_control(position="top-left", geojson=geojson) +m +``` + +Retrieve the draw features. + +```{code-cell} ipython3 +m.draw_features_selected +``` + +```{code-cell} ipython3 +m.draw_feature_collection_all +``` + +### Disable scroll zoom + +```{code-cell} ipython3 +m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style="liberty") +m +``` + ### Add basemaps You can add basemaps to the map using the `add_basemap` method. @@ -159,7 +265,94 @@ m.add_legend(builtin_legend="NWI", title="Wetland Type") m ``` -### MapTiler styles +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style="streets") + +source = { + "type": "raster", + "tiles": [ + "https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015" + ], + "tileSize": 256, +} +layer = { + "id": "wms-test-layer", + "type": "raster", + "source": "wms-test-source", + "paint": {}, +} +m.add_source("wms-test-source", source) +m.add_layer(layer, before_id="aeroway_fill") +m +``` + +### Add raster tile + +```{code-cell} ipython3 +style = { + "version": 8, + "sources": { + "raster-tiles": { + "type": "raster", + "tiles": [ + "https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg" + ], + "tileSize": 256, + "attribution": 'Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors', + } + }, + "layers": [ + { + "id": "simple-tiles", + "type": "raster", + "source": "raster-tiles", + "minzoom": 0, + "maxzoom": 22, + } + ], +} +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.5, 40], zoom=2, style=style) +m +``` + +### Add a vector tile source + +```{code-cell} ipython3 +MAPTILER_KEY = leafmap.get_api_key("MAPTILER_KEY") +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[-122.447303, 37.753574], zoom=13, style="streets") +source = { + "type": "vector", + "url": f"https://api.maptiler.com/tiles/contours/tiles.json?key={MAPTILER_KEY}", +} +layer = { + "id": "terrain-data", + "type": "line", + "source": "contours", + "source-layer": "contour", + "layout": {"line-join": "round", "line-cap": "round"}, + "paint": {"line-color": "#ff69b4", "line-width": 1}, +} +m.add_source("contours", source) +m.add_layer(layer) +m +``` + +## MapTiler + +## Set up API Key + +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/). + +```{code-cell} ipython3 +# import os +# os.environ["MAPTILER_KEY"] = "YOUR_API_KEY" +``` You can use any named style from MapTiler by setting the style parameter to the name of the style. @@ -415,6 +608,12 @@ m.add_marker(lng_lat=[12.550343, 55.665957]) m ``` +```{code-cell} ipython3 +m = leafmap.Map(center=[0, 0], zoom=2, style="streets") +m.add_marker(lng_lat=[0, 0], options={"draggable": True}) +m +``` + ```{code-cell} ipython3 import requests ``` @@ -452,6 +651,140 @@ m.add_popup("cities") m ``` +### Customize marker icon image + +```{code-cell} ipython3 +url = ( + "https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson" +) +geojson = requests.get(url).json() +``` + +```{code-cell} ipython3 +m = leafmap.Map(style="streets") +source = {"type": "geojson", "data": geojson} + +layer = { + "id": "cities", + "type": "symbol", + "source": "point", + "layout": { + "icon-image": "marker_15", + "icon-size": 1, + }, +} +m.add_source("point", source) +m.add_layer(layer) +m.add_popup("cities") +m +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[0, 0], zoom=1, style="positron") +image = "https://maplibre.org/maplibre-gl-js/docs/assets/osgeo-logo.png" +m.add_image("custom-marker", image) +source = { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [100.4933, 13.7551]}, + "properties": {"year": "2004"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [6.6523, 46.5535]}, + "properties": {"year": "2006"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-123.3596, 48.4268]}, + "properties": {"year": "2007"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [18.4264, -33.9224]}, + "properties": {"year": "2008"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [151.195, -33.8552]}, + "properties": {"year": "2009"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [2.1404, 41.3925]}, + "properties": {"year": "2010"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-104.8548, 39.7644]}, + "properties": {"year": "2011"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-1.1665, 52.9539]}, + "properties": {"year": "2013"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-122.6544, 45.5428]}, + "properties": {"year": "2014"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [126.974, 37.5651]}, + "properties": {"year": "2015"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [7.1112, 50.7255]}, + "properties": {"year": "2016"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-71.0314, 42.3539]}, + "properties": {"year": "2017"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [39.2794, -6.8173]}, + "properties": {"year": "2018"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [26.0961, 44.4379]}, + "properties": {"year": "2019"}, + }, + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-114.0879, 51.0279]}, + "properties": {"year": "2020"}, + }, + ], + }, +} + +m.add_source("conferences", source) +layer = { + "id": "conferences", + "type": "symbol", + "source": "conferences", + "layout": { + "icon-image": "custom-marker", + "text-field": ["get", "year"], + "text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"], + "text-offset": [0, 1.25], + "text-anchor": "top", + }, +} + +m.add_layer(layer) +m +``` + ### Line data ```{code-cell} ipython3 @@ -671,6 +1004,25 @@ leafmap.download_file(url, filepath, quiet=True) m.open_geojson() ``` +### GeoPandas + +```{code-cell} ipython3 +import geopandas as gpd +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") +url = "https://github.com/opengeos/datasets/releases/download/us/us_states.geojson" +gdf = gpd.read_file(url) +paint = { + "fill-color": "#3388ff", + "fill-opacity": 0.8, + "fill-outline-color": "#ffffff", +} +m.add_gdf(gdf, layer_type="fill", name="States", paint=paint) +m +``` + ### Change building color ```{code-cell} ipython3 @@ -692,176 +1044,468 @@ m m.add_call("zoomTo", 19, {"duration": 9000}) ``` -### Live feature update - -#### Animate a point along a route +### Add a new layer below labels ```{code-cell} ipython3 -import time +m = leafmap.Map(center=[-88.137343, 35.137451], zoom=4, style="streets") +source = { + "type": "geojson", + "data": "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson", +} +m.add_source("urban-areas", source) +first_symbol_layer = m.find_first_symbol_layer() +layer = { + "id": "urban-areas-fill", + "type": "fill", + "source": "urban-areas", + "layout": {}, + "paint": {"fill-color": "#f08", "fill-opacity": 0.4}, +} +m.add_layer(layer, before_id=first_symbol_layer["id"]) +m ``` +### Heat map + ```{code-cell} ipython3 -m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") -url = "https://github.com/opengeos/datasets/releases/download/us/arc_with_bearings.geojson" -geojson = requests.get(url).json() -bearings = geojson["features"][0]["properties"]["bearings"] -coordinates = geojson["features"][0]["geometry"]["coordinates"][:-1] -m.add_geojson(geojson, name="route") +m = leafmap.Map(center=[-120, 50], zoom=2, style="basic") +source = { + "type": "geojson", + "data": "https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson", +} +m.add_source("earthquakes", source) +layer = { + "id": "earthquakes-heat", + "type": "heatmap", + "source": "earthquakes", + "maxzoom": 9, + "paint": { + # Increase the heatmap weight based on frequency and property magnitude + "heatmap-weight": ["interpolate", ["linear"], ["get", "mag"], 0, 0, 6, 1], + # Increase the heatmap color weight weight by zoom level + # heatmap-intensity is a multiplier on top of heatmap-weight + "heatmap-intensity": ["interpolate", ["linear"], ["zoom"], 0, 1, 9, 3], + # Color ramp for heatmap. Domain is 0 (low) to 1 (high). + # Begin color ramp at 0-stop with a 0-transparency color + # to create a blur-like effect. + "heatmap-color": [ + "interpolate", + ["linear"], + ["heatmap-density"], + 0, + "rgba(33,102,172,0)", + 0.2, + "rgb(103,169,207)", + 0.4, + "rgb(209,229,240)", + 0.6, + "rgb(253,219,199)", + 0.8, + "rgb(239,138,98)", + 1, + "rgb(178,24,43)", + ], + # Adjust the heatmap radius by zoom level + "heatmap-radius": ["interpolate", ["linear"], ["zoom"], 0, 2, 9, 20], + # Transition from heatmap to circle layer by zoom level + "heatmap-opacity": ["interpolate", ["linear"], ["zoom"], 7, 1, 9, 0], + }, +} +m.add_layer(layer, before_id="waterway") +layer2 = { + "id": "earthquakes-point", + "type": "circle", + "source": "earthquakes", + "minzoom": 7, + "paint": { + # Size circle radius by earthquake magnitude and zoom level + "circle-radius": [ + "interpolate", + ["linear"], + ["zoom"], + 7, + ["interpolate", ["linear"], ["get", "mag"], 1, 1, 6, 4], + 16, + ["interpolate", ["linear"], ["get", "mag"], 1, 5, 6, 50], + ], + # Color circle by earthquake magnitude + "circle-color": [ + "interpolate", + ["linear"], + ["get", "mag"], + 1, + "rgba(33,102,172,0)", + 2, + "rgb(103,169,207)", + 3, + "rgb(209,229,240)", + 4, + "rgb(253,219,199)", + 5, + "rgb(239,138,98)", + 6, + "rgb(178,24,43)", + ], + "circle-stroke-color": "white", + "circle-stroke-width": 1, + # Transition from heatmap to circle layer by zoom level + "circle-opacity": ["interpolate", ["linear"], ["zoom"], 7, 0, 8, 1], + }, +} +m.add_layer(layer2, before_id="waterway") +m +``` -origin = [-122.414, 37.776] -destination = [-77.032, 38.913] +### Visualize population density -point = { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": {}, - "geometry": {"type": "Point", "coordinates": origin}, - } - ], +```{code-cell} ipython3 +m = leafmap.Map(center=[30.0222, -1.9596], zoom=7, style="streets") +source = { + "type": "geojson", + "data": "https://maplibre.org/maplibre-gl-js/docs/assets/rwanda-provinces.geojson", } -source = {"type": "geojson", "data": point} -m.add_source("point", source) +m.add_source("rwanda-provinces", source) layer = { - "id": "point", - "source": "point", - "type": "symbol", - "layout": { - "icon-image": "airport_15", - "icon-rotate": ["get", "bearing"], - "icon-rotation-alignment": "map", - "icon-overlap": "always", - "icon-ignore-placement": True, + "id": "rwanda-provinces", + "type": "fill", + "source": "rwanda-provinces", + "layout": {}, + "paint": { + "fill-color": [ + "let", + "density", + ["/", ["get", "population"], ["get", "sq-km"]], + [ + "interpolate", + ["linear"], + ["zoom"], + 8, + [ + "interpolate", + ["linear"], + ["var", "density"], + 274, + ["to-color", "#edf8e9"], + 1551, + ["to-color", "#006d2c"], + ], + 10, + [ + "interpolate", + ["linear"], + ["var", "density"], + 274, + ["to-color", "#eff3ff"], + 1551, + ["to-color", "#08519c"], + ], + ], + ], + "fill-opacity": 0.7, }, } m.add_layer(layer) m ``` +## Visualize raster data + +### Local raster data + +You can load local raster data using the `add_raster` method. + ```{code-cell} ipython3 -for index, coordinate in enumerate(coordinates): - point["features"][0]["geometry"]["coordinates"] = coordinate - point["features"][0]["properties"]["bearing"] = bearings[index] - m.set_data("point", point) - time.sleep(0.05) +url = "https://github.com/opengeos/datasets/releases/download/raster/landsat.tif" +filepath = "landsat.tif" +leafmap.download_file(url, filepath) ``` -#### Update a feature in realtime +```{code-cell} ipython3 +m = leafmap.Map(style="streets") +m.add_raster(filepath, indexes=[3, 2, 1], vmin=0, vmax=100, name="Landsat-321") +m.add_raster(filepath, indexes=[4, 3, 2], vmin=0, vmax=100, name="Landsat-432") +m +``` ```{code-cell} ipython3 -m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style="3d-terrain") +m.layer_interact() +``` + +```{code-cell} ipython3 +url = "https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif" +filepath = "srtm90.tif" +leafmap.download_file(url, filepath) +``` + +```{code-cell} ipython3 +m = leafmap.Map(style="satellite") +m.add_raster(filepath, colormap="terrain", name="DEM") m ``` ```{code-cell} ipython3 -import geopandas as gpd +m.layer_interact() +``` + +### Cloud Optimized GeoTIFF (COG) + +You can load Cloud Optimized GeoTIFF (COG) data using the `add_cog_layer` method. + +```{code-cell} ipython3 +m = leafmap.Map(style="satellite") +before = ( + "https://github.com/opengeos/datasets/releases/download/raster/Libya-2023-07-01.tif" +) +after = ( + "https://github.com/opengeos/datasets/releases/download/raster/Libya-2023-09-13.tif" +) +m.add_cog_layer(before, name="Before", attribution="Maxar") +m.add_cog_layer(after, name="After", attribution="Maxar", fit_bounds=True) +m ``` ```{code-cell} ipython3 -url = "https://maplibre.org/maplibre-gl-js/docs/assets/hike.geojson" -gdf = gpd.read_file(url) -coordinates = list(gdf.geometry[0].coords) -print(coordinates[:5]) +m.layer_interact() ``` +### STAC layer + +You can load SpatioTemporal Asset Catalog (STAC) data using the `add_stac_layer` method. + ```{code-cell} ipython3 -source = { - "type": "geojson", - "data": { - "type": "Feature", - "geometry": {"type": "LineString", "coordinates": [coordinates[0]]}, - }, +m = leafmap.Map(style="streets") +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" +m.add_stac_layer(url, bands=["pan"], name="Panchromatic", vmin=0, vmax=150) +m.add_stac_layer(url, bands=["B4", "B3", "B2"], name="RGB", vmin=0, vmax=150) +m +``` + +```{code-cell} ipython3 +m.layer_interact() +``` + +```{code-cell} ipython3 +collection = "landsat-8-c2-l2" +item = "LC08_L2SP_047027_20201204_02_T1" +``` + +```{code-cell} ipython3 +leafmap.stac_assets(collection=collection, item=item, titiler_endpoint="pc") +``` + +```{code-cell} ipython3 +m = leafmap.Map(style="streets") +m.add_stac_layer( + collection=collection, + item=item, + assets=["SR_B5", "SR_B4", "SR_B3"], + name="Color infrared", +) +m +``` + +## Interact with the map + ++++ + +### Display a non-interactive map + +```{code-cell} ipython3 +m = leafmap.Map( + center=[-74.5, 40], zoom=9, interactive=False, style="streets", controls={} +) +m +``` + +### Fit bounds + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.5, 40], zoom=9, style="streets") +m +``` + +Fit to Kenya. + +```{code-cell} ipython3 +bounds = [[32.958984, -5.353521], [43.50585, 5.615985]] +m.fit_bounds(bounds) +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[-77.0214, 38.897], zoom=12, style="streets") + +geojson = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "LineString", + "properties": {}, + "coordinates": [ + [-77.0366048812866, 38.89873175227713], + [-77.03364372253417, 38.89876515143842], + [-77.03364372253417, 38.89549195896866], + [-77.02982425689697, 38.89549195896866], + [-77.02400922775269, 38.89387200688839], + [-77.01519012451172, 38.891416957534204], + [-77.01521158218382, 38.892068305429156], + [-77.00813055038452, 38.892051604275686], + [-77.00832366943358, 38.89143365883688], + [-77.00818419456482, 38.89082405874451], + [-77.00815200805664, 38.88989712255097], + ], + }, + } + ], } -m.add_source("trace", source) + +m.add_source("LineString", {"type": "geojson", "data": geojson}) layer = { - "id": "trace", + "id": "LineString", "type": "line", - "source": "trace", - "paint": {"line-color": "yellow", "line-opacity": 0.75, "line-width": 5}, + "source": "LineString", + "layout": {"line-join": "round", "line-cap": "round"}, + "paint": {"line-color": "#BF93E4", "line-width": 5}, } m.add_layer(layer) -m.jump_to({"center": coordinates[0], "zoom": 14}) -m.set_pitch(30) +m ``` ```{code-cell} ipython3 -for coord in coordinates: - time.sleep(0.005) - source["data"]["geometry"]["coordinates"].append(coord) - m.set_data("trace", source["data"]) - m.pan_to(coord) +bounds = leafmap.geojson_bounds(geojson) +bounds ``` -## Visualize raster data +```{code-cell} ipython3 +m.fit_bounds(bounds) +``` -### Local raster data +### Restrict map panning to an area -You can load local raster data using the `add_raster` method. +```{code-cell} ipython3 +bounds = [ + [-74.04728500751165, 40.68392799015035], + [-73.91058699000139, 40.87764500765852], +] +``` ```{code-cell} ipython3 -url = "https://github.com/opengeos/datasets/releases/download/raster/landsat.tif" -filepath = "landsat.tif" -leafmap.download_file(url, filepath) +m = leafmap.Map(center=[-73.9978, 40.7209], zoom=13, max_bounds=bounds, style="streets") +m ``` +### Fly to + ```{code-cell} ipython3 -m = leafmap.Map(style="streets") -m.add_raster(filepath, indexes=[3, 2, 1], vmin=0, vmax=100, name="Landsat-321") -m.add_raster(filepath, indexes=[4, 3, 2], vmin=0, vmax=100, name="Landsat-432") +m = leafmap.Map(center=[-2.242467, 53.478122], zoom=9, style="streets") +m +``` + +```{code-cell} ipython3 +m.fly_to(lon=-73.983609, lat=40.754368, zoom=12) +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.5, 40], zoom=9, style="streets") +m +``` + +```{code-cell} ipython3 +options = { + "lon": 74.5, + "lat": 40, + "zoom": 9, + "bearing": 0, + "speed": 0.2, + "curve": 1, + "essential": True, +} + +m.fly_to(**options) +``` + +### Jump to a series of locations + +```{code-cell} ipython3 +import time +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[100.507, 13.745], zoom=9, style="streets") + +cities = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Point", "coordinates": [100.507, 13.745]}, + }, + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Point", "coordinates": [98.993, 18.793]}, + }, + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Point", "coordinates": [99.838, 19.924]}, + }, + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Point", "coordinates": [102.812, 17.408]}, + }, + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Point", "coordinates": [100.458, 7.001]}, + }, + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Point", "coordinates": [100.905, 12.935]}, + }, + ], +} m ``` ```{code-cell} ipython3 -m.layer_interact() +for index, city in enumerate(cities["features"]): + time.sleep(2) + coords = city["geometry"]["coordinates"] + m.jump_to({"center": coords}) ``` +### Get coordinates of the mouse pointer + ```{code-cell} ipython3 -url = "https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif" -filepath = "srtm90.tif" -leafmap.download_file(url, filepath) +import ipywidgets as widgets ``` ```{code-cell} ipython3 -m = leafmap.Map(style="satellite") -m.add_raster(filepath, colormap="terrain", name="DEM") +m = leafmap.Map(center=[-74.5, 40], zoom=9, style="streets") m ``` ```{code-cell} ipython3 -m.layer_interact() +m.clicked ``` -### Cloud Optimized GeoTIFF (COG) - -You can load Cloud Optimized GeoTIFF (COG) data using the `add_cog_layer` method. - ```{code-cell} ipython3 -m = leafmap.Map(style="satellite") -before = ( - "https://github.com/opengeos/datasets/releases/download/raster/Libya-2023-07-01.tif" -) -after = ( - "https://github.com/opengeos/datasets/releases/download/raster/Libya-2023-09-13.tif" -) -m.add_cog_layer(before, name="Before", attribution="Maxar") -m.add_cog_layer(after, name="After", attribution="Maxar", fit_bounds=True) -m -``` +output = widgets.Output() -```{code-cell} ipython3 -m.layer_interact() -``` -### STAC layer +def log_lng_lat(lng_lat): + with output: + output.clear_output() + print(lng_lat.new) -You can load SpatioTemporal Asset Catalog (STAC) data using the `add_stac_layer` method. -```{code-cell} ipython3 -m = leafmap.Map() -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" -m.add_stac_layer(url, bands=["B4", "B3", "B2"], name="SPOT", vmin=0, vmax=150, nodata=0) -m +m.observe(log_lng_lat, names="clicked") +output ``` ## Customize layer styles @@ -912,8 +1556,72 @@ m.add_layer(layer) m ``` +### Variable label placement + ```{code-cell} ipython3 +m = leafmap.Map(center=[-77.04, 38.907], zoom=11, style="streets") + +places = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {"description": "Ford's Theater", "icon": "theatre"}, + "geometry": {"type": "Point", "coordinates": [-77.038659, 38.931567]}, + }, + { + "type": "Feature", + "properties": {"description": "The Gaslight", "icon": "theatre"}, + "geometry": {"type": "Point", "coordinates": [-77.003168, 38.894651]}, + }, + { + "type": "Feature", + "properties": {"description": "Horrible Harry's", "icon": "bar"}, + "geometry": {"type": "Point", "coordinates": [-77.090372, 38.881189]}, + }, + { + "type": "Feature", + "properties": {"description": "Bike Party", "icon": "bicycle"}, + "geometry": {"type": "Point", "coordinates": [-77.052477, 38.943951]}, + }, + { + "type": "Feature", + "properties": {"description": "Rockabilly Rockstars", "icon": "music"}, + "geometry": {"type": "Point", "coordinates": [-77.031706, 38.914581]}, + }, + { + "type": "Feature", + "properties": {"description": "District Drum Tribe", "icon": "music"}, + "geometry": {"type": "Point", "coordinates": [-77.020945, 38.878241]}, + }, + { + "type": "Feature", + "properties": {"description": "Motown Memories", "icon": "music"}, + "geometry": {"type": "Point", "coordinates": [-77.007481, 38.876516]}, + }, + ], +} +source = {"type": "geojson", "data": places} +m.add_source("places", source) + +layer = { + "id": "poi-labels", + "type": "symbol", + "source": "places", + "layout": { + "text-field": ["get", "description"], + "text-variable-anchor": ["top", "bottom", "left", "right"], + "text-radial-offset": 0.5, + "text-justify": "auto", + "icon-image": ["concat", ["get", "icon"], "_15"], + }, +} +m.add_layer(layer) +m +``` +```{code-cell} ipython3 +m.rotate_to(bearing=180, options={"duration": 10000}) ``` ## PMTiles @@ -1631,6 +2339,247 @@ If you have an Earth Engine, you can uncomment the first two code blocks to add # m ``` +## Animations + +### Animate a line + +```{code-cell} ipython3 +import time +import pandas as pd +``` + +```{code-cell} ipython3 +url = "https://github.com/opengeos/datasets/releases/download/world/animated_line_data.csv" +df = pd.read_csv(url) +df_sample = df.sample(n=1000, random_state=1).sort_index() +df_sample.loc[len(df_sample)] = df.iloc[-1] +df_sample.head() +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[0, 0], zoom=0.5, style="streets") +geojson = { + "type": "FeatureCollection", + "features": [ + {"type": "Feature", "geometry": {"type": "LineString", "coordinates": [[0, 0]]}} + ], +} +source = {"type": "geojson", "data": geojson} +m.add_source("line", source) +layer = { + "id": "line-animation", + "type": "line", + "source": "line", + "layout": {"line-cap": "round", "line-join": "round"}, + "paint": {"line-color": "#ed6498", "line-width": 5, "line-opacity": 0.8}, +} +m.add_layer(layer) +m +``` + +```{code-cell} ipython3 +run_times = 2 +for i in range(run_times): + geojson["features"][0]["geometry"]["coordinates"] = [[0, 0]] + for row in df_sample.itertuples(): + time.sleep(0.005) + geojson["features"][0]["geometry"]["coordinates"].append([row.x, row.y]) + m.set_data("line", geojson) +``` + +### Animate map camera around a point + +```{code-cell} ipython3 +m = leafmap.Map(center=[-87.62712, 41.89033], zoom=15, pitch=45, style="streets") +layers = m.get_style_layers() +for layer in layers: + if layer["type"] == "symbol" and ("text-field" in layer["layout"]): + m.remove_layer(layer["id"]) +layer = { + "id": "3d-buildings", + "source": "composite", + "source-layer": "building", + "filter": ["==", "extrude", "true"], + "type": "fill-extrusion", + "min_zoom": 15, + "paint": { + "fill-extrusion-color": "#aaa", + "fill-extrusion-height": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + 0, + 15.05, + ["get", "height"], + ], + "fill-extrusion-base": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + 0, + 15.05, + ["get", "min_height"], + ], + "fill-extrusion-opacity": 0.6, + }, +} +m.add_layer(layer) +m +``` + +### Animate a point + +```{code-cell} ipython3 +for degree in range(0, 360, 1): + m.rotate_to(degree, {"duration": 0}) + time.sleep(0.1) +``` + +```{code-cell} ipython3 +import math +``` + +```{code-cell} ipython3 +def point_on_circle(angle, radius): + return { + "type": "Point", + "coordinates": [math.cos(angle) * radius, math.sin(angle) * radius], + } +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[0, 0], zoom=2, style="streets") +radius = 20 +source = {"type": "geojson", "data": point_on_circle(0, radius)} +m.add_source("point", source) +layer = { + "id": "point", + "source": "point", + "type": "circle", + "paint": {"circle-radius": 10, "circle-color": "#007cbf"}, +} +m.add_layer(layer) +m +``` + +```{code-cell} ipython3 +def animate_marker(duration, frame_rate, radius): + start_time = time.time() + while (time.time() - start_time) < duration: + timestamp = (time.time() - start_time) * 1000 # Convert to milliseconds + angle = timestamp / 1000 # Divisor controls the animation speed + point = point_on_circle(angle, radius) + m.set_data("point", point) + # Wait for the next frame + time.sleep(1 / frame_rate) +``` + +```{code-cell} ipython3 +duration = 10 # Duration of the animation in seconds +frame_rate = 30 # Frames per second +``` + +```{code-cell} ipython3 +animate_marker(duration, frame_rate, radius) +``` + +### Animate a point along a route + +```{code-cell} ipython3 +m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") +url = "https://github.com/opengeos/datasets/releases/download/us/arc_with_bearings.geojson" +geojson = requests.get(url).json() +bearings = geojson["features"][0]["properties"]["bearings"] +coordinates = geojson["features"][0]["geometry"]["coordinates"][:-1] +m.add_geojson(geojson, name="route") + +origin = [-122.414, 37.776] +destination = [-77.032, 38.913] + +point = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": {"type": "Point", "coordinates": origin}, + } + ], +} +source = {"type": "geojson", "data": point} +m.add_source("point", source) +layer = { + "id": "point", + "source": "point", + "type": "symbol", + "layout": { + "icon-image": "airport_15", + "icon-rotate": ["get", "bearing"], + "icon-rotation-alignment": "map", + "icon-overlap": "always", + "icon-ignore-placement": True, + }, +} +m.add_layer(layer) +m +``` + +```{code-cell} ipython3 +for index, coordinate in enumerate(coordinates): + point["features"][0]["geometry"]["coordinates"] = coordinate + point["features"][0]["properties"]["bearing"] = bearings[index] + m.set_data("point", point) + time.sleep(0.05) +``` + +### Update a feature in realtime + +```{code-cell} ipython3 +m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style="3d-terrain") +m +``` + +```{code-cell} ipython3 +import geopandas as gpd +``` + +```{code-cell} ipython3 +url = "https://maplibre.org/maplibre-gl-js/docs/assets/hike.geojson" +gdf = gpd.read_file(url) +coordinates = list(gdf.geometry[0].coords) +print(coordinates[:5]) +``` + +```{code-cell} ipython3 +source = { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": {"type": "LineString", "coordinates": [coordinates[0]]}, + }, +} +m.add_source("trace", source) +layer = { + "id": "trace", + "type": "line", + "source": "trace", + "paint": {"line-color": "yellow", "line-opacity": 0.75, "line-width": 5}, +} +m.add_layer(layer) +m.jump_to({"center": coordinates[0], "zoom": 14}) +m.set_pitch(30) +``` + +```{code-cell} ipython3 +for coord in coordinates: + time.sleep(0.005) + source["data"]["geometry"]["coordinates"].append(coord) + m.set_data("trace", source["data"]) + m.pan_to(coord) +``` + ## To HTML 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. From 0cda0e5ced06637c3369ac7b2b5d1bf1edf3d28c Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 26 Oct 2024 12:38:57 -0400 Subject: [PATCH 2/6] Update intro --- book/geospatial/maplibre.ipynb | 1415 ++++++++++++++++---------------- book/geospatial/maplibre.md | 741 +++++++++-------- 2 files changed, 1097 insertions(+), 1059 deletions(-) diff --git a/book/geospatial/maplibre.ipynb b/book/geospatial/maplibre.ipynb index 4ab3a2b..c82d3f7 100644 --- a/book/geospatial/maplibre.ipynb +++ b/book/geospatial/maplibre.ipynb @@ -11,11 +11,18 @@ "\n", "## Overview\n", "\n", - "The notebook demonstrates how to create 3D maps using the [MapLibre](https://github.com/eodaGmbH/py-maplibregl) Python package. The examples shown in this notebook are based on the [MapLibre documentation](https://eodagmbh.github.io/py-maplibregl/examples/vancouver_blocks/). \n", + "This lecture introduces the [MapLibre](https://github.com/eodaGmbH/py-maplibregl) Python package, a flexible open-source mapping Python package that allows users to create interactive and customizable 3D and 2D maps in Python. By leveraging the MapLibre library, GIS developers can visualize geospatial data with a variety of customization options and mapping styles. The notebook demonstrates essential concepts like creating interactive maps, customizing basemaps, adding various data layers, and implementing map controls for enhanced functionality. Additionally, advanced features, such as 3D building visualization and layer control, provide students with practical tools for real-world geospatial data analysis and visualization.\n", "\n", - "## Learning Objectives\n", + "## Learning Outcomes\n", "\n", - "TBA. \n", + "By the end of this lecture, students will be able to:\n", + "\n", + "1. Set up and install MapLibre for geospatial visualization in Python.\n", + "2. Create basic interactive maps and apply different basemap styles.\n", + "3. Customize map features, including markers, lines, polygons, and map controls.\n", + "4. Utilize advanced features such as 3D buildings and choropleth maps.\n", + "5. Integrate and manage multiple data layers, including GeoJSON, raster, and vector layers.\n", + "6. Export map visualizations as standalone HTML files for sharing and deployment.\n", "\n", "## Useful Resources\n", "\n", @@ -63,8 +70,6 @@ "source": [ "## Create interactive maps\n", "\n", - "### Create a simple map\n", - "\n", "Let's create a simple interactive map using Leafmap." ] }, @@ -172,11 +177,11 @@ "id": "15", "metadata": {}, "source": [ - "### Add map controls\n", + "## Add map controls\n", "\n", "The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`.\n", "\n", - "#### Geolocate control" + "### Geolocate control" ] }, { @@ -196,7 +201,7 @@ "id": "17", "metadata": {}, "source": [ - "#### Fullscreen control" + "### Fullscreen control" ] }, { @@ -216,7 +221,7 @@ "id": "19", "metadata": {}, "source": [ - "#### Navigation control" + "### Navigation control" ] }, { @@ -236,7 +241,7 @@ "id": "21", "metadata": {}, "source": [ - "#### Draw control \n", + "### Draw control \n", "\n", "Add the default draw control." ] @@ -376,18 +381,10 @@ "m.draw_feature_collection_all" ] }, - { - "cell_type": "markdown", - "id": "31", - "metadata": {}, - "source": [ - "### Disable scroll zoom" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -397,9 +394,11 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "32", "metadata": {}, "source": [ + "## Add layers\n", + "\n", "### Add basemaps\n", "\n", "You can add basemaps to the map using the `add_basemap` method." @@ -408,7 +407,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -420,7 +419,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -430,7 +429,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -440,7 +439,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "To add basemaps interactively, use the `add_basemap` method without specifying the `basemap` parameter." @@ -449,7 +448,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -458,7 +457,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "38", "metadata": {}, "source": [ "### Add XYZ tile layer\n", @@ -469,7 +468,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -481,7 +480,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "40", "metadata": {}, "source": [ "### Add WMS layer\n", @@ -492,7 +491,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -506,7 +505,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -521,7 +520,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -547,7 +546,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "44", "metadata": {}, "source": [ "### Add raster tile" @@ -556,7 +555,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -587,7 +586,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -597,7 +596,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "47", "metadata": {}, "source": [ "### Add a vector tile source" @@ -606,7 +605,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -616,7 +615,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -640,20 +639,17 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "## MapTiler\n", - "\n", - "## 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": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -663,7 +659,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "52", "metadata": {}, "source": [ "You can use any named style from MapTiler by setting the style parameter to the name of the style.\n", @@ -674,7 +670,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -685,7 +681,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -696,7 +692,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -707,7 +703,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -717,7 +713,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "57", "metadata": {}, "source": [ "## 3D mapping\n", @@ -730,7 +726,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "58", "metadata": {}, "outputs": [], "source": [ @@ -742,7 +738,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -754,7 +750,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -766,7 +762,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "61", "metadata": {}, "outputs": [], "source": [ @@ -778,7 +774,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "62", "metadata": {}, "outputs": [], "source": [ @@ -791,7 +787,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "63", "metadata": {}, "source": [ "### 3D buildings\n", @@ -802,7 +798,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "64", "metadata": {}, "outputs": [], "source": [ @@ -860,7 +856,7 @@ { "cell_type": "code", "execution_count": null, - "id": "66", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -875,7 +871,7 @@ }, { "cell_type": "markdown", - "id": "67", + "id": "66", "metadata": {}, "source": [ "### 3D indoor mapping\n", @@ -886,7 +882,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -898,7 +894,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "68", "metadata": {}, "outputs": [], "source": [ @@ -908,7 +904,7 @@ { "cell_type": "code", "execution_count": null, - "id": "70", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -933,7 +929,7 @@ }, { "cell_type": "markdown", - "id": "71", + "id": "70", "metadata": {}, "source": [ "### 3D choropleth map" @@ -942,7 +938,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "71", "metadata": {}, "outputs": [], "source": [ @@ -991,7 +987,7 @@ { "cell_type": "code", "execution_count": null, - "id": "73", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -1031,7 +1027,7 @@ }, { "cell_type": "markdown", - "id": "74", + "id": "73", "metadata": {}, "source": [ "## Visualize vector data\n", @@ -1044,7 +1040,7 @@ { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "74", "metadata": {}, "outputs": [], "source": [ @@ -1056,7 +1052,7 @@ { "cell_type": "code", "execution_count": null, - "id": "76", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -1068,7 +1064,7 @@ { "cell_type": "code", "execution_count": null, - "id": "77", + "id": "76", "metadata": {}, "outputs": [], "source": [ @@ -1078,7 +1074,7 @@ { "cell_type": "code", "execution_count": null, - "id": "78", + "id": "77", "metadata": {}, "outputs": [], "source": [ @@ -1091,7 +1087,7 @@ { "cell_type": "code", "execution_count": null, - "id": "79", + "id": "78", "metadata": {}, "outputs": [], "source": [ @@ -1104,7 +1100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "80", + "id": "79", "metadata": {}, "outputs": [], "source": [ @@ -1128,7 +1124,7 @@ }, { "cell_type": "markdown", - "id": "81", + "id": "80", "metadata": {}, "source": [ "### Customize marker icon image" @@ -1137,7 +1133,7 @@ { "cell_type": "code", "execution_count": null, - "id": "82", + "id": "81", "metadata": {}, "outputs": [], "source": [ @@ -1150,7 +1146,7 @@ { "cell_type": "code", "execution_count": null, - "id": "83", + "id": "82", "metadata": {}, "outputs": [], "source": [ @@ -1175,7 +1171,7 @@ { "cell_type": "code", "execution_count": null, - "id": "84", + "id": "83", "metadata": {}, "outputs": [], "source": [ @@ -1286,7 +1282,7 @@ }, { "cell_type": "markdown", - "id": "85", + "id": "84", "metadata": {}, "source": [ "### Line data" @@ -1295,7 +1291,7 @@ { "cell_type": "code", "execution_count": null, - "id": "86", + "id": "85", "metadata": {}, "outputs": [], "source": [ @@ -1349,7 +1345,7 @@ }, { "cell_type": "markdown", - "id": "87", + "id": "86", "metadata": {}, "source": [ "### Polygon data" @@ -1358,7 +1354,7 @@ { "cell_type": "code", "execution_count": null, - "id": "88", + "id": "87", "metadata": {}, "outputs": [], "source": [ @@ -1409,7 +1405,7 @@ { "cell_type": "code", "execution_count": null, - "id": "89", + "id": "88", "metadata": {}, "outputs": [], "source": [ @@ -1421,7 +1417,7 @@ }, { "cell_type": "markdown", - "id": "90", + "id": "89", "metadata": {}, "source": [ "### Multiple geometries" @@ -1430,7 +1426,7 @@ { "cell_type": "code", "execution_count": null, - "id": "91", + "id": "90", "metadata": {}, "outputs": [], "source": [ @@ -1464,7 +1460,7 @@ { "cell_type": "code", "execution_count": null, - "id": "92", + "id": "91", "metadata": {}, "outputs": [], "source": [ @@ -1473,7 +1469,7 @@ }, { "cell_type": "markdown", - "id": "93", + "id": "92", "metadata": {}, "source": [ "### Marker cluster\n", @@ -1484,7 +1480,7 @@ { "cell_type": "code", "execution_count": null, - "id": "94", + "id": "93", "metadata": {}, "outputs": [], "source": [ @@ -1546,7 +1542,7 @@ }, { "cell_type": "markdown", - "id": "95", + "id": "94", "metadata": {}, "source": [ "### Local vector data\n", @@ -1557,7 +1553,7 @@ { "cell_type": "code", "execution_count": null, - "id": "96", + "id": "95", "metadata": {}, "outputs": [], "source": [ @@ -1568,7 +1564,7 @@ { "cell_type": "code", "execution_count": null, - "id": "97", + "id": "96", "metadata": {}, "outputs": [], "source": [ @@ -1580,7 +1576,7 @@ { "cell_type": "code", "execution_count": null, - "id": "98", + "id": "97", "metadata": {}, "outputs": [], "source": [ @@ -1589,7 +1585,7 @@ }, { "cell_type": "markdown", - "id": "99", + "id": "98", "metadata": {}, "source": [ "### GeoPandas" @@ -1598,7 +1594,7 @@ { "cell_type": "code", "execution_count": null, - "id": "100", + "id": "99", "metadata": {}, "outputs": [], "source": [ @@ -1608,7 +1604,7 @@ { "cell_type": "code", "execution_count": null, - "id": "101", + "id": "100", "metadata": {}, "outputs": [], "source": [ @@ -1626,7 +1622,7 @@ }, { "cell_type": "markdown", - "id": "102", + "id": "101", "metadata": {}, "source": [ "### Change building color" @@ -1635,7 +1631,7 @@ { "cell_type": "code", "execution_count": null, - "id": "103", + "id": "102", "metadata": {}, "outputs": [], "source": [ @@ -1656,7 +1652,7 @@ { "cell_type": "code", "execution_count": null, - "id": "104", + "id": "103", "metadata": {}, "outputs": [], "source": [ @@ -1665,7 +1661,7 @@ }, { "cell_type": "markdown", - "id": "105", + "id": "104", "metadata": {}, "source": [ "### Add a new layer below labels" @@ -1674,7 +1670,7 @@ { "cell_type": "code", "execution_count": null, - "id": "106", + "id": "105", "metadata": {}, "outputs": [], "source": [ @@ -1698,7 +1694,7 @@ }, { "cell_type": "markdown", - "id": "107", + "id": "106", "metadata": {}, "source": [ "### Heat map" @@ -1707,7 +1703,7 @@ { "cell_type": "code", "execution_count": null, - "id": "108", + "id": "107", "metadata": {}, "outputs": [], "source": [ @@ -1801,7 +1797,7 @@ }, { "cell_type": "markdown", - "id": "109", + "id": "108", "metadata": {}, "source": [ "### Visualize population density" @@ -1810,7 +1806,7 @@ { "cell_type": "code", "execution_count": null, - "id": "110", + "id": "109", "metadata": {}, "outputs": [], "source": [ @@ -1865,7 +1861,7 @@ }, { "cell_type": "markdown", - "id": "111", + "id": "110", "metadata": {}, "source": [ "## Visualize raster data\n", @@ -1878,19 +1874,19 @@ { "cell_type": "code", "execution_count": null, - "id": "112", + "id": "111", "metadata": {}, "outputs": [], "source": [ "url = \"https://github.com/opengeos/datasets/releases/download/raster/landsat.tif\"\n", "filepath = \"landsat.tif\"\n", - "leafmap.download_file(url, filepath)" + "leafmap.download_file(url, filepath, quiet=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "113", + "id": "112", "metadata": {}, "outputs": [], "source": [ @@ -1903,7 +1899,7 @@ { "cell_type": "code", "execution_count": null, - "id": "114", + "id": "113", "metadata": {}, "outputs": [], "source": [ @@ -1913,19 +1909,19 @@ { "cell_type": "code", "execution_count": null, - "id": "115", + "id": "114", "metadata": {}, "outputs": [], "source": [ "url = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"\n", "filepath = \"srtm90.tif\"\n", - "leafmap.download_file(url, filepath)" + "leafmap.download_file(url, filepath, quiet=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "116", + "id": "115", "metadata": {}, "outputs": [], "source": [ @@ -1937,7 +1933,7 @@ { "cell_type": "code", "execution_count": null, - "id": "117", + "id": "116", "metadata": {}, "outputs": [], "source": [ @@ -1946,7 +1942,7 @@ }, { "cell_type": "markdown", - "id": "118", + "id": "117", "metadata": {}, "source": [ "### Cloud Optimized GeoTIFF (COG)\n", @@ -1957,7 +1953,7 @@ { "cell_type": "code", "execution_count": null, - "id": "119", + "id": "118", "metadata": {}, "outputs": [], "source": [ @@ -1976,7 +1972,7 @@ { "cell_type": "code", "execution_count": null, - "id": "120", + "id": "119", "metadata": {}, "outputs": [], "source": [ @@ -1985,7 +1981,7 @@ }, { "cell_type": "markdown", - "id": "121", + "id": "120", "metadata": {}, "source": [ "### STAC layer\n", @@ -1996,7 +1992,7 @@ { "cell_type": "code", "execution_count": null, - "id": "122", + "id": "121", "metadata": {}, "outputs": [], "source": [ @@ -2010,7 +2006,7 @@ { "cell_type": "code", "execution_count": null, - "id": "123", + "id": "122", "metadata": {}, "outputs": [], "source": [ @@ -2020,7 +2016,7 @@ { "cell_type": "code", "execution_count": null, - "id": "124", + "id": "123", "metadata": {}, "outputs": [], "source": [ @@ -2031,7 +2027,7 @@ { "cell_type": "code", "execution_count": null, - "id": "125", + "id": "124", "metadata": {}, "outputs": [], "source": [ @@ -2041,7 +2037,7 @@ { "cell_type": "code", "execution_count": null, - "id": "126", + "id": "125", "metadata": {}, "outputs": [], "source": [ @@ -2057,7 +2053,7 @@ }, { "cell_type": "markdown", - "id": "127", + "id": "126", "metadata": {}, "source": [ "## Interact with the map" @@ -2065,7 +2061,7 @@ }, { "cell_type": "markdown", - "id": "128", + "id": "127", "metadata": {}, "source": [ "### Display a non-interactive map" @@ -2074,7 +2070,7 @@ { "cell_type": "code", "execution_count": null, - "id": "129", + "id": "128", "metadata": {}, "outputs": [], "source": [ @@ -2086,8 +2082,27 @@ }, { "cell_type": "markdown", + "id": "129", + "metadata": {}, + "source": [ + "### Disable scroll zoom" + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "130", "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style=\"liberty\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "131", + "metadata": {}, "source": [ "### Fit bounds" ] @@ -2095,7 +2110,7 @@ { "cell_type": "code", "execution_count": null, - "id": "131", + "id": "132", "metadata": {}, "outputs": [], "source": [ @@ -2105,7 +2120,7 @@ }, { "cell_type": "markdown", - "id": "132", + "id": "133", "metadata": {}, "source": [ "Fit to Kenya." @@ -2114,7 +2129,7 @@ { "cell_type": "code", "execution_count": null, - "id": "133", + "id": "134", "metadata": {}, "outputs": [], "source": [ @@ -2125,7 +2140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "134", + "id": "135", "metadata": {}, "outputs": [], "source": [ @@ -2172,7 +2187,7 @@ { "cell_type": "code", "execution_count": null, - "id": "135", + "id": "136", "metadata": {}, "outputs": [], "source": [ @@ -2183,7 +2198,7 @@ { "cell_type": "code", "execution_count": null, - "id": "136", + "id": "137", "metadata": {}, "outputs": [], "source": [ @@ -2192,7 +2207,7 @@ }, { "cell_type": "markdown", - "id": "137", + "id": "138", "metadata": {}, "source": [ "### Restrict map panning to an area" @@ -2201,7 +2216,7 @@ { "cell_type": "code", "execution_count": null, - "id": "138", + "id": "139", "metadata": {}, "outputs": [], "source": [ @@ -2214,7 +2229,7 @@ { "cell_type": "code", "execution_count": null, - "id": "139", + "id": "140", "metadata": {}, "outputs": [], "source": [ @@ -2224,7 +2239,7 @@ }, { "cell_type": "markdown", - "id": "140", + "id": "141", "metadata": {}, "source": [ "### Fly to" @@ -2233,7 +2248,7 @@ { "cell_type": "code", "execution_count": null, - "id": "141", + "id": "142", "metadata": {}, "outputs": [], "source": [ @@ -2244,7 +2259,7 @@ { "cell_type": "code", "execution_count": null, - "id": "142", + "id": "143", "metadata": {}, "outputs": [], "source": [ @@ -2254,7 +2269,7 @@ { "cell_type": "code", "execution_count": null, - "id": "143", + "id": "144", "metadata": {}, "outputs": [], "source": [ @@ -2265,7 +2280,7 @@ { "cell_type": "code", "execution_count": null, - "id": "144", + "id": "145", "metadata": {}, "outputs": [], "source": [ @@ -2284,7 +2299,7 @@ }, { "cell_type": "markdown", - "id": "145", + "id": "146", "metadata": {}, "source": [ "### Jump to a series of locations" @@ -2293,7 +2308,7 @@ { "cell_type": "code", "execution_count": null, - "id": "146", + "id": "147", "metadata": {}, "outputs": [], "source": [ @@ -2303,7 +2318,7 @@ { "cell_type": "code", "execution_count": null, - "id": "147", + "id": "148", "metadata": {}, "outputs": [], "source": [ @@ -2350,7 +2365,7 @@ { "cell_type": "code", "execution_count": null, - "id": "148", + "id": "149", "metadata": {}, "outputs": [], "source": [ @@ -2362,7 +2377,7 @@ }, { "cell_type": "markdown", - "id": "149", + "id": "150", "metadata": {}, "source": [ "### Get coordinates of the mouse pointer" @@ -2371,7 +2386,7 @@ { "cell_type": "code", "execution_count": null, - "id": "150", + "id": "151", "metadata": {}, "outputs": [], "source": [ @@ -2381,7 +2396,7 @@ { "cell_type": "code", "execution_count": null, - "id": "151", + "id": "152", "metadata": {}, "outputs": [], "source": [ @@ -2392,7 +2407,7 @@ { "cell_type": "code", "execution_count": null, - "id": "152", + "id": "153", "metadata": {}, "outputs": [], "source": [ @@ -2402,7 +2417,7 @@ { "cell_type": "code", "execution_count": null, - "id": "153", + "id": "154", "metadata": {}, "outputs": [], "source": [ @@ -2421,7 +2436,7 @@ }, { "cell_type": "markdown", - "id": "154", + "id": "155", "metadata": {}, "source": [ "## Customize layer styles" @@ -2429,7 +2444,7 @@ }, { "cell_type": "markdown", - "id": "155", + "id": "156", "metadata": {}, "source": [ "### Change layer color" @@ -2438,7 +2453,7 @@ { "cell_type": "code", "execution_count": null, - "id": "156", + "id": "157", "metadata": {}, "outputs": [], "source": [ @@ -2449,7 +2464,7 @@ { "cell_type": "code", "execution_count": null, - "id": "157", + "id": "158", "metadata": {}, "outputs": [], "source": [ @@ -2458,7 +2473,7 @@ }, { "cell_type": "markdown", - "id": "158", + "id": "159", "metadata": {}, "source": [ "### Change case of labels" @@ -2467,7 +2482,7 @@ { "cell_type": "code", "execution_count": null, - "id": "159", + "id": "160", "metadata": {}, "outputs": [], "source": [ @@ -2503,7 +2518,7 @@ }, { "cell_type": "markdown", - "id": "160", + "id": "161", "metadata": {}, "source": [ "### Variable label placement" @@ -2512,7 +2527,7 @@ { "cell_type": "code", "execution_count": null, - "id": "161", + "id": "162", "metadata": {}, "outputs": [], "source": [ @@ -2580,7 +2595,7 @@ { "cell_type": "code", "execution_count": null, - "id": "162", + "id": "163", "metadata": {}, "outputs": [], "source": [ @@ -2589,27 +2604,14 @@ }, { "cell_type": "markdown", - "id": "163", + "id": "164", "metadata": {}, "source": [ - "## PMTiles\n", + "## Add custom components\n", "\n", - "Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format for fast and efficient rendering of vector tiles.\n", + "You can add custom components to the map, including images, videos, text, color bar, and legend.\n", "\n", - "### Protomaps sample data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "164", - "metadata": {}, - "outputs": [], - "source": [ - "url = \"https://open.gishub.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']}\")" + "### Add image" ] }, { @@ -2619,44 +2621,34 @@ "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", + "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", - " \"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", + "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" ] }, @@ -2667,170 +2659,143 @@ "metadata": {}, "outputs": [], "source": [ - "m.layer_interact()" + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "image = \"https://i.imgur.com/LmTETPX.png\"\n", + "m.add_image(image=image, position=\"bottom-right\")\n", + "m" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "167", "metadata": {}, + "outputs": [], "source": [ - "### Overture data\n", - "\n", - "You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps)." - ] - }, - { - "cell_type": "markdown", - "id": "168", - "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)." + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "content = ''\n", + "m.add_html(content, bg_color=\"transparent\", position=\"bottom-right\")\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "169", + "id": "168", "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']}\")" - ] + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "170", + "id": "169", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[0, 20], zoom=2, height=\"800px\")\n", - "m.add_basemap(\"Google Hybrid\", visible=False)\n", + "import numpy as np\n", "\n", - "style = {\n", - " \"version\": 8,\n", - " \"sources\": {\n", - " \"example_source\": {\n", - " \"type\": \"vector\",\n", - " \"url\": \"pmtiles://\" + url,\n", - " \"attribution\": \"PMTiles\",\n", - " }\n", + "\n", + "# Generate the icon data\n", + "width = 64 # The image will be 64 pixels square\n", + "height = 64\n", + "bytes_per_pixel = 4 # Each pixel is represented by 4 bytes: red, green, blue, and alpha\n", + "data = np.zeros((width, width, bytes_per_pixel), dtype=np.uint8)\n", + "\n", + "for x in range(width):\n", + " for y in range(width):\n", + " data[y, x, 0] = int((y / width) * 255) # red\n", + " data[y, x, 1] = int((x / width) * 255) # green\n", + " data[y, x, 2] = 128 # blue\n", + " data[y, x, 3] = 255 # alpha\n", + "\n", + "# Flatten the data array\n", + "flat_data = data.flatten()\n", + "\n", + "# Create the image dictionary\n", + "image_dict = {\n", + " \"width\": width,\n", + " \"height\": height,\n", + " \"data\": flat_data.tolist(),\n", + "}\n", + "\n", + "m = leafmap.Map(center=[0, 0], zoom=1, style=\"streets\")\n", + "m.add_image(\"gradient\", image_dict)\n", + "source = {\n", + " \"type\": \"geojson\",\n", + " \"data\": {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", + " {\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [0, 0]}}\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", + "layer = {\n", + " \"id\": \"points\",\n", + " \"type\": \"symbol\",\n", + " \"source\": \"point\",\n", + " \"layout\": {\"icon-image\": \"gradient\"},\n", + "}\n", "\n", - "m.add_pmtiles(\n", - " url,\n", - " style=style,\n", - " visible=True,\n", - " opacity=1.0,\n", - " tooltip=True,\n", - ")\n", + "m.add_source(\"point\", source)\n", + "m.add_layer(layer)\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "171", + "id": "170", "metadata": {}, "outputs": [], - "source": [ - "m.layer_interact()" - ] + "source": [] }, { "cell_type": "markdown", - "id": "172", + "id": "171", "metadata": {}, "source": [ - "### 3D PMTiles\n", - "\n", - "Visualize the global building data in 3D." + "### Add text" ] }, { "cell_type": "code", "execution_count": null, - "id": "173", + "id": "172", "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']}\")" + "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": "code", - "execution_count": null, - "id": "174", + "cell_type": "markdown", + "id": "173", "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", + "### Add GIF" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "174", + "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" ] }, @@ -2839,7 +2804,7 @@ "id": "175", "metadata": {}, "source": [ - "### 3D buildings" + "### Add HTML" ] }, { @@ -2849,12 +2814,24 @@ "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(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_3d_buildings(release=\"2024-09-18\", template=\"simple\")\n", - "m.add_layer_control()\n", + "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" ] }, @@ -2863,7 +2840,7 @@ "id": "177", "metadata": {}, "source": [ - "### 2D buildings" + "### Add colorbar" ] }, { @@ -2873,19 +2850,29 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"buildings\", opacity=0.8)\n", - "m.add_layer_control()\n", - "m" + "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "179", "metadata": {}, + "outputs": [], "source": [ - "### Transportation" + "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" ] }, { @@ -2895,98 +2882,200 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"transportation\", opacity=0.8)\n", - "m.add_layer_control()\n", + "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": "markdown", - "id": "181", - "metadata": {}, - "source": [ - "### Places" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "182", + "id": "181", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"places\", opacity=0.8)\n", - "m.add_layer_control()\n", + "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": "183", + "id": "182", "metadata": {}, "source": [ - "### Addresses" + "### Add legend" ] }, { "cell_type": "code", "execution_count": null, - "id": "184", + "id": "183", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"addresses\", opacity=0.8)\n", - "m.add_layer_control()\n", + "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": "markdown", - "id": "185", + "cell_type": "code", + "execution_count": null, + "id": "184", "metadata": {}, + "outputs": [], "source": [ - "### Base" + "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": "186", + "id": "185", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"base\", opacity=0.8)\n", - "m.add_layer_control()\n", + "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": "187", + "id": "186", "metadata": {}, "source": [ - "### Divisions" + "### 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": "188", + "id": "187", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map()\n", - "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", - "m.add_overture_data(theme=\"divisions\", opacity=0.8)\n", - "m.add_layer_control()\n", + "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": "188", + "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" ] }, @@ -2995,11 +3084,11 @@ "id": "189", "metadata": {}, "source": [ - "## Add custom components\n", + "## PMTiles\n", "\n", - "You can add custom components to the map, including images, videos, text, color bar, and legend.\n", + "Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format for fast and efficient rendering of vector tiles.\n", "\n", - "### Add image" + "### Protomaps sample data" ] }, { @@ -3009,35 +3098,10 @@ "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" + "url = \"https://open.gishub.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']}\")" ] }, { @@ -3047,9 +3111,44 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", - "image = \"https://i.imgur.com/LmTETPX.png\"\n", - "m.add_image(image=image, position=\"bottom-right\")\n", + "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" ] }, @@ -3060,75 +3159,27 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", - "content = ''\n", - "m.add_html(content, bg_color=\"transparent\", position=\"bottom-right\")\n", - "m" + "m.layer_interact()" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "193", "metadata": {}, - "outputs": [], - "source": [] + "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, + "cell_type": "markdown", "id": "194", "metadata": {}, - "outputs": [], "source": [ - "import numpy as np\n", - "\n", - "\n", - "# Generate the icon data\n", - "width = 64 # The image will be 64 pixels square\n", - "height = 64\n", - "bytes_per_pixel = 4 # Each pixel is represented by 4 bytes: red, green, blue, and alpha\n", - "data = np.zeros((width, width, bytes_per_pixel), dtype=np.uint8)\n", - "\n", - "for x in range(width):\n", - " for y in range(width):\n", - " data[y, x, 0] = int((y / width) * 255) # red\n", - " data[y, x, 1] = int((x / width) * 255) # green\n", - " data[y, x, 2] = 128 # blue\n", - " data[y, x, 3] = 255 # alpha\n", - "\n", - "# Flatten the data array\n", - "flat_data = data.flatten()\n", - "\n", - "# Create the image dictionary\n", - "image_dict = {\n", - " \"width\": width,\n", - " \"height\": height,\n", - " \"data\": flat_data.tolist(),\n", - "}\n", - "\n", - "m = leafmap.Map(center=[0, 0], zoom=1, style=\"streets\")\n", - "m.add_image(\"gradient\", image_dict)\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\": {\"icon-image\": \"gradient\"},\n", - "}\n", + "### Source Cooperative\n", "\n", - "m.add_source(\"point\", source)\n", - "m.add_layer(layer)\n", - "m" + "Let's visualize the [Google-Microsoft Open Buildings - combined by VIDA](https://beta.source.coop/repositories/vida/google-microsoft-open-buildings/description)." ] }, { @@ -3137,14 +3188,53 @@ "id": "195", "metadata": {}, "outputs": [], - "source": [] + "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": "markdown", + "cell_type": "code", + "execution_count": null, "id": "196", "metadata": {}, + "outputs": [], "source": [ - "### Add text" + "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" ] }, { @@ -3154,12 +3244,7 @@ "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" + "m.layer_interact()" ] }, { @@ -3167,7 +3252,9 @@ "id": "198", "metadata": {}, "source": [ - "### Add GIF" + "### 3D PMTiles\n", + "\n", + "Visualize the global building data in 3D." ] }, { @@ -3177,68 +3264,98 @@ "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": "200", - "metadata": {}, - "source": [ - "### Add HTML" + "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": "201", + "id": "200", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", - "html = \"\"\"\n", - "\n", - "\n", - "\n", + "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", - "🚀\n", - "

I will display 🚁

\n", - "

I will display 🚂

\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", - "\n", - "\n", - "\"\"\"\n", - "m.add_html(html, bg_color=\"transparent\")\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": "202", + "id": "201", "metadata": {}, "source": [ - "### Add colorbar" + "### 3D buildings" ] }, { "cell_type": "code", "execution_count": null, - "id": "203", + "id": "202", "metadata": {}, "outputs": [], "source": [ - "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"" + "m = leafmap.Map(\n", + " center=[-74.0095, 40.7046], zoom=16, pitch=60, bearing=-17, style=\"positron\"\n", + ")\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_3d_buildings(release=\"2024-09-18\", template=\"simple\")\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "203", + "metadata": {}, + "source": [ + "### 2D buildings" ] }, { @@ -3246,48 +3363,21 @@ "execution_count": null, "id": "204", "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", + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"buildings\", opacity=0.8)\n", + "m.add_layer_control()\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "205", "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" + "### Transportation" ] }, { @@ -3297,25 +3387,10 @@ "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 = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"transportation\", opacity=0.8)\n", + "m.add_layer_control()\n", "m" ] }, @@ -3324,7 +3399,7 @@ "id": "207", "metadata": {}, "source": [ - "### Add legend" + "### Places" ] }, { @@ -3334,34 +3409,19 @@ "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 = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"places\", opacity=0.8)\n", + "m.add_layer_control()\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "209", "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" + "### Addresses" ] }, { @@ -3371,40 +3431,10 @@ "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 = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"addresses\", opacity=0.8)\n", + "m.add_layer_control()\n", "m" ] }, @@ -3413,10 +3443,7 @@ "id": "211", "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." + "### Base" ] }, { @@ -3426,50 +3453,38 @@ "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 = leafmap.Map(center=[-74.0095, 40.7046], zoom=16)\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"base\", opacity=0.8)\n", "m.add_layer_control()\n", "m" ] }, + { + "cell_type": "markdown", + "id": "213", + "metadata": {}, + "source": [ + "### Divisions" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "213", + "id": "214", "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 = leafmap.Map()\n", + "m.add_basemap(\"Esri.WorldImagery\", visible=False)\n", + "m.add_overture_data(theme=\"divisions\", opacity=0.8)\n", "m.add_layer_control()\n", "m" ] }, { "cell_type": "markdown", - "id": "214", + "id": "215", "metadata": {}, "source": [ "## Deck.GL layers\n", @@ -3482,7 +3497,7 @@ { "cell_type": "code", "execution_count": null, - "id": "215", + "id": "216", "metadata": {}, "outputs": [], "source": [ @@ -3511,7 +3526,7 @@ }, { "cell_type": "markdown", - "id": "216", + "id": "217", "metadata": {}, "source": [ "### Multiple Deck.GL layers" @@ -3520,7 +3535,7 @@ { "cell_type": "code", "execution_count": null, - "id": "217", + "id": "218", "metadata": {}, "outputs": [], "source": [ @@ -3530,7 +3545,7 @@ { "cell_type": "code", "execution_count": null, - "id": "218", + "id": "219", "metadata": {}, "outputs": [], "source": [ @@ -3542,7 +3557,7 @@ { "cell_type": "code", "execution_count": null, - "id": "219", + "id": "220", "metadata": {}, "outputs": [], "source": [ @@ -3593,7 +3608,7 @@ }, { "cell_type": "markdown", - "id": "220", + "id": "221", "metadata": {}, "source": [ "## Google Earth Engine\n", @@ -3604,7 +3619,7 @@ { "cell_type": "code", "execution_count": null, - "id": "221", + "id": "222", "metadata": {}, "outputs": [], "source": [ @@ -3620,7 +3635,7 @@ { "cell_type": "code", "execution_count": null, - "id": "222", + "id": "223", "metadata": {}, "outputs": [], "source": [ @@ -3629,7 +3644,7 @@ }, { "cell_type": "markdown", - "id": "223", + "id": "224", "metadata": {}, "source": [ "We can also overlay other data layers on top of Earth Engine data layers." @@ -3638,7 +3653,7 @@ { "cell_type": "code", "execution_count": null, - "id": "224", + "id": "225", "metadata": {}, "outputs": [], "source": [ @@ -3653,7 +3668,7 @@ }, { "cell_type": "markdown", - "id": "225", + "id": "226", "metadata": {}, "source": [ "If you have an Earth Engine, you can uncomment the first two code blocks to add any Earth Engine datasets." @@ -3662,7 +3677,7 @@ { "cell_type": "code", "execution_count": null, - "id": "226", + "id": "227", "metadata": {}, "outputs": [], "source": [ @@ -3673,7 +3688,7 @@ { "cell_type": "code", "execution_count": null, - "id": "227", + "id": "228", "metadata": {}, "outputs": [], "source": [ @@ -3688,7 +3703,7 @@ }, { "cell_type": "markdown", - "id": "228", + "id": "229", "metadata": {}, "source": [ "## Animations\n", @@ -3699,7 +3714,7 @@ { "cell_type": "code", "execution_count": null, - "id": "229", + "id": "230", "metadata": {}, "outputs": [], "source": [ @@ -3710,7 +3725,7 @@ { "cell_type": "code", "execution_count": null, - "id": "230", + "id": "231", "metadata": {}, "outputs": [], "source": [ @@ -3724,7 +3739,7 @@ { "cell_type": "code", "execution_count": null, - "id": "231", + "id": "232", "metadata": {}, "outputs": [], "source": [ @@ -3751,7 +3766,7 @@ { "cell_type": "code", "execution_count": null, - "id": "232", + "id": "233", "metadata": {}, "outputs": [], "source": [ @@ -3766,7 +3781,7 @@ }, { "cell_type": "markdown", - "id": "233", + "id": "234", "metadata": {}, "source": [ "### Animate map camera around a point" @@ -3775,7 +3790,7 @@ { "cell_type": "code", "execution_count": null, - "id": "234", + "id": "235", "metadata": {}, "outputs": [], "source": [ @@ -3820,7 +3835,7 @@ }, { "cell_type": "markdown", - "id": "235", + "id": "236", "metadata": {}, "source": [ "### Animate a point" @@ -3829,7 +3844,7 @@ { "cell_type": "code", "execution_count": null, - "id": "236", + "id": "237", "metadata": {}, "outputs": [], "source": [ @@ -3841,7 +3856,7 @@ { "cell_type": "code", "execution_count": null, - "id": "237", + "id": "238", "metadata": {}, "outputs": [], "source": [ @@ -3851,7 +3866,7 @@ { "cell_type": "code", "execution_count": null, - "id": "238", + "id": "239", "metadata": {}, "outputs": [], "source": [ @@ -3865,7 +3880,7 @@ { "cell_type": "code", "execution_count": null, - "id": "239", + "id": "240", "metadata": {}, "outputs": [], "source": [ @@ -3886,7 +3901,7 @@ { "cell_type": "code", "execution_count": null, - "id": "240", + "id": "241", "metadata": {}, "outputs": [], "source": [ @@ -3904,7 +3919,7 @@ { "cell_type": "code", "execution_count": null, - "id": "241", + "id": "242", "metadata": {}, "outputs": [], "source": [ @@ -3915,7 +3930,7 @@ { "cell_type": "code", "execution_count": null, - "id": "242", + "id": "243", "metadata": {}, "outputs": [], "source": [ @@ -3924,7 +3939,7 @@ }, { "cell_type": "markdown", - "id": "243", + "id": "244", "metadata": {}, "source": [ "### Animate a point along a route" @@ -3933,7 +3948,7 @@ { "cell_type": "code", "execution_count": null, - "id": "244", + "id": "245", "metadata": {}, "outputs": [], "source": [ @@ -3978,7 +3993,7 @@ { "cell_type": "code", "execution_count": null, - "id": "245", + "id": "246", "metadata": {}, "outputs": [], "source": [ @@ -3991,7 +4006,7 @@ }, { "cell_type": "markdown", - "id": "246", + "id": "247", "metadata": {}, "source": [ "### Update a feature in realtime" @@ -4000,7 +4015,7 @@ { "cell_type": "code", "execution_count": null, - "id": "247", + "id": "248", "metadata": {}, "outputs": [], "source": [ @@ -4011,7 +4026,7 @@ { "cell_type": "code", "execution_count": null, - "id": "248", + "id": "249", "metadata": {}, "outputs": [], "source": [ @@ -4021,7 +4036,7 @@ { "cell_type": "code", "execution_count": null, - "id": "249", + "id": "250", "metadata": {}, "outputs": [], "source": [ @@ -4034,7 +4049,7 @@ { "cell_type": "code", "execution_count": null, - "id": "250", + "id": "251", "metadata": {}, "outputs": [], "source": [ @@ -4060,7 +4075,7 @@ { "cell_type": "code", "execution_count": null, - "id": "251", + "id": "252", "metadata": {}, "outputs": [], "source": [ @@ -4073,7 +4088,7 @@ }, { "cell_type": "markdown", - "id": "252", + "id": "253", "metadata": {}, "source": [ "## To HTML\n", @@ -4084,7 +4099,7 @@ { "cell_type": "code", "execution_count": null, - "id": "253", + "id": "254", "metadata": {}, "outputs": [], "source": [ @@ -4096,7 +4111,7 @@ { "cell_type": "code", "execution_count": null, - "id": "254", + "id": "255", "metadata": {}, "outputs": [], "source": [ @@ -4117,7 +4132,7 @@ { "cell_type": "code", "execution_count": null, - "id": "255", + "id": "256", "metadata": {}, "outputs": [], "source": [ @@ -4136,6 +4151,16 @@ ")\n", "m" ] + }, + { + "cell_type": "markdown", + "id": "257", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this lecture, we explored the functionality of the MapLibre library for creating and customizing interactive maps in Python. We covered how to build a map from scratch, add controls, and manage different basemaps. Additionally, we explored more complex visualizations, including 3D building and terrain views, layer customization, and data integration with GeoJSON and raster formats. By understanding and applying these techniques, students are now equipped to develop dynamic geospatial visualizations using MapLibre, enhancing both analytical and presentation capabilities in their GIS projects." + ] } ], "metadata": { diff --git a/book/geospatial/maplibre.md b/book/geospatial/maplibre.md index 86aec98..96ab907 100644 --- a/book/geospatial/maplibre.md +++ b/book/geospatial/maplibre.md @@ -17,11 +17,18 @@ kernelspec: ## Overview -The notebook demonstrates how to create 3D maps using the [MapLibre](https://github.com/eodaGmbH/py-maplibregl) Python package. The examples shown in this notebook are based on the [MapLibre documentation](https://eodagmbh.github.io/py-maplibregl/examples/vancouver_blocks/). +This lecture introduces the [MapLibre](https://github.com/eodaGmbH/py-maplibregl) Python package, a flexible open-source mapping Python package that allows users to create interactive and customizable 3D and 2D maps in Python. By leveraging the MapLibre library, GIS developers can visualize geospatial data with a variety of customization options and mapping styles. The notebook demonstrates essential concepts like creating interactive maps, customizing basemaps, adding various data layers, and implementing map controls for enhanced functionality. Additionally, advanced features, such as 3D building visualization and layer control, provide students with practical tools for real-world geospatial data analysis and visualization. -## Learning Objectives +## Learning Outcomes -TBA. +By the end of this lecture, students will be able to: + +1. Set up and install MapLibre for geospatial visualization in Python. +2. Create basic interactive maps and apply different basemap styles. +3. Customize map features, including markers, lines, polygons, and map controls. +4. Utilize advanced features such as 3D buildings and choropleth maps. +5. Integrate and manage multiple data layers, including GeoJSON, raster, and vector layers. +6. Export map visualizations as standalone HTML files for sharing and deployment. ## Useful Resources @@ -45,8 +52,6 @@ import leafmap.maplibregl as leafmap ## Create interactive maps -### Create a simple map - Let's create a simple interactive map using Leafmap. ```{code-cell} ipython3 @@ -88,11 +93,11 @@ m = leafmap.Map(style="liberty") m ``` -### Add map controls +## Add map controls The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`. -#### Geolocate control +### Geolocate control ```{code-cell} ipython3 m = leafmap.Map() @@ -100,7 +105,7 @@ m.add_control("geolocate", position="top-left") m ``` -#### Fullscreen control +### Fullscreen control ```{code-cell} ipython3 m = leafmap.Map(center=[11.255, 43.77], zoom=13, style="streets", controls={}) @@ -108,7 +113,7 @@ m.add_control("fullscreen", position="top-right") m ``` -#### Navigation control +### Navigation control ```{code-cell} ipython3 m = leafmap.Map(center=[11.255, 43.77], zoom=13, style="streets", controls={}) @@ -116,7 +121,7 @@ m.add_control("navigation", position="top-left") m ``` -#### Draw control +### Draw control Add the default draw control. @@ -201,13 +206,13 @@ m.draw_features_selected m.draw_feature_collection_all ``` -### Disable scroll zoom - ```{code-cell} ipython3 m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style="liberty") m ``` +## Add layers + ### Add basemaps You can add basemaps to the map using the `add_basemap` method. @@ -298,7 +303,7 @@ style = { "https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg" ], "tileSize": 256, - "attribution": 'Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors', + "attribution": "Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors", } }, "layers": [ @@ -344,9 +349,6 @@ m ``` ## MapTiler - -## Set up API Key - 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/). ```{code-cell} ipython3 @@ -1217,7 +1219,7 @@ You can load local raster data using the `add_raster` method. ```{code-cell} ipython3 url = "https://github.com/opengeos/datasets/releases/download/raster/landsat.tif" filepath = "landsat.tif" -leafmap.download_file(url, filepath) +leafmap.download_file(url, filepath, quiet=True) ``` ```{code-cell} ipython3 @@ -1234,7 +1236,7 @@ m.layer_interact() ```{code-cell} ipython3 url = "https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif" filepath = "srtm90.tif" -leafmap.download_file(url, filepath) +leafmap.download_file(url, filepath, quiet=True) ``` ```{code-cell} ipython3 @@ -1317,6 +1319,13 @@ m = leafmap.Map( m ``` +### Disable scroll zoom + +```{code-cell} ipython3 +m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style="liberty") +m +``` + ### Fit bounds ```{code-cell} ipython3 @@ -1624,391 +1633,142 @@ m m.rotate_to(bearing=180, options={"duration": 10000}) ``` -## PMTiles - -Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format for fast and efficient rendering of vector tiles. +## Add custom components -### Protomaps sample data +You can add custom components to the map, including images, videos, text, color bar, and legend. -```{code-cell} ipython3 -url = "https://open.gishub.org/data/pmtiles/protomaps_firenze.pmtiles" -metadata = leafmap.pmtiles_metadata(url) -print(f"layer names: {metadata['layer_names']}") -print(f"bounds: {metadata['bounds']}") -``` +### Add image ```{code-cell} ipython3 -m = leafmap.Map() - -style = { - "version": 8, - "sources": { - "example_source": { - "type": "vector", - "url": "pmtiles://" + url, - "attribution": "PMTiles", - } +m = leafmap.Map(center=[0.349419, -1.80921], zoom=3, style="streets") +image = "https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.png" +source = { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + {"type": "Feature", "geometry": {"type": "Point", "coordinates": [0, 0]}} + ], }, - "layers": [ - { - "id": "buildings", - "source": "example_source", - "source-layer": "landuse", - "type": "fill", - "paint": {"fill-color": "steelblue"}, - }, - { - "id": "roads", - "source": "example_source", - "source-layer": "roads", - "type": "line", - "paint": {"line-color": "black"}, - }, - ], } -# style = leafmap.pmtiles_style(url) # Use default style +layer = { + "id": "points", + "type": "symbol", + "source": "point", + "layout": { + "icon-image": "cat", + "icon-size": 0.25, + "text-field": "I love kitty!", + "text-font": ["Open Sans Regular"], + "text-offset": [0, 3], + "text-anchor": "top", + }, +} +m.add_image("cat", image) +m.add_source("point", source) +m.add_layer(layer) +m +``` -m.add_pmtiles( - url, - style=style, - visible=True, - opacity=1.0, - tooltip=True, -) +```{code-cell} ipython3 +m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") +image = "https://i.imgur.com/LmTETPX.png" +m.add_image(image=image, position="bottom-right") m ``` ```{code-cell} ipython3 -m.layer_interact() +m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") +content = '' +m.add_html(content, bg_color="transparent", position="bottom-right") +m ``` -### Overture data +```{code-cell} ipython3 -You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps). +``` -+++ +```{code-cell} ipython3 +import numpy as np -### Source Cooperative -Let's visualize the [Google-Microsoft Open Buildings - combined by VIDA](https://beta.source.coop/repositories/vida/google-microsoft-open-buildings/description). +# Generate the icon data +width = 64 # The image will be 64 pixels square +height = 64 +bytes_per_pixel = 4 # Each pixel is represented by 4 bytes: red, green, blue, and alpha +data = np.zeros((width, width, bytes_per_pixel), dtype=np.uint8) -```{code-cell} ipython3 -url = "https://data.source.coop/vida/google-microsoft-open-buildings/pmtiles/go_ms_building_footprints.pmtiles" -metadata = leafmap.pmtiles_metadata(url) -print(f"layer names: {metadata['layer_names']}") -print(f"bounds: {metadata['bounds']}") -``` +for x in range(width): + for y in range(width): + data[y, x, 0] = int((y / width) * 255) # red + data[y, x, 1] = int((x / width) * 255) # green + data[y, x, 2] = 128 # blue + data[y, x, 3] = 255 # alpha -```{code-cell} ipython3 -m = leafmap.Map(center=[0, 20], zoom=2, height="800px") -m.add_basemap("Google Hybrid", visible=False) +# Flatten the data array +flat_data = data.flatten() -style = { - "version": 8, - "sources": { - "example_source": { - "type": "vector", - "url": "pmtiles://" + url, - "attribution": "PMTiles", - } +# Create the image dictionary +image_dict = { + "width": width, + "height": height, + "data": flat_data.tolist(), +} + +m = leafmap.Map(center=[0, 0], zoom=1, style="streets") +m.add_image("gradient", image_dict) +source = { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + {"type": "Feature", "geometry": {"type": "Point", "coordinates": [0, 0]}} + ], }, - "layers": [ - { - "id": "buildings", - "source": "example_source", - "source-layer": "building_footprints", - "type": "fill", - "paint": {"fill-color": "#3388ff", "fill-opacity": 0.5}, - }, - ], } -# style = leafmap.pmtiles_style(url) # Use default style +layer = { + "id": "points", + "type": "symbol", + "source": "point", + "layout": {"icon-image": "gradient"}, +} -m.add_pmtiles( - url, - style=style, - visible=True, - opacity=1.0, - tooltip=True, -) +m.add_source("point", source) +m.add_layer(layer) m ``` ```{code-cell} ipython3 -m.layer_interact() -``` -### 3D PMTiles +``` -Visualize the global building data in 3D. +### Add text ```{code-cell} ipython3 -url = "https://data.source.coop/cholmes/overture/overture-buildings.pmtiles" -metadata = leafmap.pmtiles_metadata(url) -print(f"layer names: {metadata['layer_names']}") -print(f"bounds: {metadata['bounds']}") +m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") +text = "Hello World" +m.add_text(text, fontsize=20, position="bottom-right") +text2 = "Awesome Text!" +m.add_text(text2, fontsize=25, bg_color="rgba(255, 255, 255, 0.8)", position="top-left") +m ``` -```{code-cell} ipython3 -m = leafmap.Map( - center=[-74.0095, 40.7046], zoom=16, pitch=60, bearing=-17, style="positron" -) -m.add_basemap("OpenStreetMap.Mapnik") -m.add_basemap("Esri.WorldImagery", visible=False) - -style = { - "layers": [ - { - "id": "buildings", - "source": "example_source", - "source-layer": "buildings", - "type": "fill-extrusion", - "filter": [ - ">", - ["get", "height"], - 0, - ], # only show buildings with height info - "paint": { - "fill-extrusion-color": [ - "interpolate", - ["linear"], - ["get", "height"], - 0, - "lightgray", - 200, - "royalblue", - 400, - "lightblue", - ], - "fill-extrusion-height": ["*", ["get", "height"], 1], - }, - }, - ], -} +### Add GIF -m.add_pmtiles( - url, - style=style, - visible=True, - opacity=1.0, - tooltip=True, - template="Height: {{height}}
Country: {{country_iso}}", - fit_bounds=False, -) -m.add_layer_control() +```{code-cell} ipython3 +m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") +image = "https://i.imgur.com/KeiAsTv.gif" +m.add_image(image=image, width=250, height=250, position="bottom-right") +text = "I love sloth!🦥" +m.add_text(text, fontsize=35, padding="20px") +image2 = "https://i.imgur.com/kZC2tpr.gif" +m.add_image(image=image2, bg_color="transparent", position="bottom-left") m ``` -### 3D buildings - -```{code-cell} ipython3 -m = leafmap.Map( - center=[-74.0095, 40.7046], zoom=16, pitch=60, bearing=-17, style="positron" -) -m.add_basemap("Esri.WorldImagery", visible=False) -m.add_overture_3d_buildings(release="2024-09-18", template="simple") -m.add_layer_control() -m -``` - -### 2D buildings - -```{code-cell} ipython3 -m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) -m.add_basemap("Esri.WorldImagery", visible=False) -m.add_overture_data(theme="buildings", opacity=0.8) -m.add_layer_control() -m -``` - -### Transportation - -```{code-cell} ipython3 -m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) -m.add_basemap("Esri.WorldImagery", visible=False) -m.add_overture_data(theme="transportation", opacity=0.8) -m.add_layer_control() -m -``` - -### Places - -```{code-cell} ipython3 -m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) -m.add_basemap("Esri.WorldImagery", visible=False) -m.add_overture_data(theme="places", opacity=0.8) -m.add_layer_control() -m -``` - -### Addresses - -```{code-cell} ipython3 -m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) -m.add_basemap("Esri.WorldImagery", visible=False) -m.add_overture_data(theme="addresses", opacity=0.8) -m.add_layer_control() -m -``` - -### Base - -```{code-cell} ipython3 -m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) -m.add_basemap("Esri.WorldImagery", visible=False) -m.add_overture_data(theme="base", opacity=0.8) -m.add_layer_control() -m -``` - -### Divisions - -```{code-cell} ipython3 -m = leafmap.Map() -m.add_basemap("Esri.WorldImagery", visible=False) -m.add_overture_data(theme="divisions", opacity=0.8) -m.add_layer_control() -m -``` - -## Add custom components - -You can add custom components to the map, including images, videos, text, color bar, and legend. - -### Add image - -```{code-cell} ipython3 -m = leafmap.Map(center=[0.349419, -1.80921], zoom=3, style="streets") -image = "https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.png" -source = { - "type": "geojson", - "data": { - "type": "FeatureCollection", - "features": [ - {"type": "Feature", "geometry": {"type": "Point", "coordinates": [0, 0]}} - ], - }, -} - -layer = { - "id": "points", - "type": "symbol", - "source": "point", - "layout": { - "icon-image": "cat", - "icon-size": 0.25, - "text-field": "I love kitty!", - "text-font": ["Open Sans Regular"], - "text-offset": [0, 3], - "text-anchor": "top", - }, -} -m.add_image("cat", image) -m.add_source("point", source) -m.add_layer(layer) -m -``` - -```{code-cell} ipython3 -m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") -image = "https://i.imgur.com/LmTETPX.png" -m.add_image(image=image, position="bottom-right") -m -``` - -```{code-cell} ipython3 -m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") -content = '' -m.add_html(content, bg_color="transparent", position="bottom-right") -m -``` - -```{code-cell} ipython3 - -``` - -```{code-cell} ipython3 -import numpy as np - - -# Generate the icon data -width = 64 # The image will be 64 pixels square -height = 64 -bytes_per_pixel = 4 # Each pixel is represented by 4 bytes: red, green, blue, and alpha -data = np.zeros((width, width, bytes_per_pixel), dtype=np.uint8) - -for x in range(width): - for y in range(width): - data[y, x, 0] = int((y / width) * 255) # red - data[y, x, 1] = int((x / width) * 255) # green - data[y, x, 2] = 128 # blue - data[y, x, 3] = 255 # alpha - -# Flatten the data array -flat_data = data.flatten() - -# Create the image dictionary -image_dict = { - "width": width, - "height": height, - "data": flat_data.tolist(), -} - -m = leafmap.Map(center=[0, 0], zoom=1, style="streets") -m.add_image("gradient", image_dict) -source = { - "type": "geojson", - "data": { - "type": "FeatureCollection", - "features": [ - {"type": "Feature", "geometry": {"type": "Point", "coordinates": [0, 0]}} - ], - }, -} - -layer = { - "id": "points", - "type": "symbol", - "source": "point", - "layout": {"icon-image": "gradient"}, -} - -m.add_source("point", source) -m.add_layer(layer) -m -``` - -```{code-cell} ipython3 - -``` - -### Add text - -```{code-cell} ipython3 -m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") -text = "Hello World" -m.add_text(text, fontsize=20, position="bottom-right") -text2 = "Awesome Text!" -m.add_text(text2, fontsize=25, bg_color="rgba(255, 255, 255, 0.8)", position="top-left") -m -``` - -### Add GIF - -```{code-cell} ipython3 -m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") -image = "https://i.imgur.com/KeiAsTv.gif" -m.add_image(image=image, width=250, height=250, position="bottom-right") -text = "I love sloth!🦥" -m.add_text(text, fontsize=35, padding="20px") -image2 = "https://i.imgur.com/kZC2tpr.gif" -m.add_image(image=image2, bg_color="transparent", position="bottom-left") -m -``` - -### Add HTML +### Add HTML ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") @@ -2204,6 +1964,255 @@ m.add_layer_control() m ``` +## PMTiles + +Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format for fast and efficient rendering of vector tiles. + +### Protomaps sample data + +```{code-cell} ipython3 +url = "https://open.gishub.org/data/pmtiles/protomaps_firenze.pmtiles" +metadata = leafmap.pmtiles_metadata(url) +print(f"layer names: {metadata['layer_names']}") +print(f"bounds: {metadata['bounds']}") +``` + +```{code-cell} ipython3 +m = leafmap.Map() + +style = { + "version": 8, + "sources": { + "example_source": { + "type": "vector", + "url": "pmtiles://" + url, + "attribution": "PMTiles", + } + }, + "layers": [ + { + "id": "buildings", + "source": "example_source", + "source-layer": "landuse", + "type": "fill", + "paint": {"fill-color": "steelblue"}, + }, + { + "id": "roads", + "source": "example_source", + "source-layer": "roads", + "type": "line", + "paint": {"line-color": "black"}, + }, + ], +} + +# style = leafmap.pmtiles_style(url) # Use default style + +m.add_pmtiles( + url, + style=style, + visible=True, + opacity=1.0, + tooltip=True, +) +m +``` + +```{code-cell} ipython3 +m.layer_interact() +``` + +### Overture data + +You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps). + ++++ + +### Source Cooperative + +Let's visualize the [Google-Microsoft Open Buildings - combined by VIDA](https://beta.source.coop/repositories/vida/google-microsoft-open-buildings/description). + +```{code-cell} ipython3 +url = "https://data.source.coop/vida/google-microsoft-open-buildings/pmtiles/go_ms_building_footprints.pmtiles" +metadata = leafmap.pmtiles_metadata(url) +print(f"layer names: {metadata['layer_names']}") +print(f"bounds: {metadata['bounds']}") +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[0, 20], zoom=2, height="800px") +m.add_basemap("Google Hybrid", visible=False) + +style = { + "version": 8, + "sources": { + "example_source": { + "type": "vector", + "url": "pmtiles://" + url, + "attribution": "PMTiles", + } + }, + "layers": [ + { + "id": "buildings", + "source": "example_source", + "source-layer": "building_footprints", + "type": "fill", + "paint": {"fill-color": "#3388ff", "fill-opacity": 0.5}, + }, + ], +} + +# style = leafmap.pmtiles_style(url) # Use default style + +m.add_pmtiles( + url, + style=style, + visible=True, + opacity=1.0, + tooltip=True, +) +m +``` + +```{code-cell} ipython3 +m.layer_interact() +``` + +### 3D PMTiles + +Visualize the global building data in 3D. + +```{code-cell} ipython3 +url = "https://data.source.coop/cholmes/overture/overture-buildings.pmtiles" +metadata = leafmap.pmtiles_metadata(url) +print(f"layer names: {metadata['layer_names']}") +print(f"bounds: {metadata['bounds']}") +``` + +```{code-cell} ipython3 +m = leafmap.Map( + center=[-74.0095, 40.7046], zoom=16, pitch=60, bearing=-17, style="positron" +) +m.add_basemap("OpenStreetMap.Mapnik") +m.add_basemap("Esri.WorldImagery", visible=False) + +style = { + "layers": [ + { + "id": "buildings", + "source": "example_source", + "source-layer": "buildings", + "type": "fill-extrusion", + "filter": [ + ">", + ["get", "height"], + 0, + ], # only show buildings with height info + "paint": { + "fill-extrusion-color": [ + "interpolate", + ["linear"], + ["get", "height"], + 0, + "lightgray", + 200, + "royalblue", + 400, + "lightblue", + ], + "fill-extrusion-height": ["*", ["get", "height"], 1], + }, + }, + ], +} + +m.add_pmtiles( + url, + style=style, + visible=True, + opacity=1.0, + tooltip=True, + template="Height: {{height}}
Country: {{country_iso}}", + fit_bounds=False, +) +m.add_layer_control() +m +``` + +### 3D buildings + +```{code-cell} ipython3 +m = leafmap.Map( + center=[-74.0095, 40.7046], zoom=16, pitch=60, bearing=-17, style="positron" +) +m.add_basemap("Esri.WorldImagery", visible=False) +m.add_overture_3d_buildings(release="2024-09-18", template="simple") +m.add_layer_control() +m +``` + +### 2D buildings + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) +m.add_basemap("Esri.WorldImagery", visible=False) +m.add_overture_data(theme="buildings", opacity=0.8) +m.add_layer_control() +m +``` + +### Transportation + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) +m.add_basemap("Esri.WorldImagery", visible=False) +m.add_overture_data(theme="transportation", opacity=0.8) +m.add_layer_control() +m +``` + +### Places + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) +m.add_basemap("Esri.WorldImagery", visible=False) +m.add_overture_data(theme="places", opacity=0.8) +m.add_layer_control() +m +``` + +### Addresses + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) +m.add_basemap("Esri.WorldImagery", visible=False) +m.add_overture_data(theme="addresses", opacity=0.8) +m.add_layer_control() +m +``` + +### Base + +```{code-cell} ipython3 +m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) +m.add_basemap("Esri.WorldImagery", visible=False) +m.add_overture_data(theme="base", opacity=0.8) +m.add_layer_control() +m +``` + +### Divisions + +```{code-cell} ipython3 +m = leafmap.Map() +m.add_basemap("Esri.WorldImagery", visible=False) +m.add_overture_data(theme="divisions", opacity=0.8) +m.add_layer_control() +m +``` + ## Deck.GL layers Deck.GL layers can be added to the map using the `add_deck_layer` method. @@ -2621,3 +2630,7 @@ m.to_html( ) m ``` + +## Summary + +In this lecture, we explored the functionality of the MapLibre library for creating and customizing interactive maps in Python. We covered how to build a map from scratch, add controls, and manage different basemaps. Additionally, we explored more complex visualizations, including 3D building and terrain views, layer customization, and data integration with GeoJSON and raster formats. By understanding and applying these techniques, students are now equipped to develop dynamic geospatial visualizations using MapLibre, enhancing both analytical and presentation capabilities in their GIS projects. From e7fbc313e0130ef03cb4b918aa686402d5a07309 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 26 Oct 2024 12:51:32 -0400 Subject: [PATCH 3/6] Skip maplibre execute --- _config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_config.yml b/_config.yml index 480636b..dd1c704 100644 --- a/_config.yml +++ b/_config.yml @@ -20,6 +20,7 @@ execute: execute_notebooks: "auto" # Whether to execute notebooks at build time. Must be one of ("auto", "force", "cache", "off") cache: "" # A path to the jupyter cache that will be used to store execution artifacts. Defaults to `_build/.jupyter_cache/` exclude_patterns: [ + "book/geospatial/maplibre.ipynb", "book/geospatial/geemap.ipynb", "book/geospatial/samgeo.ipynb", "book/geospatial/hypercoast.ipynb", From b7954b638b7954983fd27c3472c865b30a09c988 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 26 Oct 2024 16:40:48 -0400 Subject: [PATCH 4/6] Update maplibre lecture --- .gitignore | 3 +- book/geospatial/maplibre.ipynb | 915 ++++++++++++++------------------- book/geospatial/maplibre.md | 358 +++++-------- 3 files changed, 502 insertions(+), 774 deletions(-) diff --git a/.gitignore b/.gitignore index a2efcd7..0d031a2 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,5 @@ pour_point* streams* watershed* histogram* -book/geospatial/cache/* \ No newline at end of file +book/geospatial/cache/* +book/geospatial/*.html \ No newline at end of file diff --git a/book/geospatial/maplibre.ipynb b/book/geospatial/maplibre.ipynb index c82d3f7..d42f647 100644 --- a/book/geospatial/maplibre.ipynb +++ b/book/geospatial/maplibre.ipynb @@ -68,7 +68,7 @@ "id": "4", "metadata": {}, "source": [ - "## Create interactive maps\n", + "## Create Interactive Maps\n", "\n", "Let's create a simple interactive map using Leafmap." ] @@ -142,23 +142,22 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "12", "metadata": {}, + "outputs": [], "source": [ - "Alternatively, you can provide a URL to a vector style." + "m = leafmap.Map(style=\"liberty\")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "13", "metadata": {}, - "outputs": [], "source": [ - "style = \"https://demotiles.maplibre.org/style.json\"\n", - "m = leafmap.Map(style=style)\n", - "m" + "Alternatively, you can provide a URL to a vector style." ] }, { @@ -168,7 +167,8 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"liberty\")\n", + "style = \"https://demotiles.maplibre.org/style.json\"\n", + "m = leafmap.Map(style=style)\n", "m" ] }, @@ -381,20 +381,9 @@ "m.draw_feature_collection_all" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "31", - "metadata": {}, - "outputs": [], - "source": [ - "m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style=\"liberty\")\n", - "m" - ] - }, { "cell_type": "markdown", - "id": "32", + "id": "31", "metadata": {}, "source": [ "## Add layers\n", @@ -407,19 +396,20 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "32", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map()\n", "m.add_basemap(\"OpenTopoMap\")\n", + "m.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -429,7 +419,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -439,7 +429,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "35", "metadata": {}, "source": [ "To add basemaps interactively, use the `add_basemap` method without specifying the `basemap` parameter." @@ -448,7 +438,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -457,7 +447,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "37", "metadata": {}, "source": [ "### Add XYZ tile layer\n", @@ -468,7 +458,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -480,7 +470,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "39", "metadata": {}, "source": [ "### Add WMS layer\n", @@ -491,62 +481,37 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "40", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style=\"streets\")\n", + "m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style=\"liberty\")\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.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "41", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100.307965, 46.98692], zoom=13, pitch=45, style=\"3d-hybrid\")\n", + "m = leafmap.Map(center=[-100.307965, 46.98692], zoom=13, pitch=45, style=\"liberty\")\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(bg_layers=True)\n", + "m.add_layer_control()\n", "m.add_legend(builtin_legend=\"NWI\", title=\"Wetland Type\")\n", "m" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "43", - "metadata": {}, - "outputs": [], - "source": [ - "m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style=\"streets\")\n", - "\n", - "source = {\n", - " \"type\": \"raster\",\n", - " \"tiles\": [\n", - " \"https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015\"\n", - " ],\n", - " \"tileSize\": 256,\n", - "}\n", - "layer = {\n", - " \"id\": \"wms-test-layer\",\n", - " \"type\": \"raster\",\n", - " \"source\": \"wms-test-source\",\n", - " \"paint\": {},\n", - "}\n", - "m.add_source(\"wms-test-source\", source)\n", - "m.add_layer(layer, before_id=\"aeroway_fill\")\n", - "m" - ] - }, { "cell_type": "markdown", - "id": "44", + "id": "42", "metadata": {}, "source": [ "### Add raster tile" @@ -555,7 +520,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -586,7 +551,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -596,124 +561,124 @@ }, { "cell_type": "markdown", - "id": "47", - "metadata": {}, - "source": [ - "### Add a vector tile source" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "48", + "id": "45", "metadata": {}, - "outputs": [], "source": [ - "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")" + "## MapTiler\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": "49", + "id": "46", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-122.447303, 37.753574], zoom=13, style=\"streets\")\n", - "source = {\n", - " \"type\": \"vector\",\n", - " \"url\": f\"https://api.maptiler.com/tiles/contours/tiles.json?key={MAPTILER_KEY}\",\n", - "}\n", - "layer = {\n", - " \"id\": \"terrain-data\",\n", - " \"type\": \"line\",\n", - " \"source\": \"contours\",\n", - " \"source-layer\": \"contour\",\n", - " \"layout\": {\"line-join\": \"round\", \"line-cap\": \"round\"},\n", - " \"paint\": {\"line-color\": \"#ff69b4\", \"line-width\": 1},\n", - "}\n", - "m.add_source(\"contours\", source)\n", - "m.add_layer(layer)\n", - "m" + "# import os\n", + "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" ] }, { "cell_type": "markdown", - "id": "50", + "id": "47", "metadata": {}, "source": [ - "## MapTiler\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/)." + "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": "51", + "id": "48", "metadata": {}, "outputs": [], "source": [ - "# import os\n", - "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" + "m = leafmap.Map(style=\"streets\")\n", + "m" ] }, { - "cell_type": "markdown", - "id": "52", + "cell_type": "code", + "execution_count": null, + "id": "49", "metadata": {}, + "outputs": [], "source": [ - "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)" + "m = leafmap.Map(style=\"satellite\")\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "50", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"streets\")\n", + "m = leafmap.Map(style=\"hybrid\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "51", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"satellite\")\n", + "m = leafmap.Map(style=\"topo\")\n", "m" ] }, + { + "cell_type": "markdown", + "id": "52", + "metadata": {}, + "source": [ + "### Add a vector tile source" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "53", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"hybrid\")\n", - "m" + "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "54", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"topo\")\n", + "m = leafmap.Map(center=[-122.447303, 37.753574], zoom=13, style=\"streets\")\n", + "source = {\n", + " \"type\": \"vector\",\n", + " \"url\": f\"https://api.maptiler.com/tiles/contours/tiles.json?key={MAPTILER_KEY}\",\n", + "}\n", + "layer = {\n", + " \"id\": \"terrain-data\",\n", + " \"type\": \"line\",\n", + " \"source\": \"contours\",\n", + " \"source-layer\": \"contour\",\n", + " \"layout\": {\"line-join\": \"round\", \"line-cap\": \"round\"},\n", + " \"paint\": {\"line-color\": \"#ff69b4\", \"line-width\": 1},\n", + "}\n", + "m.add_source(\"contours\", source)\n", + "m.add_layer(layer)\n", "m" ] }, { "cell_type": "markdown", - "id": "57", + "id": "55", "metadata": {}, "source": [ "## 3D mapping\n", @@ -726,11 +691,13 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "56", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-hybrid\")\n", + "m = leafmap.Map(\n", + " center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style=\"3d-hybrid\"\n", + ")\n", "m.add_layer_control(bg_layers=True)\n", "m" ] @@ -738,11 +705,17 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "57", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-satellite\")\n", + "m = leafmap.Map(\n", + " center=[-122.1874314, 46.2022386],\n", + " zoom=13,\n", + " pitch=60,\n", + " bearing=220,\n", + " style=\"3d-satellite\",\n", + ")\n", "m.add_layer_control(bg_layers=True)\n", "m" ] @@ -750,11 +723,19 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "58", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-topo\", exaggeration=1.5, hillshade=False)\n", + "m = leafmap.Map(\n", + " center=[-122.1874314, 46.2022386],\n", + " zoom=13,\n", + " pitch=60,\n", + " bearing=220,\n", + " style=\"3d-topo\",\n", + " exaggeration=1.5,\n", + " hillshade=False,\n", + ")\n", "m.add_layer_control(bg_layers=True)\n", "m" ] @@ -762,11 +743,17 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "59", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"3d-ocean\", exaggeration=1.5, hillshade=True)\n", + "m = leafmap.Map(\n", + " center=[-122.1874314, 46.2022386],\n", + " zoom=13,\n", + " pitch=60,\n", + " bearing=220,\n", + " style=\"3d-terrain\",\n", + ")\n", "m.add_layer_control(bg_layers=True)\n", "m" ] @@ -774,20 +761,18 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "60", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(\n", - " center=[-122.19861, 46.21168], zoom=13, pitch=60, bearing=150, style=\"3d-terrain\"\n", - ")\n", + "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": "63", + "id": "61", "metadata": {}, "source": [ "### 3D buildings\n", @@ -798,7 +783,7 @@ { "cell_type": "code", "execution_count": null, - "id": "64", + "id": "62", "metadata": {}, "outputs": [], "source": [ @@ -856,7 +841,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "63", "metadata": {}, "outputs": [], "source": [ @@ -871,7 +856,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "64", "metadata": {}, "source": [ "### 3D indoor mapping\n", @@ -882,7 +867,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -894,7 +879,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68", + "id": "66", "metadata": {}, "outputs": [], "source": [ @@ -904,7 +889,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -929,7 +914,7 @@ }, { "cell_type": "markdown", - "id": "70", + "id": "68", "metadata": {}, "source": [ "### 3D choropleth map" @@ -938,7 +923,7 @@ { "cell_type": "code", "execution_count": null, - "id": "71", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -987,7 +972,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "70", "metadata": {}, "outputs": [], "source": [ @@ -1027,7 +1012,7 @@ }, { "cell_type": "markdown", - "id": "73", + "id": "71", "metadata": {}, "source": [ "## Visualize vector data\n", @@ -1040,7 +1025,7 @@ { "cell_type": "code", "execution_count": null, - "id": "74", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -1052,42 +1037,32 @@ { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "73", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[0, 0], zoom=2, style=\"streets\")\n", - "m.add_marker(lng_lat=[0, 0], options={\"draggable\": True})\n", + "m = leafmap.Map(center=[12.550343, 55.665957], zoom=8, style=\"positron\")\n", + "m.add_marker(lng_lat=[12.550343, 55.665957], options={\"draggable\": True})\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "76", - "metadata": {}, - "outputs": [], - "source": [ - "import requests" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77", + "id": "74", "metadata": {}, "outputs": [], "source": [ "url = (\n", " \"https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson\"\n", ")\n", - "geojson = requests.get(url).json()" + "geojson = leafmap.read_geojson(url)" ] }, { "cell_type": "code", "execution_count": null, - "id": "78", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -1100,7 +1075,7 @@ { "cell_type": "code", "execution_count": null, - "id": "79", + "id": "76", "metadata": {}, "outputs": [], "source": [ @@ -1124,7 +1099,7 @@ }, { "cell_type": "markdown", - "id": "80", + "id": "77", "metadata": {}, "source": [ "### Customize marker icon image" @@ -1133,134 +1108,18 @@ { "cell_type": "code", "execution_count": null, - "id": "81", - "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": "82", - "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": "code", - "execution_count": null, - "id": "83", + "id": "78", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[0, 0], zoom=1, style=\"positron\")\n", "image = \"https://maplibre.org/maplibre-gl-js/docs/assets/osgeo-logo.png\"\n", "m.add_image(\"custom-marker\", image)\n", - "source = {\n", - " \"type\": \"geojson\",\n", - " \"data\": {\n", - " \"type\": \"FeatureCollection\",\n", - " \"features\": [\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [100.4933, 13.7551]},\n", - " \"properties\": {\"year\": \"2004\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [6.6523, 46.5535]},\n", - " \"properties\": {\"year\": \"2006\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-123.3596, 48.4268]},\n", - " \"properties\": {\"year\": \"2007\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [18.4264, -33.9224]},\n", - " \"properties\": {\"year\": \"2008\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [151.195, -33.8552]},\n", - " \"properties\": {\"year\": \"2009\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [2.1404, 41.3925]},\n", - " \"properties\": {\"year\": \"2010\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-104.8548, 39.7644]},\n", - " \"properties\": {\"year\": \"2011\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-1.1665, 52.9539]},\n", - " \"properties\": {\"year\": \"2013\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-122.6544, 45.5428]},\n", - " \"properties\": {\"year\": \"2014\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [126.974, 37.5651]},\n", - " \"properties\": {\"year\": \"2015\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [7.1112, 50.7255]},\n", - " \"properties\": {\"year\": \"2016\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-71.0314, 42.3539]},\n", - " \"properties\": {\"year\": \"2017\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [39.2794, -6.8173]},\n", - " \"properties\": {\"year\": \"2018\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [26.0961, 44.4379]},\n", - " \"properties\": {\"year\": \"2019\"},\n", - " },\n", - " {\n", - " \"type\": \"Feature\",\n", - " \"geometry\": {\"type\": \"Point\", \"coordinates\": [-114.0879, 51.0279]},\n", - " \"properties\": {\"year\": \"2020\"},\n", - " },\n", - " ],\n", - " },\n", - "}\n", + "\n", + "url = \"https://github.com/opengeos/datasets/releases/download/places/osgeo_conferences.geojson\"\n", + "geojson = leafmap.read_geojson(url)\n", + "\n", + "source = {\"type\": \"geojson\", \"data\": geojson}\n", "\n", "m.add_source(\"conferences\", source)\n", "layer = {\n", @@ -1282,7 +1141,7 @@ }, { "cell_type": "markdown", - "id": "84", + "id": "79", "metadata": {}, "source": [ "### Line data" @@ -1291,7 +1150,7 @@ { "cell_type": "code", "execution_count": null, - "id": "85", + "id": "80", "metadata": {}, "outputs": [], "source": [ @@ -1345,7 +1204,7 @@ }, { "cell_type": "markdown", - "id": "86", + "id": "81", "metadata": {}, "source": [ "### Polygon data" @@ -1354,7 +1213,7 @@ { "cell_type": "code", "execution_count": null, - "id": "87", + "id": "82", "metadata": {}, "outputs": [], "source": [ @@ -1405,7 +1264,7 @@ { "cell_type": "code", "execution_count": null, - "id": "88", + "id": "83", "metadata": {}, "outputs": [], "source": [ @@ -1415,9 +1274,40 @@ "m" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "84", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=\"hybrid\")\n", + "geojson = \"https://github.com/opengeos/datasets/releases/download/places/wa_overture_buildings.geojson\"\n", + "paint = {\"fill-color\": \"#ffff00\", \"fill-opacity\": 0.5, \"fill-outline-color\": \"#ff0000\"}\n", + "m.add_geojson(geojson, layer_type=\"fill\", paint=paint, name=\"Fill\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "85", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=\"hybrid\")\n", + "geojson = \"https://github.com/opengeos/datasets/releases/download/places/wa_overture_buildings.geojson\"\n", + "paint_line = {\"line-color\": \"#ff0000\", \"line-width\": 3}\n", + "m.add_geojson(geojson, layer_type=\"line\", paint=paint_line, name=\"Outline\")\n", + "paint_fill = {\"fill-color\": \"#ffff00\", \"fill-opacity\": 0.5}\n", + "m.add_geojson(geojson, layer_type=\"fill\", paint=paint_fill, name=\"Fill\")\n", + "m.add_layer_control()\n", + "m" + ] + }, { "cell_type": "markdown", - "id": "89", + "id": "86", "metadata": {}, "source": [ "### Multiple geometries" @@ -1426,7 +1316,7 @@ { "cell_type": "code", "execution_count": null, - "id": "90", + "id": "87", "metadata": {}, "outputs": [], "source": [ @@ -1460,7 +1350,7 @@ { "cell_type": "code", "execution_count": null, - "id": "91", + "id": "88", "metadata": {}, "outputs": [], "source": [ @@ -1469,7 +1359,7 @@ }, { "cell_type": "markdown", - "id": "92", + "id": "89", "metadata": {}, "source": [ "### Marker cluster\n", @@ -1480,7 +1370,7 @@ { "cell_type": "code", "execution_count": null, - "id": "93", + "id": "90", "metadata": {}, "outputs": [], "source": [ @@ -1542,7 +1432,7 @@ }, { "cell_type": "markdown", - "id": "94", + "id": "91", "metadata": {}, "source": [ "### Local vector data\n", @@ -1553,30 +1443,30 @@ { "cell_type": "code", "execution_count": null, - "id": "95", + "id": "92", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3)\n", - "m" + "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": "96", + "id": "93", "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)" + "m = leafmap.Map(center=[-100, 40], zoom=3)\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "97", + "id": "94", "metadata": {}, "outputs": [], "source": [ @@ -1585,7 +1475,7 @@ }, { "cell_type": "markdown", - "id": "98", + "id": "95", "metadata": {}, "source": [ "### GeoPandas" @@ -1594,23 +1484,13 @@ { "cell_type": "code", "execution_count": null, - "id": "99", - "metadata": {}, - "outputs": [], - "source": [ - "import geopandas as gpd" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "100", + "id": "96", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"streets\")\n", "url = \"https://github.com/opengeos/datasets/releases/download/us/us_states.geojson\"\n", - "gdf = gpd.read_file(url)\n", + "gdf = leafmap.geojson_to_gdf(url)\n", "paint = {\n", " \"fill-color\": \"#3388ff\",\n", " \"fill-opacity\": 0.8,\n", @@ -1622,7 +1502,7 @@ }, { "cell_type": "markdown", - "id": "101", + "id": "97", "metadata": {}, "source": [ "### Change building color" @@ -1631,7 +1511,7 @@ { "cell_type": "code", "execution_count": null, - "id": "102", + "id": "98", "metadata": {}, "outputs": [], "source": [ @@ -1646,13 +1526,14 @@ " \"fill-opacity\",\n", " [\"interpolate\", [\"exponential\", 0.5], [\"zoom\"], 15, 0, 22, 1],\n", ")\n", + "m.add_layer_control(bg_layers=True)\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "103", + "id": "99", "metadata": {}, "outputs": [], "source": [ @@ -1661,7 +1542,7 @@ }, { "cell_type": "markdown", - "id": "104", + "id": "100", "metadata": {}, "source": [ "### Add a new layer below labels" @@ -1670,7 +1551,7 @@ { "cell_type": "code", "execution_count": null, - "id": "105", + "id": "101", "metadata": {}, "outputs": [], "source": [ @@ -1694,7 +1575,7 @@ }, { "cell_type": "markdown", - "id": "106", + "id": "102", "metadata": {}, "source": [ "### Heat map" @@ -1703,7 +1584,7 @@ { "cell_type": "code", "execution_count": null, - "id": "107", + "id": "103", "metadata": {}, "outputs": [], "source": [ @@ -1797,7 +1678,7 @@ }, { "cell_type": "markdown", - "id": "108", + "id": "104", "metadata": {}, "source": [ "### Visualize population density" @@ -1806,7 +1687,7 @@ { "cell_type": "code", "execution_count": null, - "id": "109", + "id": "105", "metadata": {}, "outputs": [], "source": [ @@ -1861,7 +1742,7 @@ }, { "cell_type": "markdown", - "id": "110", + "id": "106", "metadata": {}, "source": [ "## Visualize raster data\n", @@ -1874,7 +1755,7 @@ { "cell_type": "code", "execution_count": null, - "id": "111", + "id": "107", "metadata": {}, "outputs": [], "source": [ @@ -1886,20 +1767,21 @@ { "cell_type": "code", "execution_count": null, - "id": "112", + "id": "108", "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.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "113", + "id": "109", "metadata": {}, "outputs": [], "source": [ @@ -1909,7 +1791,7 @@ { "cell_type": "code", "execution_count": null, - "id": "114", + "id": "110", "metadata": {}, "outputs": [], "source": [ @@ -1921,7 +1803,7 @@ { "cell_type": "code", "execution_count": null, - "id": "115", + "id": "111", "metadata": {}, "outputs": [], "source": [ @@ -1933,7 +1815,7 @@ { "cell_type": "code", "execution_count": null, - "id": "116", + "id": "112", "metadata": {}, "outputs": [], "source": [ @@ -1942,7 +1824,7 @@ }, { "cell_type": "markdown", - "id": "117", + "id": "113", "metadata": {}, "source": [ "### Cloud Optimized GeoTIFF (COG)\n", @@ -1953,7 +1835,7 @@ { "cell_type": "code", "execution_count": null, - "id": "118", + "id": "114", "metadata": {}, "outputs": [], "source": [ @@ -1966,13 +1848,14 @@ ")\n", "m.add_cog_layer(before, name=\"Before\", attribution=\"Maxar\")\n", "m.add_cog_layer(after, name=\"After\", attribution=\"Maxar\", fit_bounds=True)\n", + "m.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "119", + "id": "115", "metadata": {}, "outputs": [], "source": [ @@ -1981,7 +1864,7 @@ }, { "cell_type": "markdown", - "id": "120", + "id": "116", "metadata": {}, "source": [ "### STAC layer\n", @@ -1992,7 +1875,7 @@ { "cell_type": "code", "execution_count": null, - "id": "121", + "id": "117", "metadata": {}, "outputs": [], "source": [ @@ -2000,13 +1883,14 @@ "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=[\"pan\"], name=\"Panchromatic\", vmin=0, vmax=150)\n", "m.add_stac_layer(url, bands=[\"B4\", \"B3\", \"B2\"], name=\"RGB\", vmin=0, vmax=150)\n", + "m.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "122", + "id": "118", "metadata": {}, "outputs": [], "source": [ @@ -2016,7 +1900,7 @@ { "cell_type": "code", "execution_count": null, - "id": "123", + "id": "119", "metadata": {}, "outputs": [], "source": [ @@ -2027,7 +1911,7 @@ { "cell_type": "code", "execution_count": null, - "id": "124", + "id": "120", "metadata": {}, "outputs": [], "source": [ @@ -2037,11 +1921,11 @@ { "cell_type": "code", "execution_count": null, - "id": "125", + "id": "121", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"streets\")\n", + "m = leafmap.Map(style=\"satellite\")\n", "m.add_stac_layer(\n", " collection=collection,\n", " item=item,\n", @@ -2053,7 +1937,7 @@ }, { "cell_type": "markdown", - "id": "126", + "id": "122", "metadata": {}, "source": [ "## Interact with the map" @@ -2061,7 +1945,7 @@ }, { "cell_type": "markdown", - "id": "127", + "id": "123", "metadata": {}, "source": [ "### Display a non-interactive map" @@ -2070,19 +1954,19 @@ { "cell_type": "code", "execution_count": null, - "id": "128", + "id": "124", "metadata": {}, "outputs": [], "source": [ "m = leafmap.Map(\n", - " center=[-74.5, 40], zoom=9, interactive=False, style=\"streets\", controls={}\n", + " center=[-122.65, 45.52], zoom=9, interactive=False, style=\"liberty\", controls={}\n", ")\n", "m" ] }, { "cell_type": "markdown", - "id": "129", + "id": "125", "metadata": {}, "source": [ "### Disable scroll zoom" @@ -2091,7 +1975,7 @@ { "cell_type": "code", "execution_count": null, - "id": "130", + "id": "126", "metadata": {}, "outputs": [], "source": [ @@ -2101,7 +1985,7 @@ }, { "cell_type": "markdown", - "id": "131", + "id": "127", "metadata": {}, "source": [ "### Fit bounds" @@ -2110,17 +1994,17 @@ { "cell_type": "code", "execution_count": null, - "id": "132", + "id": "128", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.5, 40], zoom=9, style=\"streets\")\n", + "m = leafmap.Map(center=[-74.5, 40], zoom=9, style=\"liberty\")\n", "m" ] }, { "cell_type": "markdown", - "id": "133", + "id": "129", "metadata": {}, "source": [ "Fit to Kenya." @@ -2129,7 +2013,7 @@ { "cell_type": "code", "execution_count": null, - "id": "134", + "id": "130", "metadata": {}, "outputs": [], "source": [ @@ -2140,11 +2024,11 @@ { "cell_type": "code", "execution_count": null, - "id": "135", + "id": "131", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-77.0214, 38.897], zoom=12, style=\"streets\")\n", + "m = leafmap.Map(center=[-77.0214, 38.897], zoom=12, style=\"liberty\")\n", "\n", "geojson = {\n", " \"type\": \"FeatureCollection\",\n", @@ -2187,7 +2071,7 @@ { "cell_type": "code", "execution_count": null, - "id": "136", + "id": "132", "metadata": {}, "outputs": [], "source": [ @@ -2198,7 +2082,7 @@ { "cell_type": "code", "execution_count": null, - "id": "137", + "id": "133", "metadata": {}, "outputs": [], "source": [ @@ -2207,7 +2091,7 @@ }, { "cell_type": "markdown", - "id": "138", + "id": "134", "metadata": {}, "source": [ "### Restrict map panning to an area" @@ -2216,7 +2100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "139", + "id": "135", "metadata": {}, "outputs": [], "source": [ @@ -2229,17 +2113,17 @@ { "cell_type": "code", "execution_count": null, - "id": "140", + "id": "136", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-73.9978, 40.7209], zoom=13, max_bounds=bounds, style=\"streets\")\n", + "m = leafmap.Map(center=[-73.9978, 40.7209], zoom=13, max_bounds=bounds, style=\"liberty\")\n", "m" ] }, { "cell_type": "markdown", - "id": "141", + "id": "137", "metadata": {}, "source": [ "### Fly to" @@ -2248,18 +2132,18 @@ { "cell_type": "code", "execution_count": null, - "id": "142", + "id": "138", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-2.242467, 53.478122], zoom=9, style=\"streets\")\n", + "m = leafmap.Map(center=[-2.242467, 53.478122], zoom=9, style=\"liberty\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "143", + "id": "139", "metadata": {}, "outputs": [], "source": [ @@ -2269,18 +2153,18 @@ { "cell_type": "code", "execution_count": null, - "id": "144", + "id": "140", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-74.5, 40], zoom=9, style=\"streets\")\n", + "m = leafmap.Map(center=[-74.5, 40], zoom=9, style=\"liberty\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "145", + "id": "141", "metadata": {}, "outputs": [], "source": [ @@ -2299,7 +2183,7 @@ }, { "cell_type": "markdown", - "id": "146", + "id": "142", "metadata": {}, "source": [ "### Jump to a series of locations" @@ -2308,7 +2192,7 @@ { "cell_type": "code", "execution_count": null, - "id": "147", + "id": "143", "metadata": {}, "outputs": [], "source": [ @@ -2318,7 +2202,7 @@ { "cell_type": "code", "execution_count": null, - "id": "148", + "id": "144", "metadata": {}, "outputs": [], "source": [ @@ -2365,7 +2249,7 @@ { "cell_type": "code", "execution_count": null, - "id": "149", + "id": "145", "metadata": {}, "outputs": [], "source": [ @@ -2377,7 +2261,7 @@ }, { "cell_type": "markdown", - "id": "150", + "id": "146", "metadata": {}, "source": [ "### Get coordinates of the mouse pointer" @@ -2386,7 +2270,7 @@ { "cell_type": "code", "execution_count": null, - "id": "151", + "id": "147", "metadata": {}, "outputs": [], "source": [ @@ -2396,7 +2280,7 @@ { "cell_type": "code", "execution_count": null, - "id": "152", + "id": "148", "metadata": {}, "outputs": [], "source": [ @@ -2407,7 +2291,7 @@ { "cell_type": "code", "execution_count": null, - "id": "153", + "id": "149", "metadata": {}, "outputs": [], "source": [ @@ -2417,7 +2301,7 @@ { "cell_type": "code", "execution_count": null, - "id": "154", + "id": "150", "metadata": {}, "outputs": [], "source": [ @@ -2436,7 +2320,7 @@ }, { "cell_type": "markdown", - "id": "155", + "id": "151", "metadata": {}, "source": [ "## Customize layer styles" @@ -2444,7 +2328,7 @@ }, { "cell_type": "markdown", - "id": "156", + "id": "152", "metadata": {}, "source": [ "### Change layer color" @@ -2453,7 +2337,7 @@ { "cell_type": "code", "execution_count": null, - "id": "157", + "id": "153", "metadata": {}, "outputs": [], "source": [ @@ -2464,7 +2348,7 @@ { "cell_type": "code", "execution_count": null, - "id": "158", + "id": "154", "metadata": {}, "outputs": [], "source": [ @@ -2473,7 +2357,7 @@ }, { "cell_type": "markdown", - "id": "159", + "id": "155", "metadata": {}, "source": [ "### Change case of labels" @@ -2482,7 +2366,7 @@ { "cell_type": "code", "execution_count": null, - "id": "160", + "id": "156", "metadata": {}, "outputs": [], "source": [ @@ -2518,7 +2402,7 @@ }, { "cell_type": "markdown", - "id": "161", + "id": "157", "metadata": {}, "source": [ "### Variable label placement" @@ -2527,7 +2411,7 @@ { "cell_type": "code", "execution_count": null, - "id": "162", + "id": "158", "metadata": {}, "outputs": [], "source": [ @@ -2595,7 +2479,7 @@ { "cell_type": "code", "execution_count": null, - "id": "163", + "id": "159", "metadata": {}, "outputs": [], "source": [ @@ -2604,7 +2488,7 @@ }, { "cell_type": "markdown", - "id": "164", + "id": "160", "metadata": {}, "source": [ "## Add custom components\n", @@ -2617,7 +2501,7 @@ { "cell_type": "code", "execution_count": null, - "id": "165", + "id": "161", "metadata": {}, "outputs": [], "source": [ @@ -2655,7 +2539,7 @@ { "cell_type": "code", "execution_count": null, - "id": "166", + "id": "162", "metadata": {}, "outputs": [], "source": [ @@ -2668,7 +2552,7 @@ { "cell_type": "code", "execution_count": null, - "id": "167", + "id": "163", "metadata": {}, "outputs": [], "source": [ @@ -2681,15 +2565,7 @@ { "cell_type": "code", "execution_count": null, - "id": "168", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "169", + "id": "164", "metadata": {}, "outputs": [], "source": [ @@ -2719,7 +2595,7 @@ " \"data\": flat_data.tolist(),\n", "}\n", "\n", - "m = leafmap.Map(center=[0, 0], zoom=1, style=\"streets\")\n", + "m = leafmap.Map(center=[0, 0], zoom=1, style=\"liberty\")\n", "m.add_image(\"gradient\", image_dict)\n", "source = {\n", " \"type\": \"geojson\",\n", @@ -2743,17 +2619,9 @@ "m" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "170", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", - "id": "171", + "id": "165", "metadata": {}, "source": [ "### Add text" @@ -2762,11 +2630,11 @@ { "cell_type": "code", "execution_count": null, - "id": "172", + "id": "166", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"streets\")\n", + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"liberty\")\n", "text = \"Hello World\"\n", "m.add_text(text, fontsize=20, position=\"bottom-right\")\n", "text2 = \"Awesome Text!\"\n", @@ -2776,7 +2644,7 @@ }, { "cell_type": "markdown", - "id": "173", + "id": "167", "metadata": {}, "source": [ "### Add GIF" @@ -2785,7 +2653,7 @@ { "cell_type": "code", "execution_count": null, - "id": "174", + "id": "168", "metadata": {}, "outputs": [], "source": [ @@ -2801,7 +2669,7 @@ }, { "cell_type": "markdown", - "id": "175", + "id": "169", "metadata": {}, "source": [ "### Add HTML" @@ -2810,7 +2678,7 @@ { "cell_type": "code", "execution_count": null, - "id": "176", + "id": "170", "metadata": {}, "outputs": [], "source": [ @@ -2837,7 +2705,7 @@ }, { "cell_type": "markdown", - "id": "177", + "id": "171", "metadata": {}, "source": [ "### Add colorbar" @@ -2846,55 +2714,47 @@ { "cell_type": "code", "execution_count": null, - "id": "178", - "metadata": {}, - "outputs": [], - "source": [ - "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "179", + "id": "172", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"streets\")\n", + "m = leafmap.Map(style=\"topo\")\n", + "dem = \"https://github.com/opengeos/datasets/releases/download/raster/dem.tif\"\n", "m.add_cog_layer(\n", " dem,\n", " name=\"DEM\",\n", " colormap_name=\"terrain\",\n", - " rescale=\"0, 4000\",\n", + " rescale=\"0, 1500\",\n", " fit_bounds=True,\n", - " nodata=0,\n", + " nodata=np.nan,\n", ")\n", "m.add_colorbar(\n", - " cmap=\"terrain\", vmin=0, vmax=4000, label=\"Elevation (m)\", position=\"bottom-right\"\n", + " cmap=\"terrain\", vmin=0, vmax=1500, label=\"Elevation (m)\", position=\"bottom-right\"\n", ")\n", + "m.add_layer_control()\n", "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "180", + "id": "173", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"streets\")\n", + "m = leafmap.Map(style=\"topo\")\n", "m.add_cog_layer(\n", " dem,\n", " name=\"DEM\",\n", " colormap_name=\"terrain\",\n", - " rescale=\"0, 4000\",\n", - " nodata=0,\n", + " rescale=\"0, 1500\",\n", + " nodata=np.nan,\n", " fit_bounds=True,\n", ")\n", "m.add_colorbar(\n", " cmap=\"terrain\",\n", " vmin=0,\n", - " vmax=4000,\n", + " vmax=1500,\n", " label=\"Elevation (m)\",\n", " position=\"bottom-right\",\n", " transparent=True,\n", @@ -2905,23 +2765,23 @@ { "cell_type": "code", "execution_count": null, - "id": "181", + "id": "174", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"streets\")\n", + "m = leafmap.Map(style=\"topo\")\n", "m.add_cog_layer(\n", " dem,\n", " name=\"DEM\",\n", " colormap_name=\"terrain\",\n", - " rescale=\"0, 4000\",\n", - " nodata=0,\n", + " rescale=\"0, 1500\",\n", + " nodata=np.nan,\n", " fit_bounds=True,\n", ")\n", "m.add_colorbar(\n", " cmap=\"terrain\",\n", " vmin=0,\n", - " vmax=4000,\n", + " vmax=1500,\n", " label=\"Elevation (m)\",\n", " position=\"bottom-right\",\n", " width=0.2,\n", @@ -2933,7 +2793,7 @@ }, { "cell_type": "markdown", - "id": "182", + "id": "175", "metadata": {}, "source": [ "### Add legend" @@ -2942,7 +2802,7 @@ { "cell_type": "code", "execution_count": null, - "id": "183", + "id": "176", "metadata": {}, "outputs": [], "source": [ @@ -2963,7 +2823,7 @@ { "cell_type": "code", "execution_count": null, - "id": "184", + "id": "177", "metadata": {}, "outputs": [], "source": [ @@ -2979,7 +2839,7 @@ { "cell_type": "code", "execution_count": null, - "id": "185", + "id": "178", "metadata": {}, "outputs": [], "source": [ @@ -3022,7 +2882,7 @@ }, { "cell_type": "markdown", - "id": "186", + "id": "179", "metadata": {}, "source": [ "### Add video\n", @@ -3034,7 +2894,7 @@ { "cell_type": "code", "execution_count": null, - "id": "187", + "id": "180", "metadata": {}, "outputs": [], "source": [ @@ -3059,7 +2919,7 @@ { "cell_type": "code", "execution_count": null, - "id": "188", + "id": "181", "metadata": {}, "outputs": [], "source": [ @@ -3081,7 +2941,7 @@ }, { "cell_type": "markdown", - "id": "189", + "id": "182", "metadata": {}, "source": [ "## PMTiles\n", @@ -3094,7 +2954,7 @@ { "cell_type": "code", "execution_count": null, - "id": "190", + "id": "183", "metadata": {}, "outputs": [], "source": [ @@ -3107,7 +2967,7 @@ { "cell_type": "code", "execution_count": null, - "id": "191", + "id": "184", "metadata": {}, "outputs": [], "source": [ @@ -3155,7 +3015,7 @@ { "cell_type": "code", "execution_count": null, - "id": "192", + "id": "185", "metadata": {}, "outputs": [], "source": [ @@ -3164,7 +3024,7 @@ }, { "cell_type": "markdown", - "id": "193", + "id": "186", "metadata": {}, "source": [ "### Overture data\n", @@ -3174,7 +3034,7 @@ }, { "cell_type": "markdown", - "id": "194", + "id": "187", "metadata": {}, "source": [ "### Source Cooperative\n", @@ -3185,7 +3045,7 @@ { "cell_type": "code", "execution_count": null, - "id": "195", + "id": "188", "metadata": {}, "outputs": [], "source": [ @@ -3198,11 +3058,11 @@ { "cell_type": "code", "execution_count": null, - "id": "196", + "id": "189", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[0, 20], zoom=2, height=\"800px\")\n", + "m = leafmap.Map(center=[0, 20], zoom=2)\n", "m.add_basemap(\"Google Hybrid\", visible=False)\n", "\n", "style = {\n", @@ -3240,7 +3100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "197", + "id": "190", "metadata": {}, "outputs": [], "source": [ @@ -3249,7 +3109,7 @@ }, { "cell_type": "markdown", - "id": "198", + "id": "191", "metadata": {}, "source": [ "### 3D PMTiles\n", @@ -3260,7 +3120,7 @@ { "cell_type": "code", "execution_count": null, - "id": "199", + "id": "192", "metadata": {}, "outputs": [], "source": [ @@ -3273,7 +3133,7 @@ { "cell_type": "code", "execution_count": null, - "id": "200", + "id": "193", "metadata": {}, "outputs": [], "source": [ @@ -3328,7 +3188,7 @@ }, { "cell_type": "markdown", - "id": "201", + "id": "194", "metadata": {}, "source": [ "### 3D buildings" @@ -3337,7 +3197,7 @@ { "cell_type": "code", "execution_count": null, - "id": "202", + "id": "195", "metadata": {}, "outputs": [], "source": [ @@ -3352,7 +3212,7 @@ }, { "cell_type": "markdown", - "id": "203", + "id": "196", "metadata": {}, "source": [ "### 2D buildings" @@ -3361,7 +3221,7 @@ { "cell_type": "code", "execution_count": null, - "id": "204", + "id": "197", "metadata": {}, "outputs": [], "source": [ @@ -3374,7 +3234,7 @@ }, { "cell_type": "markdown", - "id": "205", + "id": "198", "metadata": {}, "source": [ "### Transportation" @@ -3383,7 +3243,7 @@ { "cell_type": "code", "execution_count": null, - "id": "206", + "id": "199", "metadata": {}, "outputs": [], "source": [ @@ -3396,7 +3256,7 @@ }, { "cell_type": "markdown", - "id": "207", + "id": "200", "metadata": {}, "source": [ "### Places" @@ -3405,7 +3265,7 @@ { "cell_type": "code", "execution_count": null, - "id": "208", + "id": "201", "metadata": {}, "outputs": [], "source": [ @@ -3418,7 +3278,7 @@ }, { "cell_type": "markdown", - "id": "209", + "id": "202", "metadata": {}, "source": [ "### Addresses" @@ -3427,7 +3287,7 @@ { "cell_type": "code", "execution_count": null, - "id": "210", + "id": "203", "metadata": {}, "outputs": [], "source": [ @@ -3440,7 +3300,7 @@ }, { "cell_type": "markdown", - "id": "211", + "id": "204", "metadata": {}, "source": [ "### Base" @@ -3449,7 +3309,7 @@ { "cell_type": "code", "execution_count": null, - "id": "212", + "id": "205", "metadata": {}, "outputs": [], "source": [ @@ -3462,7 +3322,7 @@ }, { "cell_type": "markdown", - "id": "213", + "id": "206", "metadata": {}, "source": [ "### Divisions" @@ -3471,7 +3331,7 @@ { "cell_type": "code", "execution_count": null, - "id": "214", + "id": "207", "metadata": {}, "outputs": [], "source": [ @@ -3484,7 +3344,7 @@ }, { "cell_type": "markdown", - "id": "215", + "id": "208", "metadata": {}, "source": [ "## Deck.GL layers\n", @@ -3497,7 +3357,7 @@ { "cell_type": "code", "execution_count": null, - "id": "216", + "id": "209", "metadata": {}, "outputs": [], "source": [ @@ -3526,7 +3386,7 @@ }, { "cell_type": "markdown", - "id": "217", + "id": "210", "metadata": {}, "source": [ "### Multiple Deck.GL layers" @@ -3535,29 +3395,18 @@ { "cell_type": "code", "execution_count": null, - "id": "218", - "metadata": {}, - "outputs": [], - "source": [ - "import requests" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "219", + "id": "211", "metadata": {}, "outputs": [], "source": [ - "data = requests.get(\n", - " \"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson\"\n", - ").json()" + "url = \"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson\"\n", + "data = leafmap.read_geojson(url)" ] }, { "cell_type": "code", "execution_count": null, - "id": "220", + "id": "212", "metadata": {}, "outputs": [], "source": [ @@ -3608,7 +3457,7 @@ }, { "cell_type": "markdown", - "id": "221", + "id": "213", "metadata": {}, "source": [ "## Google Earth Engine\n", @@ -3619,7 +3468,7 @@ { "cell_type": "code", "execution_count": null, - "id": "222", + "id": "214", "metadata": {}, "outputs": [], "source": [ @@ -3635,7 +3484,7 @@ { "cell_type": "code", "execution_count": null, - "id": "223", + "id": "215", "metadata": {}, "outputs": [], "source": [ @@ -3644,7 +3493,7 @@ }, { "cell_type": "markdown", - "id": "224", + "id": "216", "metadata": {}, "source": [ "We can also overlay other data layers on top of Earth Engine data layers." @@ -3653,7 +3502,7 @@ { "cell_type": "code", "execution_count": null, - "id": "225", + "id": "217", "metadata": {}, "outputs": [], "source": [ @@ -3668,7 +3517,7 @@ }, { "cell_type": "markdown", - "id": "226", + "id": "218", "metadata": {}, "source": [ "If you have an Earth Engine, you can uncomment the first two code blocks to add any Earth Engine datasets." @@ -3677,18 +3526,20 @@ { "cell_type": "code", "execution_count": null, - "id": "227", + "id": "219", "metadata": {}, "outputs": [], "source": [ "# import ee\n", + "\n", + "# ee.Authenticate()\n", "# ee.Initialize(project=\"YOUR-PROJECT-ID\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "228", + "id": "220", "metadata": {}, "outputs": [], "source": [ @@ -3703,7 +3554,7 @@ }, { "cell_type": "markdown", - "id": "229", + "id": "221", "metadata": {}, "source": [ "## Animations\n", @@ -3714,7 +3565,7 @@ { "cell_type": "code", "execution_count": null, - "id": "230", + "id": "222", "metadata": {}, "outputs": [], "source": [ @@ -3725,7 +3576,7 @@ { "cell_type": "code", "execution_count": null, - "id": "231", + "id": "223", "metadata": {}, "outputs": [], "source": [ @@ -3739,11 +3590,11 @@ { "cell_type": "code", "execution_count": null, - "id": "232", + "id": "224", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[0, 0], zoom=0.5, style=\"streets\")\n", + "m = leafmap.Map(center=[0, 0], zoom=0.5, style=\"liberty\")\n", "geojson = {\n", " \"type\": \"FeatureCollection\",\n", " \"features\": [\n", @@ -3766,7 +3617,7 @@ { "cell_type": "code", "execution_count": null, - "id": "233", + "id": "225", "metadata": {}, "outputs": [], "source": [ @@ -3781,7 +3632,7 @@ }, { "cell_type": "markdown", - "id": "234", + "id": "226", "metadata": {}, "source": [ "### Animate map camera around a point" @@ -3790,7 +3641,7 @@ { "cell_type": "code", "execution_count": null, - "id": "235", + "id": "227", "metadata": {}, "outputs": [], "source": [ @@ -3835,7 +3686,7 @@ }, { "cell_type": "markdown", - "id": "236", + "id": "228", "metadata": {}, "source": [ "### Animate a point" @@ -3844,11 +3695,11 @@ { "cell_type": "code", "execution_count": null, - "id": "237", + "id": "229", "metadata": {}, "outputs": [], "source": [ - "for degree in range(0, 360, 1):\n", + "for degree in range(0, 180, 1):\n", " m.rotate_to(degree, {\"duration\": 0})\n", " time.sleep(0.1)" ] @@ -3856,7 +3707,7 @@ { "cell_type": "code", "execution_count": null, - "id": "238", + "id": "230", "metadata": {}, "outputs": [], "source": [ @@ -3866,7 +3717,7 @@ { "cell_type": "code", "execution_count": null, - "id": "239", + "id": "231", "metadata": {}, "outputs": [], "source": [ @@ -3880,11 +3731,11 @@ { "cell_type": "code", "execution_count": null, - "id": "240", + "id": "232", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[0, 0], zoom=2, style=\"streets\")\n", + "m = leafmap.Map(center=[0, 0], zoom=2, style=\"liberty\")\n", "radius = 20\n", "source = {\"type\": \"geojson\", \"data\": point_on_circle(0, radius)}\n", "m.add_source(\"point\", source)\n", @@ -3901,7 +3752,7 @@ { "cell_type": "code", "execution_count": null, - "id": "241", + "id": "233", "metadata": {}, "outputs": [], "source": [ @@ -3919,7 +3770,7 @@ { "cell_type": "code", "execution_count": null, - "id": "242", + "id": "234", "metadata": {}, "outputs": [], "source": [ @@ -3930,7 +3781,7 @@ { "cell_type": "code", "execution_count": null, - "id": "243", + "id": "235", "metadata": {}, "outputs": [], "source": [ @@ -3939,7 +3790,7 @@ }, { "cell_type": "markdown", - "id": "244", + "id": "236", "metadata": {}, "source": [ "### Animate a point along a route" @@ -3948,13 +3799,13 @@ { "cell_type": "code", "execution_count": null, - "id": "245", + "id": "237", "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", + "geojson = leafmap.read_geojson(url)\n", "bearings = geojson[\"features\"][0][\"properties\"][\"bearings\"]\n", "coordinates = geojson[\"features\"][0][\"geometry\"][\"coordinates\"][:-1]\n", "m.add_geojson(geojson, name=\"route\")\n", @@ -3993,7 +3844,7 @@ { "cell_type": "code", "execution_count": null, - "id": "246", + "id": "238", "metadata": {}, "outputs": [], "source": [ @@ -4006,7 +3857,7 @@ }, { "cell_type": "markdown", - "id": "247", + "id": "239", "metadata": {}, "source": [ "### Update a feature in realtime" @@ -4015,28 +3866,28 @@ { "cell_type": "code", "execution_count": null, - "id": "248", + "id": "240", "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style=\"3d-terrain\")\n", - "m" + "import geopandas as gpd" ] }, { "cell_type": "code", "execution_count": null, - "id": "249", + "id": "241", "metadata": {}, "outputs": [], "source": [ - "import geopandas as gpd" + "m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style=\"3d-terrain\")\n", + "m" ] }, { "cell_type": "code", "execution_count": null, - "id": "250", + "id": "242", "metadata": {}, "outputs": [], "source": [ @@ -4049,7 +3900,7 @@ { "cell_type": "code", "execution_count": null, - "id": "251", + "id": "243", "metadata": {}, "outputs": [], "source": [ @@ -4075,7 +3926,7 @@ { "cell_type": "code", "execution_count": null, - "id": "252", + "id": "244", "metadata": {}, "outputs": [], "source": [ @@ -4088,7 +3939,7 @@ }, { "cell_type": "markdown", - "id": "253", + "id": "245", "metadata": {}, "source": [ "## To HTML\n", @@ -4099,7 +3950,7 @@ { "cell_type": "code", "execution_count": null, - "id": "254", + "id": "246", "metadata": {}, "outputs": [], "source": [ @@ -4111,7 +3962,7 @@ { "cell_type": "code", "execution_count": null, - "id": "255", + "id": "247", "metadata": {}, "outputs": [], "source": [ @@ -4132,7 +3983,7 @@ { "cell_type": "code", "execution_count": null, - "id": "256", + "id": "248", "metadata": {}, "outputs": [], "source": [ @@ -4154,7 +4005,7 @@ }, { "cell_type": "markdown", - "id": "257", + "id": "249", "metadata": {}, "source": [ "## Summary\n", diff --git a/book/geospatial/maplibre.md b/book/geospatial/maplibre.md index 96ab907..77f0a63 100644 --- a/book/geospatial/maplibre.md +++ b/book/geospatial/maplibre.md @@ -50,7 +50,7 @@ Import the maplibre mapping backend. import leafmap.maplibregl as leafmap ``` -## Create interactive maps +## Create Interactive Maps Let's create a simple interactive map using Leafmap. @@ -80,16 +80,16 @@ m = leafmap.Map(style="background-lightgray") m ``` -Alternatively, you can provide a URL to a vector style. - ```{code-cell} ipython3 -style = "https://demotiles.maplibre.org/style.json" -m = leafmap.Map(style=style) +m = leafmap.Map(style="liberty") m ``` +Alternatively, you can provide a URL to a vector style. + ```{code-cell} ipython3 -m = leafmap.Map(style="liberty") +style = "https://demotiles.maplibre.org/style.json" +m = leafmap.Map(style=style) m ``` @@ -206,11 +206,6 @@ m.draw_features_selected m.draw_feature_collection_all ``` -```{code-cell} ipython3 -m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style="liberty") -m -``` - ## Add layers ### Add basemaps @@ -220,6 +215,7 @@ You can add basemaps to the map using the `add_basemap` method. ```{code-cell} ipython3 m = leafmap.Map() m.add_basemap("OpenTopoMap") +m.add_layer_control() m ``` @@ -254,43 +250,24 @@ m You can add WMS layers to the map using the `add_wms_layer` method. ```{code-cell} ipython3 -m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style="streets") +m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style="liberty") url = "https://img.nj.gov/imagerywms/Natural2015" layers = "Natural2015" m.add_wms_layer(url, layers=layers, before_id="aeroway_fill") +m.add_layer_control() m ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[-100.307965, 46.98692], zoom=13, pitch=45, style="3d-hybrid") +m = leafmap.Map(center=[-100.307965, 46.98692], zoom=13, pitch=45, style="liberty") +m.add_basemap("Esri.WorldImagery") url = "https://fwspublicservices.wim.usgs.gov/wetlandsmapservice/services/Wetlands/MapServer/WMSServer" m.add_wms_layer(url, layers="1", name="NWI", opacity=0.6) -m.add_layer_control(bg_layers=True) +m.add_layer_control() m.add_legend(builtin_legend="NWI", title="Wetland Type") m ``` -```{code-cell} ipython3 -m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style="streets") - -source = { - "type": "raster", - "tiles": [ - "https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015" - ], - "tileSize": 256, -} -layer = { - "id": "wms-test-layer", - "type": "raster", - "source": "wms-test-source", - "paint": {}, -} -m.add_source("wms-test-source", source) -m.add_layer(layer, before_id="aeroway_fill") -m -``` - ### Add raster tile ```{code-cell} ipython3 @@ -323,31 +300,6 @@ m = leafmap.Map(center=[-74.5, 40], zoom=2, style=style) m ``` -### Add a vector tile source - -```{code-cell} ipython3 -MAPTILER_KEY = leafmap.get_api_key("MAPTILER_KEY") -``` - -```{code-cell} ipython3 -m = leafmap.Map(center=[-122.447303, 37.753574], zoom=13, style="streets") -source = { - "type": "vector", - "url": f"https://api.maptiler.com/tiles/contours/tiles.json?key={MAPTILER_KEY}", -} -layer = { - "id": "terrain-data", - "type": "line", - "source": "contours", - "source-layer": "contour", - "layout": {"line-join": "round", "line-cap": "round"}, - "paint": {"line-color": "#ff69b4", "line-width": 1}, -} -m.add_source("contours", source) -m.add_layer(layer) -m -``` - ## MapTiler 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/). @@ -380,6 +332,31 @@ m = leafmap.Map(style="topo") m ``` +### Add a vector tile source + +```{code-cell} ipython3 +MAPTILER_KEY = leafmap.get_api_key("MAPTILER_KEY") +``` + +```{code-cell} ipython3 +m = leafmap.Map(center=[-122.447303, 37.753574], zoom=13, style="streets") +source = { + "type": "vector", + "url": f"https://api.maptiler.com/tiles/contours/tiles.json?key={MAPTILER_KEY}", +} +layer = { + "id": "terrain-data", + "type": "line", + "source": "contours", + "source-layer": "contour", + "layout": {"line-join": "round", "line-cap": "round"}, + "paint": {"line-color": "#ff69b4", "line-width": 1}, +} +m.add_source("contours", source) +m.add_layer(layer) +m +``` + ## 3D mapping ### 3D terrain @@ -387,33 +364,33 @@ m 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. ```{code-cell} ipython3 -m = leafmap.Map(style="3d-hybrid") +m = leafmap.Map(center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-hybrid") m.add_layer_control(bg_layers=True) m ``` ```{code-cell} ipython3 -m = leafmap.Map(style="3d-satellite") +m = leafmap.Map(center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-satellite") m.add_layer_control(bg_layers=True) m ``` ```{code-cell} ipython3 -m = leafmap.Map(style="3d-topo", exaggeration=1.5, hillshade=False) +m = leafmap.Map(center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-topo", exaggeration=1.5, hillshade=False) m.add_layer_control(bg_layers=True) m ``` ```{code-cell} ipython3 -m = leafmap.Map(style="3d-ocean", exaggeration=1.5, hillshade=True) +m = leafmap.Map( + center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-terrain" +) m.add_layer_control(bg_layers=True) m ``` ```{code-cell} ipython3 -m = leafmap.Map( - center=[-122.19861, 46.21168], zoom=13, pitch=60, bearing=150, style="3d-terrain" -) +m = leafmap.Map(style="3d-ocean", exaggeration=1.5, hillshade=True) m.add_layer_control(bg_layers=True) m ``` @@ -611,20 +588,14 @@ m ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[0, 0], zoom=2, style="streets") -m.add_marker(lng_lat=[0, 0], options={"draggable": True}) +m = leafmap.Map(center=[12.550343, 55.665957], zoom=8, style="positron") +m.add_marker(lng_lat=[12.550343, 55.665957], options={"draggable": True}) m ``` ```{code-cell} ipython3 -import requests -``` - -```{code-cell} ipython3 -url = ( - "https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson" -) -geojson = requests.get(url).json() +url = "https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson" +geojson = leafmap.read_geojson(url) ``` ```{code-cell} ipython3 @@ -655,118 +626,17 @@ m ### Customize marker icon image -```{code-cell} ipython3 -url = ( - "https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson" -) -geojson = requests.get(url).json() -``` - -```{code-cell} ipython3 -m = leafmap.Map(style="streets") -source = {"type": "geojson", "data": geojson} - -layer = { - "id": "cities", - "type": "symbol", - "source": "point", - "layout": { - "icon-image": "marker_15", - "icon-size": 1, - }, -} -m.add_source("point", source) -m.add_layer(layer) -m.add_popup("cities") -m -``` - ```{code-cell} ipython3 m = leafmap.Map(center=[0, 0], zoom=1, style="positron") image = "https://maplibre.org/maplibre-gl-js/docs/assets/osgeo-logo.png" m.add_image("custom-marker", image) + +url = "https://github.com/opengeos/datasets/releases/download/places/osgeo_conferences.geojson" +geojson = leafmap.read_geojson(url) + source = { "type": "geojson", - "data": { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [100.4933, 13.7551]}, - "properties": {"year": "2004"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [6.6523, 46.5535]}, - "properties": {"year": "2006"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-123.3596, 48.4268]}, - "properties": {"year": "2007"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [18.4264, -33.9224]}, - "properties": {"year": "2008"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [151.195, -33.8552]}, - "properties": {"year": "2009"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [2.1404, 41.3925]}, - "properties": {"year": "2010"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-104.8548, 39.7644]}, - "properties": {"year": "2011"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-1.1665, 52.9539]}, - "properties": {"year": "2013"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-122.6544, 45.5428]}, - "properties": {"year": "2014"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [126.974, 37.5651]}, - "properties": {"year": "2015"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [7.1112, 50.7255]}, - "properties": {"year": "2016"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-71.0314, 42.3539]}, - "properties": {"year": "2017"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [39.2794, -6.8173]}, - "properties": {"year": "2018"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [26.0961, 44.4379]}, - "properties": {"year": "2019"}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-114.0879, 51.0279]}, - "properties": {"year": "2020"}, - }, - ], - }, + "data": geojson } m.add_source("conferences", source) @@ -892,6 +762,25 @@ m.add_geojson(geojson, layer_type="fill", paint=paint) m ``` +```{code-cell} ipython3 +m = leafmap.Map(style="hybrid") +geojson = "https://github.com/opengeos/datasets/releases/download/places/wa_overture_buildings.geojson" +paint = {"fill-color": "#ffff00", "fill-opacity": 0.5, "fill-outline-color": "#ff0000"} +m.add_geojson(geojson, layer_type="fill", paint=paint, name="Fill") +m +``` + +```{code-cell} ipython3 +m = leafmap.Map(style="hybrid") +geojson = "https://github.com/opengeos/datasets/releases/download/places/wa_overture_buildings.geojson" +paint_line = {"line-color": "#ff0000", "line-width": 3} +m.add_geojson(geojson, layer_type="line", paint=paint_line, name="Outline") +paint_fill = {"fill-color": "#ffff00", "fill-opacity": 0.5} +m.add_geojson(geojson, layer_type="fill", paint=paint_fill, name="Fill") +m.add_layer_control() +m +``` + ### Multiple geometries ```{code-cell} ipython3 @@ -991,11 +880,6 @@ m You can load local vector data interactively using the `open_geojson` method. -```{code-cell} ipython3 -m = leafmap.Map(center=[-100, 40], zoom=3) -m -``` - ```{code-cell} ipython3 url = "https://github.com/opengeos/datasets/releases/download/us/us_states.geojson" filepath = "data/us_states.geojson" @@ -1003,19 +887,20 @@ leafmap.download_file(url, filepath, quiet=True) ``` ```{code-cell} ipython3 -m.open_geojson() +m = leafmap.Map(center=[-100, 40], zoom=3) +m ``` -### GeoPandas - ```{code-cell} ipython3 -import geopandas as gpd +m.open_geojson() ``` +### GeoPandas + ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") url = "https://github.com/opengeos/datasets/releases/download/us/us_states.geojson" -gdf = gpd.read_file(url) +gdf = leafmap.geojson_to_gdf(url) paint = { "fill-color": "#3388ff", "fill-opacity": 0.8, @@ -1039,6 +924,7 @@ m.set_paint_property( "fill-opacity", ["interpolate", ["exponential", 0.5], ["zoom"], 15, 0, 22, 1], ) +m.add_layer_control(bg_layers=True) m ``` @@ -1226,6 +1112,7 @@ leafmap.download_file(url, filepath, quiet=True) m = leafmap.Map(style="streets") m.add_raster(filepath, indexes=[3, 2, 1], vmin=0, vmax=100, name="Landsat-321") m.add_raster(filepath, indexes=[4, 3, 2], vmin=0, vmax=100, name="Landsat-432") +m.add_layer_control() m ``` @@ -1263,6 +1150,7 @@ after = ( ) m.add_cog_layer(before, name="Before", attribution="Maxar") m.add_cog_layer(after, name="After", attribution="Maxar", fit_bounds=True) +m.add_layer_control() m ``` @@ -1279,6 +1167,7 @@ m = leafmap.Map(style="streets") 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" m.add_stac_layer(url, bands=["pan"], name="Panchromatic", vmin=0, vmax=150) m.add_stac_layer(url, bands=["B4", "B3", "B2"], name="RGB", vmin=0, vmax=150) +m.add_layer_control() m ``` @@ -1296,7 +1185,7 @@ leafmap.stac_assets(collection=collection, item=item, titiler_endpoint="pc") ``` ```{code-cell} ipython3 -m = leafmap.Map(style="streets") +m = leafmap.Map(style="satellite") m.add_stac_layer( collection=collection, item=item, @@ -1314,7 +1203,7 @@ m ```{code-cell} ipython3 m = leafmap.Map( - center=[-74.5, 40], zoom=9, interactive=False, style="streets", controls={} + center=[-122.65, 45.52], zoom=9, interactive=False, style="liberty", controls={} ) m ``` @@ -1329,7 +1218,7 @@ m ### Fit bounds ```{code-cell} ipython3 -m = leafmap.Map(center=[-74.5, 40], zoom=9, style="streets") +m = leafmap.Map(center=[-74.5, 40], zoom=9, style="liberty") m ``` @@ -1341,7 +1230,7 @@ m.fit_bounds(bounds) ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[-77.0214, 38.897], zoom=12, style="streets") +m = leafmap.Map(center=[-77.0214, 38.897], zoom=12, style="liberty") geojson = { "type": "FeatureCollection", @@ -1400,14 +1289,14 @@ bounds = [ ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[-73.9978, 40.7209], zoom=13, max_bounds=bounds, style="streets") +m = leafmap.Map(center=[-73.9978, 40.7209], zoom=13, max_bounds=bounds, style="liberty") m ``` ### Fly to ```{code-cell} ipython3 -m = leafmap.Map(center=[-2.242467, 53.478122], zoom=9, style="streets") +m = leafmap.Map(center=[-2.242467, 53.478122], zoom=9, style="liberty") m ``` @@ -1416,7 +1305,7 @@ m.fly_to(lon=-73.983609, lat=40.754368, zoom=12) ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[-74.5, 40], zoom=9, style="streets") +m = leafmap.Map(center=[-74.5, 40], zoom=9, style="liberty") m ``` @@ -1685,10 +1574,6 @@ m.add_html(content, bg_color="transparent", position="bottom-right") m ``` -```{code-cell} ipython3 - -``` - ```{code-cell} ipython3 import numpy as np @@ -1716,7 +1601,7 @@ image_dict = { "data": flat_data.tolist(), } -m = leafmap.Map(center=[0, 0], zoom=1, style="streets") +m = leafmap.Map(center=[0, 0], zoom=1, style="liberty") m.add_image("gradient", image_dict) source = { "type": "geojson", @@ -1740,14 +1625,10 @@ m.add_layer(layer) m ``` -```{code-cell} ipython3 - -``` - ### Add text ```{code-cell} ipython3 -m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") +m = leafmap.Map(center=[-100, 40], zoom=3, style="liberty") text = "Hello World" m.add_text(text, fontsize=20, position="bottom-right") text2 = "Awesome Text!" @@ -1795,39 +1676,37 @@ m ### Add colorbar ```{code-cell} ipython3 -dem = "https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif" -``` - -```{code-cell} ipython3 -m = leafmap.Map(style="streets") +m = leafmap.Map(style="topo") +dem = "https://github.com/opengeos/datasets/releases/download/raster/dem.tif" m.add_cog_layer( dem, name="DEM", colormap_name="terrain", - rescale="0, 4000", + rescale="0, 1500", fit_bounds=True, - nodata=0, + nodata=np.nan, ) m.add_colorbar( - cmap="terrain", vmin=0, vmax=4000, label="Elevation (m)", position="bottom-right" + cmap="terrain", vmin=0, vmax=1500, label="Elevation (m)", position="bottom-right" ) +m.add_layer_control() m ``` ```{code-cell} ipython3 -m = leafmap.Map(style="streets") +m = leafmap.Map(style="topo") m.add_cog_layer( dem, name="DEM", colormap_name="terrain", - rescale="0, 4000", - nodata=0, + rescale="0, 1500", + nodata=np.nan, fit_bounds=True, ) m.add_colorbar( cmap="terrain", vmin=0, - vmax=4000, + vmax=1500, label="Elevation (m)", position="bottom-right", transparent=True, @@ -1836,19 +1715,19 @@ m ``` ```{code-cell} ipython3 -m = leafmap.Map(style="streets") +m = leafmap.Map(style="topo") m.add_cog_layer( dem, name="DEM", colormap_name="terrain", - rescale="0, 4000", - nodata=0, + rescale="0, 1500", + nodata=np.nan, fit_bounds=True, ) m.add_colorbar( cmap="terrain", vmin=0, - vmax=4000, + vmax=1500, label="Elevation (m)", position="bottom-right", width=0.2, @@ -2041,7 +1920,7 @@ print(f"bounds: {metadata['bounds']}") ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[0, 20], zoom=2, height="800px") +m = leafmap.Map(center=[0, 20], zoom=2) m.add_basemap("Google Hybrid", visible=False) style = { @@ -2246,13 +2125,8 @@ m ### Multiple Deck.GL layers ```{code-cell} ipython3 -import requests -``` - -```{code-cell} ipython3 -data = requests.get( - "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson" -).json() +url = "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson" +data = leafmap.read_geojson(url) ``` ```{code-cell} ipython3 @@ -2335,6 +2209,8 @@ If you have an Earth Engine, you can uncomment the first two code blocks to add ```{code-cell} ipython3 # import ee + +# ee.Authenticate() # ee.Initialize(project="YOUR-PROJECT-ID") ``` @@ -2366,7 +2242,7 @@ df_sample.head() ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[0, 0], zoom=0.5, style="streets") +m = leafmap.Map(center=[0, 0], zoom=0.5, style="liberty") geojson = { "type": "FeatureCollection", "features": [ @@ -2441,7 +2317,7 @@ m ### Animate a point ```{code-cell} ipython3 -for degree in range(0, 360, 1): +for degree in range(0, 180, 1): m.rotate_to(degree, {"duration": 0}) time.sleep(0.1) ``` @@ -2459,7 +2335,7 @@ def point_on_circle(angle, radius): ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[0, 0], zoom=2, style="streets") +m = leafmap.Map(center=[0, 0], zoom=2, style="liberty") radius = 20 source = {"type": "geojson", "data": point_on_circle(0, radius)} m.add_source("point", source) @@ -2499,7 +2375,7 @@ animate_marker(duration, frame_rate, radius) ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") url = "https://github.com/opengeos/datasets/releases/download/us/arc_with_bearings.geojson" -geojson = requests.get(url).json() +geojson = leafmap.read_geojson(url) bearings = geojson["features"][0]["properties"]["bearings"] coordinates = geojson["features"][0]["geometry"]["coordinates"][:-1] m.add_geojson(geojson, name="route") @@ -2546,12 +2422,12 @@ for index, coordinate in enumerate(coordinates): ### Update a feature in realtime ```{code-cell} ipython3 -m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style="3d-terrain") -m +import geopandas as gpd ``` ```{code-cell} ipython3 -import geopandas as gpd +m = leafmap.Map(center=[-122.019807, 45.632433], zoom=14, pitch=60, style="3d-terrain") +m ``` ```{code-cell} ipython3 From 438baa7036134259c0fb05d071298342368337a3 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 26 Oct 2024 16:50:34 -0400 Subject: [PATCH 5/6] Update leafmap version --- .github/workflows/build.yml | 1 + .github/workflows/deploy.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 366de92..ed362e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,7 @@ jobs: - name: Install dependencies run: | uv sync --python ${{ matrix.python-version }} + uv pip install -U leafmap uv pip install py3dep - name: Install optional dependencies diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 73fccde..4fa49c0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -30,6 +30,7 @@ jobs: - name: Install dependencies run: | uv sync --python ${{ matrix.python-version }} + uv pip install -U leafmap uv pip install py3dep - name: Install optional dependencies From 977b68fc988f7b7f4997e2b71f4a2f890897956f Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 26 Oct 2024 17:57:38 -0400 Subject: [PATCH 6/6] Add description --- book/geospatial/maplibre.ipynb | 576 ++++++++++++++++++++++----------- book/geospatial/maplibre.md | 437 ++++++++++++++++++------- 2 files changed, 697 insertions(+), 316 deletions(-) diff --git a/book/geospatial/maplibre.ipynb b/book/geospatial/maplibre.ipynb index d42f647..4f366db 100644 --- a/book/geospatial/maplibre.ipynb +++ b/book/geospatial/maplibre.ipynb @@ -30,9 +30,9 @@ "- Videos: https://bit.ly/maplibre\n", "- Demos: https://maps.gishub.org\n", "\n", - "## Installation\n", + "## Installation and Setup\n", "\n", - "Uncomment the following line to install the required Python packages." + "To install the required packages, uncomment and run the line below." ] }, { @@ -50,7 +50,7 @@ "id": "2", "metadata": {}, "source": [ - "Import the maplibre mapping backend." + "Once installed, import the `maplibregl` backend from the `leafmap` package:" ] }, { @@ -68,9 +68,11 @@ "id": "4", "metadata": {}, "source": [ - "## Create Interactive Maps\n", + "## Creating Interactive Maps\n", "\n", - "Let's create a simple interactive map using Leafmap." + "### Basic Map Setup\n", + "\n", + "Let’s start by creating a simple interactive map with default settings. This basic setup provides a blank canvas on which you can add data layers, controls, and other customizations." ] }, { @@ -89,7 +91,9 @@ "id": "6", "metadata": {}, "source": [ - "You can customize the map by specifying map center [lon, lat], zoom level, pitch, and bearing." + "### Customizing the Map's Center and Zoom Level\n", + "\n", + "You can specify the map’s center (latitude and longitude), zoom level, pitch, and bearing for a more focused view. These parameters help direct the user's attention to specific areas." ] }, { @@ -108,7 +112,9 @@ "id": "8", "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`, `liberty`, `demotiles`." + "### Choosing a Basemap Style\n", + "\n", + "MapLibre supports several pre-defined basemap styles such as `dark-matter`, `positron`, `voyager`, `liberty`, `demotiles`. You can also use custom basemap URLs for unique styling." ] }, { @@ -123,22 +129,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "10", "metadata": {}, + "outputs": [], "source": [ - "To create a map with a background color, use `style=\"background-\"`, such as `background-lightgray` and `background-green`." + "m = leafmap.Map(style=\"liberty\")\n", + "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "11", "metadata": {}, - "outputs": [], "source": [ - "m = leafmap.Map(style=\"background-lightgray\")\n", - "m" + "### Adding Background Colors\n", + "\n", + "Background color styles, such as `background-lightgray` and `background-green`, are helpful for simple or thematic maps where color-coding is needed." ] }, { @@ -148,7 +156,7 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(style=\"liberty\")\n", + "m = leafmap.Map(style=\"background-lightgray\")\n", "m" ] }, @@ -177,11 +185,21 @@ "id": "15", "metadata": {}, "source": [ - "## Add map controls\n", + "## Adding Map Controls\n", + "\n", + "Map controls enhance the usability of the map by allowing users to interact in various ways, adding elements like scale bars, zoom tools, and drawing options.\n", + "\n", + "### Available Controls\n", + "\n", + "- **Scale**: Adds a scale bar to indicate the map’s scale.\n", + "- **Fullscreen**: Expands the map to a full-screen view for better focus.\n", + "- **Geolocate**: Centers the map based on the user’s current location, if available.\n", + "- **Navigation**: Provides zoom controls and a compass for reorientation.\n", + "- **Draw**: Allows users to draw and edit shapes on the map.\n", "\n", - "The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`.\n", + "### Adding Geolocate Control\n", "\n", - "### Geolocate control" + "The Geolocate control centers the map based on the user’s current location, a helpful feature for location-based applications." ] }, { @@ -201,7 +219,9 @@ "id": "17", "metadata": {}, "source": [ - "### Fullscreen control" + "### Adding Fullscreen Control\n", + "\n", + "Fullscreen control enables users to expand the map to full screen, enhancing focus and visual clarity. This is especially useful when viewing complex or large datasets." ] }, { @@ -221,7 +241,9 @@ "id": "19", "metadata": {}, "source": [ - "### Navigation control" + "### Adding Navigation Control\n", + "\n", + "The Navigation control provides buttons for zooming and reorienting the map, improving the user's ability to navigate efficiently." ] }, { @@ -241,9 +263,9 @@ "id": "21", "metadata": {}, "source": [ - "### Draw control \n", + "### Adding Draw Control\n", "\n", - "Add the default draw control." + "The Draw control enables users to interact with the map by adding shapes such as points, lines, and polygons. This control is essential for tasks requiring spatial data input directly on the map." ] }, { @@ -263,7 +285,7 @@ "id": "23", "metadata": {}, "source": [ - "Only activate a give set of control." + "You can configure the Draw control to activate specific tools, like polygon, line, or point drawing, while also enabling or disabling a \"trash\" button to remove unwanted shapes." ] }, { @@ -297,7 +319,7 @@ "id": "26", "metadata": {}, "source": [ - "Load a GeoJSON FeatureCollection to the draw control." + "Additionally, you can load a GeoJSON FeatureCollection into the Draw control, which will allow users to view, edit, or interact with pre-defined geographical features, such as boundaries or points of interest." ] }, { @@ -358,7 +380,10 @@ "id": "28", "metadata": {}, "source": [ - "Retrieve the draw features." + "Two key methods for accessing drawn features:\n", + "\n", + "- **Selected Features**: Accesses only the currently selected features.\n", + "- **All Features**: Accesses all features added, regardless of selection, giving you full control over the spatial data on the map." ] }, { @@ -386,11 +411,13 @@ "id": "31", "metadata": {}, "source": [ - "## Add layers\n", + "## Add Layers\n", + "\n", + "Adding layers to a map enhances the data it presents, allowing different types of basemaps, tile layers, and thematic overlays to be combined for in-depth analysis.\n", "\n", - "### Add basemaps\n", + "### Adding Basemaps\n", "\n", - "You can add basemaps to the map using the `add_basemap` method." + "Basemaps provide a geographical context for the map. Using the `add_basemap` method, you can select from various basemaps, including `OpenTopoMap` and `Esri.WorldImagery`. Adding a layer control allows users to switch between multiple basemaps interactively." ] }, { @@ -432,7 +459,7 @@ "id": "35", "metadata": {}, "source": [ - "To add basemaps interactively, use the `add_basemap` method without specifying the `basemap` parameter." + "You can also add basemaps interactively, which provides flexibility for selecting the best background for your map content." ] }, { @@ -450,9 +477,9 @@ "id": "37", "metadata": {}, "source": [ - "### Add XYZ tile layer\n", + "### Adding XYZ Tile Layer\n", "\n", - "You can add XYZ tile layers to the map using the `add_tile_layer` method." + "XYZ tile layers allow integration of specific tile services like topographic, satellite, or other thematic imagery from XYZ tile servers. By specifying the URL and parameters such as `opacity` and `visibility`, XYZ layers can be customized and styled to fit the needs of your map." ] }, { @@ -473,9 +500,9 @@ "id": "39", "metadata": {}, "source": [ - "### Add WMS layer\n", + "### Adding WMS Layer\n", "\n", - "You can add WMS layers to the map using the `add_wms_layer` method." + "Web Map Service (WMS) layers provide access to external datasets served by web servers, such as thematic maps or detailed satellite imagery. Adding a WMS layer involves specifying the WMS URL and layer names, which allows for overlaying various data types such as natural imagery or land cover classifications." ] }, { @@ -514,7 +541,11 @@ "id": "42", "metadata": {}, "source": [ - "### Add raster tile" + "### Adding Raster Tiles\n", + "\n", + "Raster tiles provide a set of customized styles for map layers, such as watercolor or artistic styles, which can add unique visual elements. Configuring raster tile styles involves setting tile URLs, tile size, and attribution information. This setup provides access to a wide array of predefined designs, offering creative flexibility in how data is presented.\n", + "\n", + "With raster tiles, you can control zoom limits and apply unique visual IDs to distinguish between multiple raster sources on the map." ] }, { @@ -565,7 +596,8 @@ "metadata": {}, "source": [ "## MapTiler\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/)." + "\n", + "To use MapTiler with this notebook, you need to set up a MapTiler API key. You can obtain a free API key by signing up at [https://cloud.maptiler.com/](https://cloud.maptiler.com/)." ] }, { @@ -584,9 +616,16 @@ "id": "47", "metadata": {}, "source": [ - "You can use any named style from MapTiler by setting the style parameter to the name of the style.\n", + "Set the API key as an environment variable to access MapTiler's resources. Once set up, you can specify any named style from MapTiler by using the style parameter in the map setup.\n", + "\n", + "![](https://i.imgur.com/dp2HxR2.png)\n", "\n", - "![](https://i.imgur.com/dp2HxR2.png)" + "The following are examples of different styles available through MapTiler:\n", + "\n", + "- **Streets style**: This style displays detailed street information and is suited for urban data visualization.\n", + "- **Satellite style**: Provides high-resolution satellite imagery for various locations.\n", + "- **Hybrid style**: Combines satellite imagery with labels for a clear geographic context.\n", + "- **Topo style**: A topographic map showing terrain details, ideal for outdoor-related applications." ] }, { @@ -638,7 +677,9 @@ "id": "52", "metadata": {}, "source": [ - "### Add a vector tile source" + "### Add a vector tile source\n", + "\n", + "To include vector data from MapTiler, first obtain the MapTiler API key and then set up a vector source with the desired tile data URL. The vector tile source can then be added to a layer to display features such as contour lines, which are styled for better visibility and engagement." ] }, { @@ -681,11 +722,21 @@ "id": "55", "metadata": {}, "source": [ - "## 3D mapping\n", + "## 3D Mapping\n", + "\n", + "MapTiler provides a variety of 3D styles that enhance geographic data visualization. Styles can be prefixed with `3d-` to utilize 3D effects such as hybrid, satellite, and topo. The `3d-hillshade` style is used for visualizing hillshade effects alone.\n", + "\n", + "### 3D Terrain\n", "\n", - "### 3D terrain\n", + "These examples demonstrate different ways to implement 3D terrain visualization:\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." + "- **3D Hybrid style**: Adds terrain relief to urban data with hybrid visualization.\n", + "- **3D Satellite style**: Combines satellite imagery with 3D elevation, enhancing visual context for topography.\n", + "- **3D Topo style**: Provides a topographic view with elevation exaggeration and optional hillshade.\n", + "- **3D Terrain style**: Displays terrain with default settings for natural geographic areas.\n", + "- **3D Ocean style**: A specialized terrain style with oceanic details, using exaggeration and hillshade to emphasize depth.\n", + "\n", + "Each terrain map setup includes a pitch and bearing to adjust the map's angle and orientation, giving a better perspective of 3D features." ] }, { @@ -775,9 +826,9 @@ "id": "61", "metadata": {}, "source": [ - "### 3D buildings\n", + "### 3D Buildings\n", "\n", - "You can add 3D buildings to the map using the `add_3d_buildings` method." + "Adding 3D buildings enhances urban visualizations, showing buildings with height variations. The setup involves specifying the MapTiler API key for vector tiles and adding building data as a 3D extrusion layer. The extrusion height and color can be set based on data attributes to visualize structures with varying heights, which can be useful in city planning and urban analysis." ] }, { @@ -859,9 +910,9 @@ "id": "64", "metadata": {}, "source": [ - "### 3D indoor mapping\n", + "### 3D Indoor Mapping\n", "\n", - "Let's visualize indoor mapping data using the `add_geojson` method." + "Indoor mapping data can be visualized by loading a GeoJSON file and applying the `add_geojson` method. This setup allows for displaying floorplans with attributes such as color, height, and opacity. It provides a realistic indoor perspective, which is useful for visualizing complex structures or navigating interior spaces." ] }, { @@ -917,7 +968,15 @@ "id": "68", "metadata": {}, "source": [ - "### 3D choropleth map" + "### 3D Choropleth Map\n", + "\n", + "Choropleth maps in 3D allow visualizing attribute data by height, where regions are colored and extruded based on specific attributes, such as age or population area. Two examples are provided below:\n", + "\n", + "- **European Countries (Age at First Marriage)**: The map displays different colors and extrusion heights based on age data. Color interpolations help represent data variations across regions, making it a suitable map for demographic studies.\n", + "\n", + "- **US Counties (Census Area)**: This example uses census data to display county areas in the United States, where each county’s area is represented with a different color and height based on its size. This type of map provides insights into geographic distribution and area proportions across the region.\n", + "\n", + "Both maps use a fill-extrusion style to dynamically adjust color and height, creating a 3D effect that enhances data interpretation." ] }, { @@ -1015,11 +1074,21 @@ "id": "71", "metadata": {}, "source": [ - "## Visualize vector data\n", + "## Visualize Vector Data\n", + "\n", + "Leafmap provides a variety of methods to visualize vector data on a map, allowing you to display points, lines, polygons, and other vector shapes with custom styling and interactivity.\n", + "\n", + "### Point Data\n", + "\n", + "The following examples demonstrate how to display points on the map.\n", "\n", - "Leafmap provides a variety of methods to visualize vector data on the map.\n", + "- **Simple Marker**: Initializes the map centered on a specific latitude and longitude, then adds a static marker to the location.\n", "\n", - "### Point data" + "- **Draggable Marker**: Similar to the simple marker, but with an additional option to make the marker draggable. This allows users to reposition it directly on the map.\n", + "\n", + "- **Multiple Points with GeoJSON**: Loads point data from a GeoJSON file containing world city locations. After reading the data, it’s added to the map as a layer named “cities.” Popups can be enabled to display additional information when a user clicks on a city.\n", + "\n", + "- **Custom Symbol Layer**: Loads point data as symbols instead of markers and customizes the symbol layout using icons, which can be scaled to any size." ] }, { @@ -1102,7 +1171,11 @@ "id": "77", "metadata": {}, "source": [ - "### Customize marker icon image" + "### Customize Marker Icon Image\n", + "\n", + "To further customize point data, an image icon can be used instead of the default marker:\n", + "\n", + "- **Add Custom Icon**: Loads an OSGeo logo as the custom marker image. The GeoJSON source for conference locations is loaded, and each location is marked with the custom icon and labeled with the year. Layout options define the placement of text and icons." ] }, { @@ -1144,7 +1217,11 @@ "id": "79", "metadata": {}, "source": [ - "### Line data" + "### Line Data\n", + "\n", + "Lines can be displayed to represent routes, boundaries, or connections between locations:\n", + "\n", + "- **Basic Line**: Sets up a line with multiple coordinates to create a path. The map displays this path with rounded line joins and a defined color and width, giving it a polished appearance." ] }, { @@ -1207,7 +1284,15 @@ "id": "81", "metadata": {}, "source": [ - "### Polygon data" + "### Polygon Data\n", + "\n", + "Polygons represent regions or areas on the map. Leafmap offers options to display filled polygons with customizable colors and opacities.\n", + "\n", + "- **Basic Polygon**: Adds a GeoJSON polygon representing an area and customizes its fill color and opacity.\n", + "\n", + "- **Outlined Polygon**: In addition to filling the polygon, an outline color is specified to highlight the polygon’s boundary. Both fill and line layers are defined, enhancing visual clarity.\n", + "\n", + "- **Building Visualization with GeoJSON**: Uses a URL to load building data and displays it with a customized fill color and outline. The style uses a hybrid map view for additional context." ] }, { @@ -1310,7 +1395,11 @@ "id": "86", "metadata": {}, "source": [ - "### Multiple geometries" + "### Multiple Geometries\n", + "\n", + "This example displays both line and extrusion fills for complex data types:\n", + "\n", + "- **Extruded Blocks with Line Overlay**: Loads Vancouver building blocks data and applies both line and fill-extrusion styles. Each block’s height is based on an attribute, which provides a 3D effect. This is useful for visualizing value distribution across an urban landscape." ] }, { @@ -1362,9 +1451,9 @@ "id": "89", "metadata": {}, "source": [ - "### Marker cluster\n", + "### Marker Cluster\n", "\n", - "Create a marker cluster layer." + "Clusters help manage large datasets, such as earthquake data, by grouping nearby markers. The color and size of clusters change based on the point count, and filters differentiate individual points from clusters. This example uses nested GeoJSON layers for visualizing clustered earthquake occurrences, with separate styles for individual points and clusters." ] }, { @@ -1435,9 +1524,9 @@ "id": "91", "metadata": {}, "source": [ - "### Local vector data\n", + "### Local Vector Data\n", "\n", - "You can load local vector data interactively using the `open_geojson` method." + "Local vector files, such as GeoJSON, can be loaded directly into the map. The example downloads a GeoJSON file representing U.S. states and adds it to the map using `open_geojson`." ] }, { @@ -1478,7 +1567,9 @@ "id": "95", "metadata": {}, "source": [ - "### GeoPandas" + "### Local Vector Data\n", + "\n", + "Local vector files, such as GeoJSON, can be loaded directly into the map. The example downloads a GeoJSON file representing U.S. states and adds it to the map using `open_geojson`." ] }, { @@ -1505,7 +1596,9 @@ "id": "97", "metadata": {}, "source": [ - "### Change building color" + "### Change Building Color\n", + "\n", + "You can customize the color and opacity of buildings based on the map’s zoom level. This example changes building colors from orange at lower zoom levels to lighter shades as the zoom level increases. Additionally, the opacity gradually transitions to fully opaque, making buildings more visible at close-up zoom levels." ] }, { @@ -1545,7 +1638,9 @@ "id": "100", "metadata": {}, "source": [ - "### Add a new layer below labels" + "### Add a New Layer Below Labels\n", + "\n", + "A layer can be added below existing labels on the map to enhance clarity without obscuring labels. The urban areas dataset is displayed as a fill layer in a color and opacity that visually distinguishes it from other map elements. The layer is positioned below symbols, allowing place names to remain visible." ] }, { @@ -1578,7 +1673,9 @@ "id": "102", "metadata": {}, "source": [ - "### Heat map" + "### Heat Map\n", + "\n", + "Heatmaps visually represent data density. This example uses earthquake data and configures the heatmap layer with dynamic properties, such as weight, intensity, color, and radius, based on earthquake magnitudes. Circles are added to indicate individual earthquakes, with colors and sizes varying according to their magnitude." ] }, { @@ -1681,7 +1778,9 @@ "id": "104", "metadata": {}, "source": [ - "### Visualize population density" + "### Visualize Population Density\n", + "\n", + "Population density can be calculated and displayed dynamically. This example loads a GeoJSON of Rwandan provinces, calculating density by dividing population by area. The fill color of each province is then adjusted based on density, with different color schemes applied depending on the zoom level." ] }, { @@ -1745,11 +1844,15 @@ "id": "106", "metadata": {}, "source": [ - "## Visualize raster data\n", + "## Visualize Raster Data\n", + "\n", + "### Local Raster Data\n", "\n", - "### Local raster data\n", + "To visualize local raster files, use the `add_raster` method. In the example, a Landsat image is downloaded and displayed using two different band combinations:\n", "\n", - "You can load local raster data using the `add_raster` method." + "- **Band Combination 3-2-1 (True Color)**: Simulates natural colors in the RGB channels.\n", + "- **Band Combination 4-3-2**: Enhances vegetation, displaying it in red for better visual contrast.\n", + "These layers are added to the map along with controls to toggle them. You can adjust brightness and contrast with the `vmin` and `vmax` arguments to improve clarity." ] }, { @@ -1812,10 +1915,18 @@ "m" ] }, + { + "cell_type": "markdown", + "id": "112", + "metadata": {}, + "source": [ + "A Digital Elevation Model (DEM) is also downloaded and visualized with a terrain color scheme. Leafmap’s `layer_interact` method allows interactive adjustments." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "112", + "id": "113", "metadata": {}, "outputs": [], "source": [ @@ -1824,18 +1935,18 @@ }, { "cell_type": "markdown", - "id": "113", + "id": "114", "metadata": {}, "source": [ "### Cloud Optimized GeoTIFF (COG)\n", "\n", - "You can load Cloud Optimized GeoTIFF (COG) data using the `add_cog_layer` method." + "Cloud Optimized GeoTIFFs (COG) are large raster files stored on cloud platforms, allowing efficient streaming and loading. This example loads satellite imagery of Libya before and after an event, showing the change over time. Each image is loaded with `add_cog_layer`, and layers can be toggled for comparison. Using `fit_bounds`, the map centers on the COG layer to fit its boundaries." ] }, { "cell_type": "code", "execution_count": null, - "id": "114", + "id": "115", "metadata": {}, "outputs": [], "source": [ @@ -1855,7 +1966,7 @@ { "cell_type": "code", "execution_count": null, - "id": "115", + "id": "116", "metadata": {}, "outputs": [], "source": [ @@ -1864,18 +1975,18 @@ }, { "cell_type": "markdown", - "id": "116", + "id": "117", "metadata": {}, "source": [ - "### STAC layer\n", + "### STAC Layer\n", "\n", - "You can load SpatioTemporal Asset Catalog (STAC) data using the `add_stac_layer` method." + "The SpatioTemporal Asset Catalog (STAC) standard organizes large satellite data collections. With `add_stac_layer`, this example loads Canadian satellite data, displaying both a panchromatic and an RGB layer from the same source. This approach allows easy switching between views." ] }, { "cell_type": "code", "execution_count": null, - "id": "117", + "id": "118", "metadata": {}, "outputs": [], "source": [ @@ -1890,17 +2001,25 @@ { "cell_type": "code", "execution_count": null, - "id": "118", + "id": "119", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, + { + "cell_type": "markdown", + "id": "120", + "metadata": {}, + "source": [ + "For more control, the `collection` and `item` parameters let you specify specific assets, such as color infrared bands, for focused analysis." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "119", + "id": "121", "metadata": {}, "outputs": [], "source": [ @@ -1911,7 +2030,7 @@ { "cell_type": "code", "execution_count": null, - "id": "120", + "id": "122", "metadata": {}, "outputs": [], "source": [ @@ -1921,7 +2040,7 @@ { "cell_type": "code", "execution_count": null, - "id": "121", + "id": "123", "metadata": {}, "outputs": [], "source": [ @@ -1937,24 +2056,23 @@ }, { "cell_type": "markdown", - "id": "122", - "metadata": {}, - "source": [ - "## Interact with the map" - ] - }, - { - "cell_type": "markdown", - "id": "123", + "id": "124", "metadata": {}, "source": [ - "### Display a non-interactive map" + "## Interact with the Map\n", + "\n", + "Interactivity allows for a more tailored map experience.\n", + "\n", + "\n", + "### Display a Non-Interactive Map\n", + "\n", + "To create a static map, set `interactive=False`. This disables all user interactions, making it ideal for static presentations." ] }, { "cell_type": "code", "execution_count": null, - "id": "124", + "id": "125", "metadata": {}, "outputs": [], "source": [ @@ -1966,16 +2084,18 @@ }, { "cell_type": "markdown", - "id": "125", + "id": "126", "metadata": {}, "source": [ - "### Disable scroll zoom" + "### Disable Scroll Zoom\n", + "\n", + "Use `scroll_zoom=False` to prevent map zooming with the scroll wheel, maintaining a fixed zoom level." ] }, { "cell_type": "code", "execution_count": null, - "id": "126", + "id": "127", "metadata": {}, "outputs": [], "source": [ @@ -1985,16 +2105,18 @@ }, { "cell_type": "markdown", - "id": "127", + "id": "128", "metadata": {}, "source": [ - "### Fit bounds" + "### Fit Bounds\n", + "\n", + "The `fit_bounds` method focuses the map on a specified area. In this example, the map centers on Kenya. Additionally, a GeoJSON line is added to the map, and its bounds are automatically calculated with `geojson_bounds` for a custom zoom fit." ] }, { "cell_type": "code", "execution_count": null, - "id": "128", + "id": "129", "metadata": {}, "outputs": [], "source": [ @@ -2004,7 +2126,7 @@ }, { "cell_type": "markdown", - "id": "129", + "id": "130", "metadata": {}, "source": [ "Fit to Kenya." @@ -2013,7 +2135,7 @@ { "cell_type": "code", "execution_count": null, - "id": "130", + "id": "131", "metadata": {}, "outputs": [], "source": [ @@ -2024,7 +2146,7 @@ { "cell_type": "code", "execution_count": null, - "id": "131", + "id": "132", "metadata": {}, "outputs": [], "source": [ @@ -2071,7 +2193,7 @@ { "cell_type": "code", "execution_count": null, - "id": "132", + "id": "133", "metadata": {}, "outputs": [], "source": [ @@ -2082,7 +2204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "133", + "id": "134", "metadata": {}, "outputs": [], "source": [ @@ -2091,16 +2213,18 @@ }, { "cell_type": "markdown", - "id": "134", + "id": "135", "metadata": {}, "source": [ - "### Restrict map panning to an area" + "### Restrict Map Panning to an Area\n", + "\n", + "To limit map panning to a specific area, define a bounding box. The map is then restricted within the bounds, ensuring users do not accidentally pan away from the area of interest." ] }, { "cell_type": "code", "execution_count": null, - "id": "135", + "id": "136", "metadata": {}, "outputs": [], "source": [ @@ -2113,7 +2237,7 @@ { "cell_type": "code", "execution_count": null, - "id": "136", + "id": "137", "metadata": {}, "outputs": [], "source": [ @@ -2123,16 +2247,18 @@ }, { "cell_type": "markdown", - "id": "137", + "id": "138", "metadata": {}, "source": [ - "### Fly to" + "### Fly To\n", + "\n", + "The `fly_to` method smoothly navigates to specified coordinates. Parameters like speed and zoom provide control over the fly-to effect. This example flies from the initial map location to New York City." ] }, { "cell_type": "code", "execution_count": null, - "id": "138", + "id": "139", "metadata": {}, "outputs": [], "source": [ @@ -2143,7 +2269,7 @@ { "cell_type": "code", "execution_count": null, - "id": "139", + "id": "140", "metadata": {}, "outputs": [], "source": [ @@ -2153,7 +2279,7 @@ { "cell_type": "code", "execution_count": null, - "id": "140", + "id": "141", "metadata": {}, "outputs": [], "source": [ @@ -2164,7 +2290,7 @@ { "cell_type": "code", "execution_count": null, - "id": "141", + "id": "142", "metadata": {}, "outputs": [], "source": [ @@ -2183,16 +2309,18 @@ }, { "cell_type": "markdown", - "id": "142", + "id": "143", "metadata": {}, "source": [ - "### Jump to a series of locations" + "### Jump to a Series of Locations\n", + "\n", + "Using `jump_to`, you can navigate between multiple locations. This example sets up a list of coordinates representing cities and automatically pans to each one in sequence. Adding a delay between transitions enhances the animation effect." ] }, { "cell_type": "code", "execution_count": null, - "id": "143", + "id": "144", "metadata": {}, "outputs": [], "source": [ @@ -2202,7 +2330,7 @@ { "cell_type": "code", "execution_count": null, - "id": "144", + "id": "145", "metadata": {}, "outputs": [], "source": [ @@ -2249,7 +2377,7 @@ { "cell_type": "code", "execution_count": null, - "id": "145", + "id": "146", "metadata": {}, "outputs": [], "source": [ @@ -2261,16 +2389,18 @@ }, { "cell_type": "markdown", - "id": "146", + "id": "147", "metadata": {}, "source": [ - "### Get coordinates of the mouse pointer" + "### Get Coordinates of the Mouse Pointer\n", + "\n", + "With widgets, you can display the current mouse pointer coordinates as it moves across the map. This is useful for precision mapping tasks where knowing exact coordinates is essential." ] }, { "cell_type": "code", "execution_count": null, - "id": "147", + "id": "148", "metadata": {}, "outputs": [], "source": [ @@ -2280,7 +2410,7 @@ { "cell_type": "code", "execution_count": null, - "id": "148", + "id": "149", "metadata": {}, "outputs": [], "source": [ @@ -2291,7 +2421,7 @@ { "cell_type": "code", "execution_count": null, - "id": "149", + "id": "150", "metadata": {}, "outputs": [], "source": [ @@ -2301,7 +2431,7 @@ { "cell_type": "code", "execution_count": null, - "id": "150", + "id": "151", "metadata": {}, "outputs": [], "source": [ @@ -2318,20 +2448,18 @@ "output" ] }, - { - "cell_type": "markdown", - "id": "151", - "metadata": {}, - "source": [ - "## Customize layer styles" - ] - }, { "cell_type": "markdown", "id": "152", "metadata": {}, "source": [ - "### Change layer color" + "## Customizing Layer Styles\n", + "\n", + "Customizing layer styles enables personalized map designs.\n", + "\n", + "### Change Layer Color\n", + "\n", + "Use `style_layer_interact` to interactively change the color of map layers, such as water bodies. This method provides an interactive palette for immediate style changes." ] }, { @@ -2360,7 +2488,9 @@ "id": "155", "metadata": {}, "source": [ - "### Change case of labels" + "### Change Case of Labels\n", + "\n", + "This example displays labels in upper or lower case based on their properties. The layout options let you customize font, size, and alignment. For example, facility names are displayed in uppercase, and comments are displayed in lowercase for contrast." ] }, { @@ -2405,7 +2535,9 @@ "id": "157", "metadata": {}, "source": [ - "### Variable label placement" + "### Variable Label Placement\n", + "\n", + "Labels can be automatically positioned around features with `text-variable-anchor`. This example defines multiple anchor points for labels to avoid overlap and ensure clear visibility. The radial offset and auto-justification properties create a professional, clutter-free appearance around points of interest." ] }, { @@ -2491,11 +2623,15 @@ "id": "160", "metadata": {}, "source": [ - "## Add custom components\n", + "## Adding Custom Components\n", + "\n", + "Enhance your maps by adding custom components such as images, videos, text, color bars, and legends.\n", "\n", - "You can add custom components to the map, including images, videos, text, color bar, and legend.\n", + "### Add Image\n", "\n", - "### Add image" + "You can add an image as an overlay or as an icon for a specific layer. For instance:\n", + "- Overlaying an image directly on the map at the \"bottom-right\" corner.\n", + "- Adding an icon image to a feature. In the example, a \"cat\" image is loaded, and a marker is added at coordinates `[0, 0]` with a label \"I love kitty!\" above the icon." ] }, { @@ -2562,10 +2698,18 @@ "m" ] }, + { + "cell_type": "markdown", + "id": "164", + "metadata": {}, + "source": [ + "To customize icons, you can also generate icon data with `numpy`, creating unique color gradients and using it as a map icon." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "164", + "id": "165", "metadata": {}, "outputs": [], "source": [ @@ -2621,16 +2765,20 @@ }, { "cell_type": "markdown", - "id": "165", + "id": "166", "metadata": {}, "source": [ - "### Add text" + "### Add Text\n", + "\n", + "Add text annotations to the map, specifying parameters like font size and background color. For example:\n", + "- Text \"Hello World\" in the bottom-right corner with a transparent background.\n", + "- \"Awesome Text!\" in the top-left corner with a slightly opaque white background, making it stand out." ] }, { "cell_type": "code", "execution_count": null, - "id": "166", + "id": "167", "metadata": {}, "outputs": [], "source": [ @@ -2644,16 +2792,18 @@ }, { "cell_type": "markdown", - "id": "167", + "id": "168", "metadata": {}, "source": [ - "### Add GIF" + "### Add GIF\n", + "\n", + "GIFs can be added as animated overlays to bring your map to life. Example: add a sloth GIF in the bottom-right and a second GIF in the bottom-left corner, with a text label indicating “I love sloth!” for added character." ] }, { "cell_type": "code", "execution_count": null, - "id": "168", + "id": "169", "metadata": {}, "outputs": [], "source": [ @@ -2669,16 +2819,18 @@ }, { "cell_type": "markdown", - "id": "169", + "id": "170", "metadata": {}, "source": [ - "### Add HTML" + "### Add HTML\n", + "\n", + "Embed custom HTML content to display various HTML elements, such as emojis or stylized text. You can also adjust the font size and background transparency for better integration into the map design." ] }, { "cell_type": "code", "execution_count": null, - "id": "170", + "id": "171", "metadata": {}, "outputs": [], "source": [ @@ -2705,16 +2857,20 @@ }, { "cell_type": "markdown", - "id": "171", + "id": "172", "metadata": {}, "source": [ - "### Add colorbar" + "### Add Colorbar\n", + "\n", + "Adding a color bar enhances data interpretation. In the example:\n", + "1. A Digital Elevation Model (DEM) is displayed with a color ramp from 0 to 1500 meters.\n", + "2. `add_colorbar` method is used to create a color bar with labels, adjusting its position, opacity, and orientation for optimal readability." ] }, { "cell_type": "code", "execution_count": null, - "id": "172", + "id": "173", "metadata": {}, "outputs": [], "source": [ @@ -2738,7 +2894,7 @@ { "cell_type": "code", "execution_count": null, - "id": "173", + "id": "174", "metadata": {}, "outputs": [], "source": [ @@ -2765,7 +2921,7 @@ { "cell_type": "code", "execution_count": null, - "id": "174", + "id": "175", "metadata": {}, "outputs": [], "source": [ @@ -2793,16 +2949,20 @@ }, { "cell_type": "markdown", - "id": "175", + "id": "176", "metadata": {}, "source": [ - "### Add legend" + "### Add Legend\n", + "\n", + "Custom legends help users understand data classifications. Two methods are shown:\n", + "1. Using built-in legends, such as for NLCD (National Land Cover Database) or wetland types.\n", + "2. Custom legends are built with a dictionary of land cover types and colors. This legend provides descriptive color-coding for various land cover types, with configurable background opacity to blend with the map." ] }, { "cell_type": "code", "execution_count": null, - "id": "176", + "id": "177", "metadata": {}, "outputs": [], "source": [ @@ -2823,7 +2983,7 @@ { "cell_type": "code", "execution_count": null, - "id": "177", + "id": "178", "metadata": {}, "outputs": [], "source": [ @@ -2839,7 +2999,7 @@ { "cell_type": "code", "execution_count": null, - "id": "178", + "id": "179", "metadata": {}, "outputs": [], "source": [ @@ -2882,10 +3042,13 @@ }, { "cell_type": "markdown", - "id": "179", + "id": "180", "metadata": {}, "source": [ - "### Add video\n", + "### Add Video\n", + "\n", + "Videos can be added with geographic context by specifying corner coordinates. Videos must be listed in multiple formats to ensure compatibility across browsers. The coordinates array should define the video’s location on the map in the order: top-left, top-right, bottom-right, and bottom-left. This is demonstrated by adding drone footage to a satellite map view, enhancing the user experience with real-world visuals.\n", + "\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." @@ -2894,7 +3057,7 @@ { "cell_type": "code", "execution_count": null, - "id": "180", + "id": "181", "metadata": {}, "outputs": [], "source": [ @@ -2919,7 +3082,7 @@ { "cell_type": "code", "execution_count": null, - "id": "181", + "id": "182", "metadata": {}, "outputs": [], "source": [ @@ -2941,20 +3104,22 @@ }, { "cell_type": "markdown", - "id": "182", + "id": "183", "metadata": {}, "source": [ "## PMTiles\n", "\n", - "Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format for fast and efficient rendering of vector tiles.\n", + "Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format, enabling efficient storage and fast rendering of vector tiles directly in the browser.\n", + "\n", + "### Protomaps Sample Data\n", "\n", - "### Protomaps sample data" + "Load Protomaps data in PMTiles format for fast, high-resolution vector map data rendering. Use `pmtiles_metadata()` to fetch details like layer names and map bounds, then style and add these tiles to your map. For instance, the example shows two layers: `buildings` styled in \"steelblue\" and `roads` styled in \"black\"." ] }, { "cell_type": "code", "execution_count": null, - "id": "183", + "id": "184", "metadata": {}, "outputs": [], "source": [ @@ -2967,7 +3132,7 @@ { "cell_type": "code", "execution_count": null, - "id": "184", + "id": "185", "metadata": {}, "outputs": [], "source": [ @@ -3015,29 +3180,21 @@ { "cell_type": "code", "execution_count": null, - "id": "185", + "id": "186", "metadata": {}, "outputs": [], "source": [ "m.layer_interact()" ] }, - { - "cell_type": "markdown", - "id": "186", - "metadata": {}, - "source": [ - "### Overture data\n", - "\n", - "You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps)." - ] - }, { "cell_type": "markdown", "id": "187", "metadata": {}, "source": [ - "### Source Cooperative\n", + "### Source Cooperative Data\n", + "\n", + "Visualize the Google-Microsoft Open Buildings dataset, managed by VIDA, in PMTiles format. Fetch metadata to identify available layers, apply custom styles to the building footprints, and render them with semi-transparent colors for a clear visualization.\n", "\n", "Let's visualize the [Google-Microsoft Open Buildings - combined by VIDA](https://beta.source.coop/repositories/vida/google-microsoft-open-buildings/description)." ] @@ -3114,7 +3271,7 @@ "source": [ "### 3D PMTiles\n", "\n", - "Visualize the global building data in 3D." + "Render global building data in 3D for a realistic, textured experience. Set building colors and extrusion heights to create visually compelling cityscapes. For example, apply color gradients and height scaling based on building attributes to differentiate buildings by their heights." ] }, { @@ -3191,7 +3348,9 @@ "id": "194", "metadata": {}, "source": [ - "### 3D buildings" + "### 3D Buildings\n", + "\n", + "For a simplified setup, the `add_overture_3d_buildings` function quickly adds 3D building data from Overture’s latest release to a basemap, creating depth and spatial realism on the map." ] }, { @@ -3215,7 +3374,11 @@ "id": "196", "metadata": {}, "source": [ - "### 2D buildings" + "### 2D Buildings, Transportation, and Other Themes\n", + "\n", + "Switch between specific Overture themes, such as `buildings`, `transportation`, `places`, or `addresses`, depending on the data focus. Each theme overlays relevant features with adjustable opacity for thematic visualization, providing easy customization for urban planning or transportation studies.\n", + "\n", + "Building theme:" ] }, { @@ -3237,7 +3400,7 @@ "id": "198", "metadata": {}, "source": [ - "### Transportation" + "Transportation theme:" ] }, { @@ -3259,7 +3422,7 @@ "id": "200", "metadata": {}, "source": [ - "### Places" + "Places theme:" ] }, { @@ -3281,7 +3444,7 @@ "id": "202", "metadata": {}, "source": [ - "### Addresses" + "Addresses theme:" ] }, { @@ -3303,7 +3466,7 @@ "id": "204", "metadata": {}, "source": [ - "### Base" + "Base theme:" ] }, { @@ -3325,7 +3488,7 @@ "id": "206", "metadata": {}, "source": [ - "### Divisions" + "Divisions themem:" ] }, { @@ -3347,11 +3510,13 @@ "id": "208", "metadata": {}, "source": [ - "## Deck.GL layers\n", + "## Deck.GL Layers\n", "\n", - "Deck.GL layers can be added to the map using the `add_deck_layer` method.\n", + "Integrate interactive, high-performance visualization layers using Deck.GL. Leafmap’s `add_deck_layer` method supports multiple Deck.GL layer types, including Grid, GeoJSON, and Arc layers.\n", "\n", - "### Single Deck.GL layer" + "### Single Deck.GL Layer\n", + "\n", + "Add a `GridLayer` to visualize point data, such as the density of bike parking in San Francisco. Customize the grid cell size, elevation, and color to represent data density visually and interactively." ] }, { @@ -3389,7 +3554,11 @@ "id": "210", "metadata": {}, "source": [ - "### Multiple Deck.GL layers" + "### Multiple Deck.GL Layers\n", + "\n", + "Combine layers like `GeoJsonLayer` and `ArcLayer` for complex visualizations. For example:\n", + "1. Use `GeoJsonLayer` to show airports with varying point sizes based on significance.\n", + "2. Use `ArcLayer` to connect selected airports with London, coloring arcs to represent different paths." ] }, { @@ -3460,9 +3629,14 @@ "id": "213", "metadata": {}, "source": [ + "The result is a rich, interactive visualization that highlights both point data and relational data connections, useful for airport connectivity or hub-and-spoke modeling.\n", + "\n", + "\n", "## Google Earth Engine\n", "\n", - "You can use the Earth Engine Python API to load and visualize Earth Engine data." + "Leafmap enables integration with the Google Earth Engine (GEE) Python API, allowing for powerful visualization of Earth Engine datasets directly on a Leafmap map.\n", + "\n", + "To add Earth Engine layers, initialize a map and use `add_ee_layer` to add specific datasets, such as ESA WorldCover data. A legend can be included using `add_legend` to provide a visual reference." ] }, { @@ -3496,7 +3670,7 @@ "id": "216", "metadata": {}, "source": [ - "We can also overlay other data layers on top of Earth Engine data layers." + "You can overlay Earth Engine data with other 3D elements, like buildings, to create a multi-layered, interactive map. If you have an Earth Engine account, authenticate and initialize Earth Engine in your notebook by uncommenting the relevant code." ] }, { @@ -3559,7 +3733,9 @@ "source": [ "## Animations\n", "\n", - "### Animate a line" + "### Animate a Line\n", + "\n", + "Using Leafmap’s animation capabilities, you can animate a line by updating the coordinates of a GeoJSON line feature in real-time. The sample includes data loaded from a CSV file, which is sorted and plotted sequentially to show the line’s movement." ] }, { @@ -3635,7 +3811,9 @@ "id": "226", "metadata": {}, "source": [ - "### Animate map camera around a point" + "### Animate the Map Camera Around a Point\n", + "\n", + "Control the map camera's rotation, zoom, and angle around a given point to create a smooth animation effect. This technique is especially useful for visualizing cityscapes or other 3D environments by making buildings appear dynamic." ] }, { @@ -3689,7 +3867,9 @@ "id": "228", "metadata": {}, "source": [ - "### Animate a point" + "### Animate a Point\n", + "\n", + "Animate a point along a circular path by computing points on a circle and updating the map. This is ideal for showing circular motion on the map, and can be customized for duration and frame rate to control the animation speed." ] }, { @@ -3793,7 +3973,9 @@ "id": "236", "metadata": {}, "source": [ - "### Animate a point along a route" + "### Animate a Point Along a Route\n", + "\n", + "Create a point that follows a route specified by GeoJSON coordinates. For added realism, include rotation properties to simulate the direction of movement along the path. This animation is useful for tracking paths, such as vehicle or drone routes." ] }, { @@ -3860,7 +4042,9 @@ "id": "239", "metadata": {}, "source": [ - "### Update a feature in realtime" + "### Update a Feature in Real-Time\n", + "\n", + "For applications such as tracking or tracing a path in real time, load data from a source like GeoDataFrame, append coordinates incrementally to a line feature, and update the map display as the path extends." ] }, { @@ -3944,7 +4128,7 @@ "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." + "Export the final map to HTML using the `to_html` method. This allows you to share interactive maps online while keeping your private API keys secure. Create public API keys restricted to your website’s domain for safety. Customize the HTML output, including dimensions, title, and embedding options." ] }, { diff --git a/book/geospatial/maplibre.md b/book/geospatial/maplibre.md index 77f0a63..871905f 100644 --- a/book/geospatial/maplibre.md +++ b/book/geospatial/maplibre.md @@ -36,52 +36,60 @@ By the end of this lecture, students will be able to: - Videos: https://bit.ly/maplibre - Demos: https://maps.gishub.org -## Installation +## Installation and Setup -Uncomment the following line to install the required Python packages. +To install the required packages, uncomment and run the line below. ```{code-cell} ipython3 # %pip install -U "leafmap[maplibre]" ``` -Import the maplibre mapping backend. +Once installed, import the `maplibregl` backend from the `leafmap` package: ```{code-cell} ipython3 import leafmap.maplibregl as leafmap ``` -## Create Interactive Maps +## Creating Interactive Maps -Let's create a simple interactive map using Leafmap. +### Basic Map Setup + +Let’s start by creating a simple interactive map with default settings. This basic setup provides a blank canvas on which you can add data layers, controls, and other customizations. ```{code-cell} ipython3 m = leafmap.Map() m ``` -You can customize the map by specifying map center [lon, lat], zoom level, pitch, and bearing. +### Customizing the Map's Center and Zoom Level + +You can specify the map’s center (latitude and longitude), zoom level, pitch, and bearing for a more focused view. These parameters help direct the user's attention to specific areas. ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, pitch=0, bearing=0) m ``` -To customize the basemap, you can specify the `style` parameter. It can be an URL or a string, such as `dark-matter`, `positron`, `voyager`, `liberty`, `demotiles`. +### Choosing a Basemap Style + +MapLibre supports several pre-defined basemap styles such as `dark-matter`, `positron`, `voyager`, `liberty`, `demotiles`. You can also use custom basemap URLs for unique styling. ```{code-cell} ipython3 m = leafmap.Map(style="positron") m ``` -To create a map with a background color, use `style="background-"`, such as `background-lightgray` and `background-green`. - ```{code-cell} ipython3 -m = leafmap.Map(style="background-lightgray") +m = leafmap.Map(style="liberty") m ``` +### Adding Background Colors + +Background color styles, such as `background-lightgray` and `background-green`, are helpful for simple or thematic maps where color-coding is needed. + ```{code-cell} ipython3 -m = leafmap.Map(style="liberty") +m = leafmap.Map(style="background-lightgray") m ``` @@ -93,11 +101,21 @@ m = leafmap.Map(style=style) m ``` -## Add map controls +## Adding Map Controls + +Map controls enhance the usability of the map by allowing users to interact in various ways, adding elements like scale bars, zoom tools, and drawing options. + +### Available Controls + +- **Scale**: Adds a scale bar to indicate the map’s scale. +- **Fullscreen**: Expands the map to a full-screen view for better focus. +- **Geolocate**: Centers the map based on the user’s current location, if available. +- **Navigation**: Provides zoom controls and a compass for reorientation. +- **Draw**: Allows users to draw and edit shapes on the map. -The control to add to the map. Can be one of the following: `scale`, `fullscreen`, `geolocate`, `navigation`. +### Adding Geolocate Control -### Geolocate control +The Geolocate control centers the map based on the user’s current location, a helpful feature for location-based applications. ```{code-cell} ipython3 m = leafmap.Map() @@ -105,7 +123,9 @@ m.add_control("geolocate", position="top-left") m ``` -### Fullscreen control +### Adding Fullscreen Control + +Fullscreen control enables users to expand the map to full screen, enhancing focus and visual clarity. This is especially useful when viewing complex or large datasets. ```{code-cell} ipython3 m = leafmap.Map(center=[11.255, 43.77], zoom=13, style="streets", controls={}) @@ -113,7 +133,9 @@ m.add_control("fullscreen", position="top-right") m ``` -### Navigation control +### Adding Navigation Control + +The Navigation control provides buttons for zooming and reorienting the map, improving the user's ability to navigate efficiently. ```{code-cell} ipython3 m = leafmap.Map(center=[11.255, 43.77], zoom=13, style="streets", controls={}) @@ -121,9 +143,9 @@ m.add_control("navigation", position="top-left") m ``` -### Draw control +### Adding Draw Control -Add the default draw control. +The Draw control enables users to interact with the map by adding shapes such as points, lines, and polygons. This control is essential for tasks requiring spatial data input directly on the map. ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") @@ -131,7 +153,7 @@ m.add_draw_control(position="top-left") m ``` -Only activate a give set of control. +You can configure the Draw control to activate specific tools, like polygon, line, or point drawing, while also enabling or disabling a "trash" button to remove unwanted shapes. ```{code-cell} ipython3 from maplibre.plugins import MapboxDrawControls, MapboxDrawOptions @@ -147,7 +169,7 @@ m.add_draw_control(draw_options) m ``` -Load a GeoJSON FeatureCollection to the draw control. +Additionally, you can load a GeoJSON FeatureCollection into the Draw control, which will allow users to view, edit, or interact with pre-defined geographical features, such as boundaries or points of interest. ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") @@ -196,7 +218,10 @@ m.add_draw_control(position="top-left", geojson=geojson) m ``` -Retrieve the draw features. +Two key methods for accessing drawn features: + +- **Selected Features**: Accesses only the currently selected features. +- **All Features**: Accesses all features added, regardless of selection, giving you full control over the spatial data on the map. ```{code-cell} ipython3 m.draw_features_selected @@ -206,11 +231,13 @@ m.draw_features_selected m.draw_feature_collection_all ``` -## Add layers +## Add Layers -### Add basemaps +Adding layers to a map enhances the data it presents, allowing different types of basemaps, tile layers, and thematic overlays to be combined for in-depth analysis. -You can add basemaps to the map using the `add_basemap` method. +### Adding Basemaps + +Basemaps provide a geographical context for the map. Using the `add_basemap` method, you can select from various basemaps, including `OpenTopoMap` and `Esri.WorldImagery`. Adding a layer control allows users to switch between multiple basemaps interactively. ```{code-cell} ipython3 m = leafmap.Map() @@ -228,15 +255,15 @@ m = leafmap.Map() m ``` -To add basemaps interactively, use the `add_basemap` method without specifying the `basemap` parameter. +You can also add basemaps interactively, which provides flexibility for selecting the best background for your map content. ```{code-cell} ipython3 m.add_basemap() ``` -### Add XYZ tile layer +### Adding XYZ Tile Layer -You can add XYZ tile layers to the map using the `add_tile_layer` method. +XYZ tile layers allow integration of specific tile services like topographic, satellite, or other thematic imagery from XYZ tile servers. By specifying the URL and parameters such as `opacity` and `visibility`, XYZ layers can be customized and styled to fit the needs of your map. ```{code-cell} ipython3 m = leafmap.Map() @@ -245,9 +272,9 @@ m.add_tile_layer(url, name="USGS TOpo", attribution="USGS", opacity=1.0, visible m ``` -### Add WMS layer +### Adding WMS Layer -You can add WMS layers to the map using the `add_wms_layer` method. +Web Map Service (WMS) layers provide access to external datasets served by web servers, such as thematic maps or detailed satellite imagery. Adding a WMS layer involves specifying the WMS URL and layer names, which allows for overlaying various data types such as natural imagery or land cover classifications. ```{code-cell} ipython3 m = leafmap.Map(center=[-74.5447, 40.6892], zoom=8, style="liberty") @@ -268,7 +295,11 @@ m.add_legend(builtin_legend="NWI", title="Wetland Type") m ``` -### Add raster tile +### Adding Raster Tiles + +Raster tiles provide a set of customized styles for map layers, such as watercolor or artistic styles, which can add unique visual elements. Configuring raster tile styles involves setting tile URLs, tile size, and attribution information. This setup provides access to a wide array of predefined designs, offering creative flexibility in how data is presented. + +With raster tiles, you can control zoom limits and apply unique visual IDs to distinguish between multiple raster sources on the map. ```{code-cell} ipython3 style = { @@ -301,17 +332,25 @@ m ``` ## MapTiler -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/). + +To use MapTiler with this notebook, you need to set up a MapTiler API key. You can obtain a free API key by signing up at [https://cloud.maptiler.com/](https://cloud.maptiler.com/). ```{code-cell} ipython3 # import os # os.environ["MAPTILER_KEY"] = "YOUR_API_KEY" ``` -You can use any named style from MapTiler by setting the style parameter to the name of the style. +Set the API key as an environment variable to access MapTiler's resources. Once set up, you can specify any named style from MapTiler by using the style parameter in the map setup. ![](https://i.imgur.com/dp2HxR2.png) +The following are examples of different styles available through MapTiler: + +- **Streets style**: This style displays detailed street information and is suited for urban data visualization. +- **Satellite style**: Provides high-resolution satellite imagery for various locations. +- **Hybrid style**: Combines satellite imagery with labels for a clear geographic context. +- **Topo style**: A topographic map showing terrain details, ideal for outdoor-related applications. + ```{code-cell} ipython3 m = leafmap.Map(style="streets") m @@ -334,6 +373,8 @@ m ### Add a vector tile source +To include vector data from MapTiler, first obtain the MapTiler API key and then set up a vector source with the desired tile data URL. The vector tile source can then be added to a layer to display features such as contour lines, which are styled for better visibility and engagement. + ```{code-cell} ipython3 MAPTILER_KEY = leafmap.get_api_key("MAPTILER_KEY") ``` @@ -357,33 +398,63 @@ m.add_layer(layer) m ``` -## 3D mapping +## 3D Mapping + +MapTiler provides a variety of 3D styles that enhance geographic data visualization. Styles can be prefixed with `3d-` to utilize 3D effects such as hybrid, satellite, and topo. The `3d-hillshade` style is used for visualizing hillshade effects alone. + +### 3D Terrain -### 3D terrain +These examples demonstrate different ways to implement 3D terrain visualization: -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. +- **3D Hybrid style**: Adds terrain relief to urban data with hybrid visualization. +- **3D Satellite style**: Combines satellite imagery with 3D elevation, enhancing visual context for topography. +- **3D Topo style**: Provides a topographic view with elevation exaggeration and optional hillshade. +- **3D Terrain style**: Displays terrain with default settings for natural geographic areas. +- **3D Ocean style**: A specialized terrain style with oceanic details, using exaggeration and hillshade to emphasize depth. + +Each terrain map setup includes a pitch and bearing to adjust the map's angle and orientation, giving a better perspective of 3D features. ```{code-cell} ipython3 -m = leafmap.Map(center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-hybrid") +m = leafmap.Map( + center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-hybrid" +) m.add_layer_control(bg_layers=True) m ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-satellite") +m = leafmap.Map( + center=[-122.1874314, 46.2022386], + zoom=13, + pitch=60, + bearing=220, + style="3d-satellite", +) m.add_layer_control(bg_layers=True) m ``` ```{code-cell} ipython3 -m = leafmap.Map(center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-topo", exaggeration=1.5, hillshade=False) +m = leafmap.Map( + center=[-122.1874314, 46.2022386], + zoom=13, + pitch=60, + bearing=220, + style="3d-topo", + exaggeration=1.5, + hillshade=False, +) m.add_layer_control(bg_layers=True) m ``` ```{code-cell} ipython3 m = leafmap.Map( - center=[-122.1874314, 46.2022386], zoom=13, pitch=60, bearing=220, style="3d-terrain" + center=[-122.1874314, 46.2022386], + zoom=13, + pitch=60, + bearing=220, + style="3d-terrain", ) m.add_layer_control(bg_layers=True) m @@ -395,9 +466,9 @@ m.add_layer_control(bg_layers=True) m ``` -### 3D buildings +### 3D Buildings -You can add 3D buildings to the map using the `add_3d_buildings` method. +Adding 3D buildings enhances urban visualizations, showing buildings with height variations. The setup involves specifying the MapTiler API key for vector tiles and adding building data as a 3D extrusion layer. The extrusion height and color can be set based on data attributes to visualize structures with varying heights, which can be useful in city planning and urban analysis. ```{code-cell} ipython3 m = leafmap.Map( @@ -461,9 +532,9 @@ m.add_layer_control() m ``` -### 3D indoor mapping +### 3D Indoor Mapping -Let's visualize indoor mapping data using the `add_geojson` method. +Indoor mapping data can be visualized by loading a GeoJSON file and applying the `add_geojson` method. This setup allows for displaying floorplans with attributes such as color, height, and opacity. It provides a realistic indoor perspective, which is useful for visualizing complex structures or navigating interior spaces. ```{code-cell} ipython3 data = "https://maplibre.org/maplibre-gl-js/docs/assets/indoor-3d-map.geojson" @@ -495,7 +566,15 @@ m.add_layer_control() m ``` -### 3D choropleth map +### 3D Choropleth Map + +Choropleth maps in 3D allow visualizing attribute data by height, where regions are colored and extruded based on specific attributes, such as age or population area. Two examples are provided below: + +- **European Countries (Age at First Marriage)**: The map displays different colors and extrusion heights based on age data. Color interpolations help represent data variations across regions, making it a suitable map for demographic studies. + +- **US Counties (Census Area)**: This example uses census data to display county areas in the United States, where each county’s area is represented with a different color and height based on its size. This type of map provides insights into geographic distribution and area proportions across the region. + +Both maps use a fill-extrusion style to dynamically adjust color and height, creating a 3D effect that enhances data interpretation. ```{code-cell} ipython3 m = leafmap.Map(center=[19.43, 49.49], zoom=3, pitch=60, style="basic") @@ -575,11 +654,21 @@ m.add_layer_control() m ``` -## Visualize vector data +## Visualize Vector Data + +Leafmap provides a variety of methods to visualize vector data on a map, allowing you to display points, lines, polygons, and other vector shapes with custom styling and interactivity. + +### Point Data -Leafmap provides a variety of methods to visualize vector data on the map. +The following examples demonstrate how to display points on the map. -### Point data +- **Simple Marker**: Initializes the map centered on a specific latitude and longitude, then adds a static marker to the location. + +- **Draggable Marker**: Similar to the simple marker, but with an additional option to make the marker draggable. This allows users to reposition it directly on the map. + +- **Multiple Points with GeoJSON**: Loads point data from a GeoJSON file containing world city locations. After reading the data, it’s added to the map as a layer named “cities.” Popups can be enabled to display additional information when a user clicks on a city. + +- **Custom Symbol Layer**: Loads point data as symbols instead of markers and customizes the symbol layout using icons, which can be scaled to any size. ```{code-cell} ipython3 m = leafmap.Map(center=[12.550343, 55.665957], zoom=8, style="positron") @@ -594,7 +683,9 @@ m ``` ```{code-cell} ipython3 -url = "https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson" +url = ( + "https://github.com/opengeos/datasets/releases/download/world/world_cities.geojson" +) geojson = leafmap.read_geojson(url) ``` @@ -624,7 +715,11 @@ m.add_popup("cities") m ``` -### Customize marker icon image +### Customize Marker Icon Image + +To further customize point data, an image icon can be used instead of the default marker: + +- **Add Custom Icon**: Loads an OSGeo logo as the custom marker image. The GeoJSON source for conference locations is loaded, and each location is marked with the custom icon and labeled with the year. Layout options define the placement of text and icons. ```{code-cell} ipython3 m = leafmap.Map(center=[0, 0], zoom=1, style="positron") @@ -634,10 +729,7 @@ m.add_image("custom-marker", image) url = "https://github.com/opengeos/datasets/releases/download/places/osgeo_conferences.geojson" geojson = leafmap.read_geojson(url) -source = { - "type": "geojson", - "data": geojson -} +source = {"type": "geojson", "data": geojson} m.add_source("conferences", source) layer = { @@ -657,7 +749,11 @@ m.add_layer(layer) m ``` -### Line data +### Line Data + +Lines can be displayed to represent routes, boundaries, or connections between locations: + +- **Basic Line**: Sets up a line with multiple coordinates to create a path. The map displays this path with rounded line joins and a defined color and width, giving it a polished appearance. ```{code-cell} ipython3 m = leafmap.Map(center=[-122.486052, 37.830348], zoom=15, style="streets") @@ -708,7 +804,15 @@ m.add_layer(layer) m ``` -### Polygon data +### Polygon Data + +Polygons represent regions or areas on the map. Leafmap offers options to display filled polygons with customizable colors and opacities. + +- **Basic Polygon**: Adds a GeoJSON polygon representing an area and customizes its fill color and opacity. + +- **Outlined Polygon**: In addition to filling the polygon, an outline color is specified to highlight the polygon’s boundary. Both fill and line layers are defined, enhancing visual clarity. + +- **Building Visualization with GeoJSON**: Uses a URL to load building data and displays it with a customized fill color and outline. The style uses a hybrid map view for additional context. ```{code-cell} ipython3 m = leafmap.Map(center=[-68.137343, 45.137451], zoom=5, style="streets") @@ -781,7 +885,11 @@ m.add_layer_control() m ``` -### Multiple geometries +### Multiple Geometries + +This example displays both line and extrusion fills for complex data types: + +- **Extruded Blocks with Line Overlay**: Loads Vancouver building blocks data and applies both line and fill-extrusion styles. Each block’s height is based on an attribute, which provides a 3D effect. This is useful for visualizing value distribution across an urban landscape. ```{code-cell} ipython3 m = leafmap.Map( @@ -815,9 +923,9 @@ m m.layer_interact() ``` -### Marker cluster +### Marker Cluster -Create a marker cluster layer. +Clusters help manage large datasets, such as earthquake data, by grouping nearby markers. The color and size of clusters change based on the point count, and filters differentiate individual points from clusters. This example uses nested GeoJSON layers for visualizing clustered earthquake occurrences, with separate styles for individual points and clusters. ```{code-cell} ipython3 m = leafmap.Map(center=[-103.59179, 40.66995], zoom=3, style="streets") @@ -876,9 +984,9 @@ m.add_geojson( m ``` -### Local vector data +### Local Vector Data -You can load local vector data interactively using the `open_geojson` method. +Local vector files, such as GeoJSON, can be loaded directly into the map. The example downloads a GeoJSON file representing U.S. states and adds it to the map using `open_geojson`. ```{code-cell} ipython3 url = "https://github.com/opengeos/datasets/releases/download/us/us_states.geojson" @@ -895,7 +1003,9 @@ m m.open_geojson() ``` -### GeoPandas +### Local Vector Data + +Local vector files, such as GeoJSON, can be loaded directly into the map. The example downloads a GeoJSON file representing U.S. states and adds it to the map using `open_geojson`. ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") @@ -910,7 +1020,9 @@ m.add_gdf(gdf, layer_type="fill", name="States", paint=paint) m ``` -### Change building color +### Change Building Color + +You can customize the color and opacity of buildings based on the map’s zoom level. This example changes building colors from orange at lower zoom levels to lighter shades as the zoom level increases. Additionally, the opacity gradually transitions to fully opaque, making buildings more visible at close-up zoom levels. ```{code-cell} ipython3 m = leafmap.Map(center=[-90.73414, 14.55524], zoom=13, style="basic") @@ -932,7 +1044,9 @@ m m.add_call("zoomTo", 19, {"duration": 9000}) ``` -### Add a new layer below labels +### Add a New Layer Below Labels + +A layer can be added below existing labels on the map to enhance clarity without obscuring labels. The urban areas dataset is displayed as a fill layer in a color and opacity that visually distinguishes it from other map elements. The layer is positioned below symbols, allowing place names to remain visible. ```{code-cell} ipython3 m = leafmap.Map(center=[-88.137343, 35.137451], zoom=4, style="streets") @@ -953,7 +1067,9 @@ m.add_layer(layer, before_id=first_symbol_layer["id"]) m ``` -### Heat map +### Heat Map + +Heatmaps visually represent data density. This example uses earthquake data and configures the heatmap layer with dynamic properties, such as weight, intensity, color, and radius, based on earthquake magnitudes. Circles are added to indicate individual earthquakes, with colors and sizes varying according to their magnitude. ```{code-cell} ipython3 m = leafmap.Map(center=[-120, 50], zoom=2, style="basic") @@ -1044,7 +1160,9 @@ m.add_layer(layer2, before_id="waterway") m ``` -### Visualize population density +### Visualize Population Density + +Population density can be calculated and displayed dynamically. This example loads a GeoJSON of Rwandan provinces, calculating density by dividing population by area. The fill color of each province is then adjusted based on density, with different color schemes applied depending on the zoom level. ```{code-cell} ipython3 m = leafmap.Map(center=[30.0222, -1.9596], zoom=7, style="streets") @@ -1096,11 +1214,15 @@ m.add_layer(layer) m ``` -## Visualize raster data +## Visualize Raster Data + +### Local Raster Data -### Local raster data +To visualize local raster files, use the `add_raster` method. In the example, a Landsat image is downloaded and displayed using two different band combinations: -You can load local raster data using the `add_raster` method. +- **Band Combination 3-2-1 (True Color)**: Simulates natural colors in the RGB channels. +- **Band Combination 4-3-2**: Enhances vegetation, displaying it in red for better visual contrast. +These layers are added to the map along with controls to toggle them. You can adjust brightness and contrast with the `vmin` and `vmax` arguments to improve clarity. ```{code-cell} ipython3 url = "https://github.com/opengeos/datasets/releases/download/raster/landsat.tif" @@ -1132,13 +1254,15 @@ m.add_raster(filepath, colormap="terrain", name="DEM") m ``` +A Digital Elevation Model (DEM) is also downloaded and visualized with a terrain color scheme. Leafmap’s `layer_interact` method allows interactive adjustments. + ```{code-cell} ipython3 m.layer_interact() ``` ### Cloud Optimized GeoTIFF (COG) -You can load Cloud Optimized GeoTIFF (COG) data using the `add_cog_layer` method. +Cloud Optimized GeoTIFFs (COG) are large raster files stored on cloud platforms, allowing efficient streaming and loading. This example loads satellite imagery of Libya before and after an event, showing the change over time. Each image is loaded with `add_cog_layer`, and layers can be toggled for comparison. Using `fit_bounds`, the map centers on the COG layer to fit its boundaries. ```{code-cell} ipython3 m = leafmap.Map(style="satellite") @@ -1158,9 +1282,9 @@ m m.layer_interact() ``` -### STAC layer +### STAC Layer -You can load SpatioTemporal Asset Catalog (STAC) data using the `add_stac_layer` method. +The SpatioTemporal Asset Catalog (STAC) standard organizes large satellite data collections. With `add_stac_layer`, this example loads Canadian satellite data, displaying both a panchromatic and an RGB layer from the same source. This approach allows easy switching between views. ```{code-cell} ipython3 m = leafmap.Map(style="streets") @@ -1175,6 +1299,8 @@ m m.layer_interact() ``` +For more control, the `collection` and `item` parameters let you specify specific assets, such as color infrared bands, for focused analysis. + ```{code-cell} ipython3 collection = "landsat-8-c2-l2" item = "LC08_L2SP_047027_20201204_02_T1" @@ -1195,11 +1321,14 @@ m.add_stac_layer( m ``` -## Interact with the map +## Interact with the Map + +Interactivity allows for a more tailored map experience. + -+++ +### Display a Non-Interactive Map -### Display a non-interactive map +To create a static map, set `interactive=False`. This disables all user interactions, making it ideal for static presentations. ```{code-cell} ipython3 m = leafmap.Map( @@ -1208,14 +1337,18 @@ m = leafmap.Map( m ``` -### Disable scroll zoom +### Disable Scroll Zoom + +Use `scroll_zoom=False` to prevent map zooming with the scroll wheel, maintaining a fixed zoom level. ```{code-cell} ipython3 m = leafmap.Map(center=[-122.65, 45.52], zoom=9, scroll_zoom=False, style="liberty") m ``` -### Fit bounds +### Fit Bounds + +The `fit_bounds` method focuses the map on a specified area. In this example, the map centers on Kenya. Additionally, a GeoJSON line is added to the map, and its bounds are automatically calculated with `geojson_bounds` for a custom zoom fit. ```{code-cell} ipython3 m = leafmap.Map(center=[-74.5, 40], zoom=9, style="liberty") @@ -1279,7 +1412,9 @@ bounds m.fit_bounds(bounds) ``` -### Restrict map panning to an area +### Restrict Map Panning to an Area + +To limit map panning to a specific area, define a bounding box. The map is then restricted within the bounds, ensuring users do not accidentally pan away from the area of interest. ```{code-cell} ipython3 bounds = [ @@ -1293,7 +1428,9 @@ m = leafmap.Map(center=[-73.9978, 40.7209], zoom=13, max_bounds=bounds, style="l m ``` -### Fly to +### Fly To + +The `fly_to` method smoothly navigates to specified coordinates. Parameters like speed and zoom provide control over the fly-to effect. This example flies from the initial map location to New York City. ```{code-cell} ipython3 m = leafmap.Map(center=[-2.242467, 53.478122], zoom=9, style="liberty") @@ -1323,7 +1460,9 @@ options = { m.fly_to(**options) ``` -### Jump to a series of locations +### Jump to a Series of Locations + +Using `jump_to`, you can navigate between multiple locations. This example sets up a list of coordinates representing cities and automatically pans to each one in sequence. Adding a delay between transitions enhances the animation effect. ```{code-cell} ipython3 import time @@ -1377,7 +1516,9 @@ for index, city in enumerate(cities["features"]): m.jump_to({"center": coords}) ``` -### Get coordinates of the mouse pointer +### Get Coordinates of the Mouse Pointer + +With widgets, you can display the current mouse pointer coordinates as it moves across the map. This is useful for precision mapping tasks where knowing exact coordinates is essential. ```{code-cell} ipython3 import ipywidgets as widgets @@ -1406,11 +1547,13 @@ m.observe(log_lng_lat, names="clicked") output ``` -## Customize layer styles +## Customizing Layer Styles -+++ +Customizing layer styles enables personalized map designs. -### Change layer color +### Change Layer Color + +Use `style_layer_interact` to interactively change the color of map layers, such as water bodies. This method provides an interactive palette for immediate style changes. ```{code-cell} ipython3 m = leafmap.Map(center=[12.338, 45.4385], zoom=17, style="streets") @@ -1421,7 +1564,9 @@ m m.style_layer_interact(id="water") ``` -### Change case of labels +### Change Case of Labels + +This example displays labels in upper or lower case based on their properties. The layout options let you customize font, size, and alignment. For example, facility names are displayed in uppercase, and comments are displayed in lowercase for contrast. ```{code-cell} ipython3 m = leafmap.Map(center=[-116.231, 43.604], zoom=11, style="streets") @@ -1454,7 +1599,9 @@ m.add_layer(layer) m ``` -### Variable label placement +### Variable Label Placement + +Labels can be automatically positioned around features with `text-variable-anchor`. This example defines multiple anchor points for labels to avoid overlap and ensure clear visibility. The radial offset and auto-justification properties create a professional, clutter-free appearance around points of interest. ```{code-cell} ipython3 m = leafmap.Map(center=[-77.04, 38.907], zoom=11, style="streets") @@ -1522,11 +1669,15 @@ m m.rotate_to(bearing=180, options={"duration": 10000}) ``` -## Add custom components +## Adding Custom Components -You can add custom components to the map, including images, videos, text, color bar, and legend. +Enhance your maps by adding custom components such as images, videos, text, color bars, and legends. -### Add image +### Add Image + +You can add an image as an overlay or as an icon for a specific layer. For instance: +- Overlaying an image directly on the map at the "bottom-right" corner. +- Adding an icon image to a feature. In the example, a "cat" image is loaded, and a marker is added at coordinates `[0, 0]` with a label "I love kitty!" above the icon. ```{code-cell} ipython3 m = leafmap.Map(center=[0.349419, -1.80921], zoom=3, style="streets") @@ -1574,6 +1725,8 @@ m.add_html(content, bg_color="transparent", position="bottom-right") m ``` +To customize icons, you can also generate icon data with `numpy`, creating unique color gradients and using it as a map icon. + ```{code-cell} ipython3 import numpy as np @@ -1625,7 +1778,11 @@ m.add_layer(layer) m ``` -### Add text +### Add Text + +Add text annotations to the map, specifying parameters like font size and background color. For example: +- Text "Hello World" in the bottom-right corner with a transparent background. +- "Awesome Text!" in the top-left corner with a slightly opaque white background, making it stand out. ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="liberty") @@ -1638,6 +1795,8 @@ m ### Add GIF +GIFs can be added as animated overlays to bring your map to life. Example: add a sloth GIF in the bottom-right and a second GIF in the bottom-left corner, with a text label indicating “I love sloth!” for added character. + ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") image = "https://i.imgur.com/KeiAsTv.gif" @@ -1651,6 +1810,8 @@ m ### Add HTML +Embed custom HTML content to display various HTML elements, such as emojis or stylized text. You can also adjust the font size and background transparency for better integration into the map design. + ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") html = """ @@ -1673,7 +1834,11 @@ m.add_html(html, bg_color="transparent") m ``` -### Add colorbar +### Add Colorbar + +Adding a color bar enhances data interpretation. In the example: +1. A Digital Elevation Model (DEM) is displayed with a color ramp from 0 to 1500 meters. +2. `add_colorbar` method is used to create a color bar with labels, adjusting its position, opacity, and orientation for optimal readability. ```{code-cell} ipython3 m = leafmap.Map(style="topo") @@ -1737,7 +1902,11 @@ m.add_colorbar( m ``` -### Add legend +### Add Legend + +Custom legends help users understand data classifications. Two methods are shown: +1. Using built-in legends, such as for NLCD (National Land Cover Database) or wetland types. +2. Custom legends are built with a dictionary of land cover types and colors. This legend provides descriptive color-coding for various land cover types, with configurable background opacity to blend with the map. ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="positron") @@ -1802,7 +1971,10 @@ m.add_legend( m ``` -### Add video +### Add Video + +Videos can be added with geographic context by specifying corner coordinates. Videos must be listed in multiple formats to ensure compatibility across browsers. The coordinates array should define the video’s location on the map in the order: top-left, top-right, bottom-right, and bottom-left. This is demonstrated by adding drone footage to a satellite map view, enhancing the user experience with real-world visuals. + 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. The `coordinates` array contains [longitude, latitude] pairs for the video corners listed in clockwise order: top left, top right, bottom right, bottom left. @@ -1845,9 +2017,11 @@ m ## PMTiles -Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format for fast and efficient rendering of vector tiles. +Leafmap supports the [PMTiles](https://protomaps.com/docs/pmtiles/) format, enabling efficient storage and fast rendering of vector tiles directly in the browser. + +### Protomaps Sample Data -### Protomaps sample data +Load Protomaps data in PMTiles format for fast, high-resolution vector map data rendering. Use `pmtiles_metadata()` to fetch details like layer names and map bounds, then style and add these tiles to your map. For instance, the example shows two layers: `buildings` styled in "steelblue" and `roads` styled in "black". ```{code-cell} ipython3 url = "https://open.gishub.org/data/pmtiles/protomaps_firenze.pmtiles" @@ -1902,13 +2076,9 @@ m m.layer_interact() ``` -### Overture data +### Source Cooperative Data -You can also visualize Overture data. Inspired by [overture-maps](https://github.com/tebben/overture-maps). - -+++ - -### Source Cooperative +Visualize the Google-Microsoft Open Buildings dataset, managed by VIDA, in PMTiles format. Fetch metadata to identify available layers, apply custom styles to the building footprints, and render them with semi-transparent colors for a clear visualization. Let's visualize the [Google-Microsoft Open Buildings - combined by VIDA](https://beta.source.coop/repositories/vida/google-microsoft-open-buildings/description). @@ -1961,7 +2131,7 @@ m.layer_interact() ### 3D PMTiles -Visualize the global building data in 3D. +Render global building data in 3D for a realistic, textured experience. Set building colors and extrusion heights to create visually compelling cityscapes. For example, apply color gradients and height scaling based on building attributes to differentiate buildings by their heights. ```{code-cell} ipython3 url = "https://data.source.coop/cholmes/overture/overture-buildings.pmtiles" @@ -2020,7 +2190,9 @@ m.add_layer_control() m ``` -### 3D buildings +### 3D Buildings + +For a simplified setup, the `add_overture_3d_buildings` function quickly adds 3D building data from Overture’s latest release to a basemap, creating depth and spatial realism on the map. ```{code-cell} ipython3 m = leafmap.Map( @@ -2032,7 +2204,11 @@ m.add_layer_control() m ``` -### 2D buildings +### 2D Buildings, Transportation, and Other Themes + +Switch between specific Overture themes, such as `buildings`, `transportation`, `places`, or `addresses`, depending on the data focus. Each theme overlays relevant features with adjustable opacity for thematic visualization, providing easy customization for urban planning or transportation studies. + +Building theme: ```{code-cell} ipython3 m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) @@ -2042,7 +2218,7 @@ m.add_layer_control() m ``` -### Transportation +Transportation theme: ```{code-cell} ipython3 m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) @@ -2052,7 +2228,7 @@ m.add_layer_control() m ``` -### Places +Places theme: ```{code-cell} ipython3 m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) @@ -2062,7 +2238,7 @@ m.add_layer_control() m ``` -### Addresses +Addresses theme: ```{code-cell} ipython3 m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) @@ -2072,7 +2248,7 @@ m.add_layer_control() m ``` -### Base +Base theme: ```{code-cell} ipython3 m = leafmap.Map(center=[-74.0095, 40.7046], zoom=16) @@ -2082,7 +2258,7 @@ m.add_layer_control() m ``` -### Divisions +Divisions themem: ```{code-cell} ipython3 m = leafmap.Map() @@ -2092,11 +2268,13 @@ m.add_layer_control() m ``` -## Deck.GL layers +## Deck.GL Layers + +Integrate interactive, high-performance visualization layers using Deck.GL. Leafmap’s `add_deck_layer` method supports multiple Deck.GL layer types, including Grid, GeoJSON, and Arc layers. -Deck.GL layers can be added to the map using the `add_deck_layer` method. +### Single Deck.GL Layer -### Single Deck.GL layer +Add a `GridLayer` to visualize point data, such as the density of bike parking in San Francisco. Customize the grid cell size, elevation, and color to represent data density visually and interactively. ```{code-cell} ipython3 m = leafmap.Map( @@ -2122,7 +2300,11 @@ m.add_deck_layers([deck_grid_layer], tooltip="Number of points: {{ count }}") m ``` -### Multiple Deck.GL layers +### Multiple Deck.GL Layers + +Combine layers like `GeoJsonLayer` and `ArcLayer` for complex visualizations. For example: +1. Use `GeoJsonLayer` to show airports with varying point sizes based on significance. +2. Use `ArcLayer` to connect selected airports with London, coloring arcs to represent different paths. ```{code-cell} ipython3 url = "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson" @@ -2175,9 +2357,14 @@ m.add_deck_layers( m ``` +The result is a rich, interactive visualization that highlights both point data and relational data connections, useful for airport connectivity or hub-and-spoke modeling. + + ## Google Earth Engine -You can use the Earth Engine Python API to load and visualize Earth Engine data. +Leafmap enables integration with the Google Earth Engine (GEE) Python API, allowing for powerful visualization of Earth Engine datasets directly on a Leafmap map. + +To add Earth Engine layers, initialize a map and use `add_ee_layer` to add specific datasets, such as ESA WorldCover data. A legend can be included using `add_legend` to provide a visual reference. ```{code-cell} ipython3 m = leafmap.Map( @@ -2193,7 +2380,7 @@ m m.layer_interact() ``` -We can also overlay other data layers on top of Earth Engine data layers. +You can overlay Earth Engine data with other 3D elements, like buildings, to create a multi-layered, interactive map. If you have an Earth Engine account, authenticate and initialize Earth Engine in your notebook by uncommenting the relevant code. ```{code-cell} ipython3 m = leafmap.Map( @@ -2226,7 +2413,9 @@ If you have an Earth Engine, you can uncomment the first two code blocks to add ## Animations -### Animate a line +### Animate a Line + +Using Leafmap’s animation capabilities, you can animate a line by updating the coordinates of a GeoJSON line feature in real-time. The sample includes data loaded from a CSV file, which is sorted and plotted sequentially to show the line’s movement. ```{code-cell} ipython3 import time @@ -2272,7 +2461,9 @@ for i in range(run_times): m.set_data("line", geojson) ``` -### Animate map camera around a point +### Animate the Map Camera Around a Point + +Control the map camera's rotation, zoom, and angle around a given point to create a smooth animation effect. This technique is especially useful for visualizing cityscapes or other 3D environments by making buildings appear dynamic. ```{code-cell} ipython3 m = leafmap.Map(center=[-87.62712, 41.89033], zoom=15, pitch=45, style="streets") @@ -2314,7 +2505,9 @@ m.add_layer(layer) m ``` -### Animate a point +### Animate a Point + +Animate a point along a circular path by computing points on a circle and updating the map. This is ideal for showing circular motion on the map, and can be customized for duration and frame rate to control the animation speed. ```{code-cell} ipython3 for degree in range(0, 180, 1): @@ -2370,7 +2563,9 @@ frame_rate = 30 # Frames per second animate_marker(duration, frame_rate, radius) ``` -### Animate a point along a route +### Animate a Point Along a Route + +Create a point that follows a route specified by GeoJSON coordinates. For added realism, include rotation properties to simulate the direction of movement along the path. This animation is useful for tracking paths, such as vehicle or drone routes. ```{code-cell} ipython3 m = leafmap.Map(center=[-100, 40], zoom=3, style="streets") @@ -2419,7 +2614,9 @@ for index, coordinate in enumerate(coordinates): time.sleep(0.05) ``` -### Update a feature in realtime +### Update a Feature in Real-Time + +For applications such as tracking or tracing a path in real time, load data from a source like GeoDataFrame, append coordinates incrementally to a line feature, and update the map display as the path extends. ```{code-cell} ipython3 import geopandas as gpd @@ -2467,7 +2664,7 @@ for coord in coordinates: ## To HTML -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. +Export the final map to HTML using the `to_html` method. This allows you to share interactive maps online while keeping your private API keys secure. Create public API keys restricted to your website’s domain for safety. Customize the HTML output, including dimensions, title, and embedding options. ```{code-cell} ipython3 # import os