diff --git a/docs/maplibre/add_colorbar.ipynb b/docs/maplibre/add_colorbar.ipynb new file mode 100644 index 0000000000..6c7591ab58 --- /dev/null +++ b/docs/maplibre/add_colorbar.ipynb @@ -0,0 +1,175 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/add_colorbar.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/add_colorbar.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Add a colorbar to the map**\n", + "\n", + "This source code of this example is adapted from the MapLibre GL JS example - [maplibre_xxx](https://maplibre.org/maplibre-gl-js/docs/examples/maplibre_xxx/).\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To run this notebook, you will need an [API key](https://docs.maptiler.com/cloud/api/authentication-key/) from [MapTiler](https://www.maptiler.com/cloud/). Once you have the API key, you can set it as an environment variable in your notebook or script as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")\n", + "style = f\"https://api.maptiler.com/maps/streets/style.json?key={MAPTILER_KEY}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=style)\n", + "m.add_cog_layer(\n", + " dem, name=\"DEM\", colormap_name=\"terrain\", rescale=\"0, 4000\", fit_bounds=True\n", + ")\n", + "m.add_colorbar(\n", + " cmap=\"terrain\", vmin=0, vmax=4000, label=\"Elevation (m)\", position=\"bottom-right\"\n", + ")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/84t0Sum.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=style)\n", + "m.add_cog_layer(\n", + " dem, name=\"DEM\", colormap_name=\"terrain\", rescale=\"0, 4000\", 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", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/B2VQoHe.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(style=style)\n", + "m.add_cog_layer(\n", + " dem, name=\"DEM\", colormap_name=\"terrain\", rescale=\"0, 4000\", 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", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/wsJsPr7.png)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/add_components.ipynb b/docs/maplibre/add_components.ipynb new file mode 100644 index 0000000000..65aafda5d1 --- /dev/null +++ b/docs/maplibre/add_components.ipynb @@ -0,0 +1,117 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/add_components.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/add_components.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Add components to the map**\n", + "\n", + "This notebook demonstrates how to add various components to the map, including legends, colorbars, text, and HTML elements.\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-100, 40], zoom=3, style=\"positron\")\n", + "\n", + "## Add a legend\n", + "url = \"https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2021_Land_Cover_L48/wms?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=NLCD_2021_Land_Cover_L48\"\n", + "m.add_wms_layer(url, 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", + "\n", + "# Add a colorbar\n", + "dem = \"https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif\"\n", + "m.add_cog_layer(\n", + " dem, name=\"DEM\", colormap_name=\"terrain\", rescale=\"0, 4000\", fit_bounds=False\n", + ")\n", + "m.add_colorbar(\n", + " cmap=\"terrain\", vmin=0, vmax=4000, label=\"Elevation (m)\", position=\"bottom-right\"\n", + ")\n", + "\n", + "# Add text\n", + "text = \"Awesome Map!\"\n", + "m.add_text(text, position=\"top-left\")\n", + "\n", + "# Add HTML content\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", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/ZWmiKAF.png)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/add_html.ipynb b/docs/maplibre/add_html.ipynb new file mode 100644 index 0000000000..25ba7b20ca --- /dev/null +++ b/docs/maplibre/add_html.ipynb @@ -0,0 +1,92 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/add_html.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/add_html.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Add HTML content to the map**\n", + "\n", + "This example shows how to add HTML content to the map using the `add_html()` method.\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "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", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/TgalNOv.png)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/add_legend.ipynb b/docs/maplibre/add_legend.ipynb new file mode 100644 index 0000000000..11a4b434e9 --- /dev/null +++ b/docs/maplibre/add_legend.ipynb @@ -0,0 +1,127 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/add_legend.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/add_legend.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Add a legend to the map**\n", + "\n", + "This notebook demonstrates how to add a legend to a map.\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "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?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=NLCD_2021_Land_Cover_L48\"\n", + "m.add_wms_layer(url, 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, + "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?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=NLCD_2021_Land_Cover_L48\"\n", + "m.add_wms_layer(url, 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" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/dy60trf.png)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/add_logo.ipynb b/docs/maplibre/add_logo.ipynb new file mode 100644 index 0000000000..3342aab145 --- /dev/null +++ b/docs/maplibre/add_logo.ipynb @@ -0,0 +1,89 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/add_logo.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/add_logo.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Add a logo to the map**\n", + "\n", + "This example shows how to add a logo to the map.\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "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, + "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": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/Pp9U4Li.png)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/add_text.ipynb b/docs/maplibre/add_text.ipynb new file mode 100644 index 0000000000..d551a6e10c --- /dev/null +++ b/docs/maplibre/add_text.ipynb @@ -0,0 +1,116 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/add_text.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/add_text.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Add text to the map**\n", + "\n", + "Add HTML text to the map.\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To run this notebook, you will need an [API key](https://docs.maptiler.com/cloud/api/authentication-key/) from [MapTiler](https://www.maptiler.com/cloud/). Once you have the API key, you can set it as an environment variable in your notebook or script as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")\n", + "style = f\"https://api.maptiler.com/maps/streets/style.json?key={MAPTILER_KEY}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-100, 40], zoom=3, style=style)\n", + "text = \"Hello World\"\n", + "m.add_text(text, fontsize=20, position=\"bottom-right\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-100, 40], zoom=3, style=style)\n", + "text = \"Awesome Text!\"\n", + "m.add_text(text, fontsize=25, bg_color=\"rgba(255, 255, 255, 0.8)\", position=\"top-left\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/UAtlh3r.png)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/layer_control.ipynb b/docs/maplibre/layer_control.ipynb new file mode 100644 index 0000000000..b04e07fa51 --- /dev/null +++ b/docs/maplibre/layer_control.ipynb @@ -0,0 +1,225 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=maplibre/layer_control.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/maplibre/layer_control.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Control layer visibility using the built-in layer control**\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install \"leafmap[maplibre]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import leafmap.maplibregl as leafmap" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To run this notebook, you will need an [API key](https://docs.maptiler.com/cloud/api/authentication-key/) from [MapTiler](https://www.maptiler.com/cloud/). Once you have the API key, you can set it as an environment variable in your notebook or script as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# os.environ[\"MAPTILER_KEY\"] = \"YOUR_API_KEY\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MAPTILER_KEY = leafmap.get_api_key(\"MAPTILER_KEY\")\n", + "style = f\"https://api.maptiler.com/maps/streets/style.json?key={MAPTILER_KEY}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "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, position=\"top-left\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/Il0NRId.gif)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(\n", + " center=[-123.13, 49.254], zoom=11, style=\"dark-matter\", pitch=45, bearing=0\n", + ")\n", + "url = \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json\"\n", + "paint_line = {\n", + " \"line-color\": \"white\",\n", + " \"line-width\": 2,\n", + "}\n", + "paint_fill = {\n", + " \"fill-extrusion-color\": {\n", + " \"property\": \"valuePerSqm\",\n", + " \"stops\": [\n", + " [0, \"grey\"],\n", + " [1000, \"yellow\"],\n", + " [5000, \"orange\"],\n", + " [10000, \"darkred\"],\n", + " [50000, \"lightblue\"],\n", + " ],\n", + " },\n", + " \"fill-extrusion-height\": [\"*\", 10, [\"sqrt\", [\"get\", \"valuePerSqm\"]]],\n", + " \"fill-extrusion-opacity\": 0.9,\n", + "}\n", + "m.add_geojson(url, layer_type=\"line\", paint=paint_line, name=\"blocks-line\")\n", + "m.add_geojson(url, layer_type=\"fill-extrusion\", paint=paint_fill, name=\"blocks-fill\")\n", + "m.add_layer_control(layer_ids=[\"blocks-line\", \"blocks-fill\"])\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m.layer_interact()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/FXUwvXf.gif)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[4.4562, 51.9066], zoom=14)\n", + "m.add_basemap(\"Esri.WorldImagery\")\n", + "url = \"https://storage.googleapis.com/ahp-research/overture/pmtiles/overture.pmtiles\"\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\": \"buildings\",\n", + " \"type\": \"fill\",\n", + " \"paint\": {\"fill-color\": \"#FFFFB3\", \"fill-opacity\": 0.5},\n", + " },\n", + " {\n", + " \"id\": \"places\",\n", + " \"source\": \"example_source\",\n", + " \"source-layer\": \"places\",\n", + " \"type\": \"fill\",\n", + " \"paint\": {\"fill-color\": \"#BEBADA\", \"fill-opacity\": 0.5},\n", + " },\n", + " {\n", + " \"id\": \"roads\",\n", + " \"source\": \"example_source\",\n", + " \"source-layer\": \"roads\",\n", + " \"type\": \"line\",\n", + " \"paint\": {\"line-color\": \"#FB8072\"},\n", + " },\n", + " ],\n", + "}\n", + "\n", + "m.add_pmtiles(\n", + " url,\n", + " style=style,\n", + " visible=True,\n", + " opacity=1.0,\n", + " tooltip=True,\n", + ")\n", + "m.set_zoom(14)\n", + "m.add_layer_control()\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m.layer_interact()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/BfkjBEJ.gif)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/maplibre/overview.md b/docs/maplibre/overview.md index ef7ad038e3..f9e1b41a89 100644 --- a/docs/maplibre/overview.md +++ b/docs/maplibre/overview.md @@ -26,12 +26,30 @@ Add a default marker to the map. [![](https://i.imgur.com/ufmqTzx.png)](https://leafmap.org/maplibre/add_a_marker) +## Add a colorbar + +Add a colorbar to the map. + +[![](https://i.imgur.com/84t0Sum.png)](https://leafmap.org/maplibre/add_colorbar) + +## Add components to the map + +Add various components to the map, including legends, colorbars, text, and HTML content. + +[![](https://i.imgur.com/ZWmiKAF.png)](https://leafmap.org/maplibre/add_components) + ## Add deck.gl layers Add deck.gl layers to the map. [![](https://i.imgur.com/rQR4687.png)](https://leafmap.org/maplibre/add_deckgl_layer) +## Add HTML content to the map + +Add HTML content to the map. + +[![](https://i.imgur.com/TgalNOv.png)](https://leafmap.org/maplibre/add_html) + ## Add an icon to the map Add an icon to the map from an external URL and use it in a symbol layer. @@ -44,6 +62,24 @@ Add an icon to the map that was generated at runtime. [![](https://i.imgur.com/qWWlnAm.png)](https://leafmap.org/maplibre/add_image_generated) +## Add a legend to the map + +Add a custom legend to the map. + +[![](https://i.imgur.com/dy60trf.png)](https://leafmap.org/maplibre/add_legend) + +## Add a logo to the map + +Add an image to the map as a logo. + +[![](https://i.imgur.com/Pp9U4Li.png)](https://leafmap.org/maplibre/add_logo) + +## Add text to the map + +Add text to the map. + +[![](https://i.imgur.com/UAtlh3r.png)](https://leafmap.org/maplibre/add_text) + ## Animate a line Animate a line by updating a GeoJSON source on each frame. @@ -242,6 +278,12 @@ Use setLayoutProperty to switch languages dynamically. [![](https://i.imgur.com/gIRDqQK.png)](https://leafmap.org/maplibre/language_switch) +## Add a layer control + +Control layer visibility with a layer control. + +[![](https://i.imgur.com/NffngdY.png)](https://leafmap.org/maplibre/layer_control) + ## Create a gradient line using an expression Use the line-gradient paint property and an expression to visualize distance from the starting point of a line. diff --git a/leafmap/maplibregl.py b/leafmap/maplibregl.py index dbd17ae63f..cdf33a11ed 100644 --- a/leafmap/maplibregl.py +++ b/leafmap/maplibregl.py @@ -239,6 +239,9 @@ def add_control( control = AttributionControl(**kwargs) elif control == "draw": self.add_draw_control(position=position, **kwargs) + elif control == "layers": + self.add_layer_control(position=position, **kwargs) + return else: print( "Control can only be one of the following: 'scale', 'fullscreen', 'geolocate', 'navigation', and 'draw'." @@ -1757,11 +1760,12 @@ def _read_image(self, image: str) -> Dict[str, Union[int, List[int]]]: def add_image( self, - id: str, - image: Union[str, Dict], + id: str = None, + image: Union[str, Dict] = None, width: int = None, height: int = None, coordinates: List[float] = None, + position: str = None, icon_size: float = 1.0, **kwargs: Any, ) -> None: @@ -1776,6 +1780,8 @@ def add_image( height (int, optional): The height of the image. Defaults to None. coordinates (List[float], optional): The longitude and latitude coordinates to place the image. + position (str, optional): The position of the image. Defaults to None. + Can be one of 'top-right', 'top-left', 'bottom-right', 'bottom-left'. icon_size (float, optional): The size of the icon. Defaults to 1.0. Returns: @@ -1783,47 +1789,57 @@ def add_image( """ import numpy as np - if isinstance(image, str): - image_dict = self._read_image(image) - elif isinstance(image, dict): - image_dict = image - elif isinstance(image, np.ndarray): - image_dict = { - "width": width, - "height": height, - "data": image.flatten().tolist(), - } + if id is None: + id = "image" + + if position is not None: + html = f'' + self.add_html(html, position=position, **kwargs) else: - raise ValueError( - "The image must be a URL, a local file path, or a numpy array." - ) - super().add_call("addImage", id, image_dict) - - if coordinates is not None: - - source = { - "type": "geojson", - "data": { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": coordinates}, - } - ], - }, - } + if isinstance(image, str): + image_dict = self._read_image(image) + elif isinstance(image, dict): + image_dict = image + elif isinstance(image, np.ndarray): + image_dict = { + "width": width, + "height": height, + "data": image.flatten().tolist(), + } + else: + raise ValueError( + "The image must be a URL, a local file path, or a numpy array." + ) + super().add_call("addImage", id, image_dict) + + if coordinates is not None: + + source = { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": coordinates, + }, + } + ], + }, + } - self.add_source("image_point", source) + self.add_source("image_point", source) - kwargs["id"] = "image_points" - kwargs["type"] = "symbol" - kwargs["source"] = "image_point" - if "layout" not in kwargs: - kwargs["layout"] = {} - kwargs["layout"]["icon-image"] = id - kwargs["layout"]["icon-size"] = icon_size - self.add_layer(kwargs) + kwargs["id"] = "image_points" + kwargs["type"] = "symbol" + kwargs["source"] = "image_point" + if "layout" not in kwargs: + kwargs["layout"] = {} + kwargs["layout"]["icon-image"] = id + kwargs["layout"]["icon-size"] = icon_size + self.add_layer(kwargs) def to_streamlit( self, @@ -2139,6 +2155,373 @@ def find_first_symbol_layer(self) -> Optional[Dict]: return layer return None + def add_text( + self, + text: str, + fontsize: int = 20, + fontcolor: str = "black", + bold: bool = False, + padding: str = "5px", + bg_color: str = "white", + border_radius: str = "5px", + position: str = "bottom-right", + **kwargs: Any, + ) -> None: + """ + Adds text to the map with customizable styling. + + This method allows adding a text widget to the map with various styling options such as font size, color, + background color, and more. The text's appearance can be further customized using additional CSS properties + passed through kwargs. + + Args: + text (str): The text to add to the map. + fontsize (int, optional): The font size of the text. Defaults to 20. + fontcolor (str, optional): The color of the text. Defaults to "black". + bold (bool, optional): If True, the text will be bold. Defaults to False. + padding (str, optional): The padding around the text. Defaults to "5px". + bg_color (str, optional): The background color of the text widget. Defaults to "white". + To make the background transparent, set this to "transparent". + To make the background half transparent, set this to "rgba(255, 255, 255, 0.5)". + border_radius (str, optional): The border radius of the text widget. Defaults to "5px". + position (str, optional): The position of the text widget on the map. Defaults to "bottom-right". + **kwargs (Any): Additional CSS properties to apply to the text widget. + + Returns: + None + """ + from maplibre.controls import InfoBoxControl + + if bg_color == "transparent" and "box-shadow" not in kwargs: + kwargs["box-shadow"] = "none" + + css_text = f"""font-size: {fontsize}px; color: {fontcolor}; + font-weight: {'bold' if bold else 'normal'}; padding: {padding}; + background-color: {bg_color}; border-radius: {border_radius};""" + + for key, value in kwargs.items(): + css_text += f" {key.replace('_', '-')}: {value};" + + control = InfoBoxControl(content=text, css_text=css_text) + self.add_control(control, position=position) + + def add_html( + self, + html: str, + bg_color: str = "white", + position: str = "bottom-right", + **kwargs: Union[str, int, float], + ) -> None: + """ + Add HTML content to the map. + + This method allows for the addition of arbitrary HTML content to the map, which can be used to display + custom information or controls. The background color and position of the HTML content can be customized. + + Args: + html (str): The HTML content to add. + bg_color (str, optional): The background color of the HTML content. Defaults to "white". + To make the background transparent, set this to "transparent". + To make the background half transparent, set this to "rgba(255, 255, 255, 0.5)". + position (str, optional): The position of the HTML content on the map. Can be one of "top-left", + "top-right", "bottom-left", "bottom-right". Defaults to "bottom-right". + **kwargs: Additional keyword arguments for future use. + + Returns: + None + """ + # Check if an HTML string contains local images and convert them to base64. + html = check_html_string(html) + self.add_text(html, position=position, bg_color=bg_color, **kwargs) + + def add_legend( + self, + title: str = "Legend", + legend_dict: Optional[Dict[str, str]] = None, + labels: Optional[List[str]] = None, + colors: Optional[List[str]] = None, + fontsize: int = 15, + bg_color: str = "white", + position: str = "bottom-right", + builtin_legend: Optional[str] = None, + **kwargs: Union[str, int, float], + ) -> None: + """ + Adds a legend to the map. + + This method allows for the addition of a legend to the map. The legend can be customized with a title, + labels, colors, and more. A built-in legend can also be specified. + + Args: + title (str, optional): The title of the legend. Defaults to "Legend". + legend_dict (Optional[Dict[str, str]], optional): A dictionary with legend items as keys and colors as values. + If provided, `labels` and `colors` will be ignored. Defaults to None. + labels (Optional[List[str]], optional): A list of legend labels. Defaults to None. + colors (Optional[List[str]], optional): A list of colors corresponding to the labels. Defaults to None. + fontsize (int, optional): The font size of the legend text. Defaults to 15. + bg_color (str, optional): The background color of the legend. Defaults to "white". + To make the background transparent, set this to "transparent". + To make the background half transparent, set this to "rgba(255, 255, 255, 0.5)". + position (str, optional): The position of the legend on the map. Can be one of "top-left", + "top-right", "bottom-left", "bottom-right". Defaults to "bottom-right". + builtin_legend (Optional[str], optional): The name of a built-in legend to use. Defaults to None. + **kwargs: Additional keyword arguments for future use. + + Returns: + None + """ + import pkg_resources + from .legends import builtin_legends + + pkg_dir = os.path.dirname( + pkg_resources.resource_filename("leafmap", "leafmap.py") + ) + legend_template = os.path.join(pkg_dir, "data/template/legend.html") + + if not os.path.exists(legend_template): + print("The legend template does not exist.") + return + + if labels is not None: + if not isinstance(labels, list): + print("The legend keys must be a list.") + return + else: + labels = ["One", "Two", "Three", "Four", "etc"] + + if colors is not None: + if not isinstance(colors, list): + print("The legend colors must be a list.") + return + elif all(isinstance(item, tuple) for item in colors): + try: + colors = [rgb_to_hex(x) for x in colors] + except Exception as e: + print(e) + elif all((item.startswith("#") and len(item) == 7) for item in colors): + pass + elif all((len(item) == 6) for item in colors): + pass + else: + print("The legend colors must be a list of tuples.") + return + else: + colors = [ + "#8DD3C7", + "#FFFFB3", + "#BEBADA", + "#FB8072", + "#80B1D3", + ] + + if len(labels) != len(colors): + print("The legend keys and values must be the same length.") + return + + allowed_builtin_legends = builtin_legends.keys() + if builtin_legend is not None: + if builtin_legend not in allowed_builtin_legends: + print( + "The builtin legend must be one of the following: {}".format( + ", ".join(allowed_builtin_legends) + ) + ) + return + else: + legend_dict = builtin_legends[builtin_legend] + labels = list(legend_dict.keys()) + colors = list(legend_dict.values()) + + if legend_dict is not None: + if not isinstance(legend_dict, dict): + print("The legend dict must be a dictionary.") + return + else: + labels = list(legend_dict.keys()) + colors = list(legend_dict.values()) + if all(isinstance(item, tuple) for item in colors): + try: + colors = [rgb_to_hex(x) for x in colors] + except Exception as e: + print(e) + + allowed_positions = [ + "top-left", + "top-right", + "bottom-left", + "bottom-right", + ] + if position not in allowed_positions: + print( + "The position must be one of the following: {}".format( + ", ".join(allowed_positions) + ) + ) + return + + header = [] + content = [] + footer = [] + + with open(legend_template) as f: + lines = f.readlines() + lines[3] = lines[3].replace("Legend", title) + header = lines[:6] + footer = lines[11:] + + for index, key in enumerate(labels): + color = colors[index] + if not color.startswith("#"): + color = "#" + color + item = "
  • {}
  • \n".format( + color, key + ) + content.append(item) + + legend_html = header + content + footer + legend_text = "".join(legend_html) + + self.add_html( + legend_text, + fontsize=fontsize, + bg_color=bg_color, + position=position, + **kwargs, + ) + + def add_colorbar( + self, + width: Optional[float] = 3.0, + height: Optional[float] = 0.2, + vmin: Optional[float] = 0, + vmax: Optional[float] = 1.0, + palette: Optional[List[str]] = None, + vis_params: Optional[Dict[str, Union[str, float, int]]] = None, + cmap: Optional[str] = "gray", + discrete: Optional[bool] = False, + label: Optional[str] = None, + label_size: Optional[int] = 10, + label_weight: Optional[str] = "normal", + tick_size: Optional[int] = 8, + bg_color: Optional[str] = "white", + orientation: Optional[str] = "horizontal", + dpi: Optional[Union[str, float]] = "figure", + transparent: Optional[bool] = False, + position: str = "bottom-right", + **kwargs, + ) -> str: + """ + Add a colorbar to the map. + + This function uses matplotlib to generate a colorbar, saves it as a PNG file, and adds it to the map using + the Map.add_html() method. The colorbar can be customized in various ways including its size, color palette, + label, and orientation. + + Args: + width (Optional[float]): Width of the colorbar in inches. Defaults to 3.0. + height (Optional[float]): Height of the colorbar in inches. Defaults to 0.2. + vmin (Optional[float]): Minimum value of the colorbar. Defaults to 0. + vmax (Optional[float]): Maximum value of the colorbar. Defaults to 1.0. + palette (Optional[List[str]]): List of colors or a colormap name for the colorbar. Defaults to None. + vis_params (Optional[Dict[str, Union[str, float, int]]]): Visualization parameters as a dictionary. + cmap (Optional[str]): Matplotlib colormap name. Defaults to "gray". + discrete (Optional[bool]): Whether to create a discrete colorbar. Defaults to False. + label (Optional[str]): Label for the colorbar. Defaults to None. + label_size (Optional[int]): Font size for the colorbar label. Defaults to 10. + label_weight (Optional[str]): Font weight for the colorbar label. Defaults to "normal". + tick_size (Optional[int]): Font size for the colorbar tick labels. Defaults to 8. + bg_color (Optional[str]): Background color for the colorbar. Defaults to "white". + orientation (Optional[str]): Orientation of the colorbar ("vertical" or "horizontal"). Defaults to "horizontal". + dpi (Optional[Union[str, float]]): Resolution in dots per inch. If 'figure', uses the figure's dpi value. Defaults to "figure". + transparent (Optional[bool]): Whether the background is transparent. Defaults to False. + position (str): Position of the colorbar on the map. Defaults to "bottom-right". + **kwargs: Additional keyword arguments passed to matplotlib.pyplot.savefig(). + + Returns: + str: Path to the generated colorbar image. + """ + + if transparent: + bg_color = "transparent" + + colorbar = save_colorbar( + None, + width, + height, + vmin, + vmax, + palette, + vis_params, + cmap, + discrete, + label, + label_size, + label_weight, + tick_size, + bg_color, + orientation, + dpi, + transparent, + show_colorbar=False, + ) + + html = f'' + + self.add_html(html, bg_color=bg_color, position=position, **kwargs) + + def add_layer_control( + self, + layer_ids: Optional[List[str]] = None, + theme: str = "default", + css_text: Optional[str] = None, + position: str = "top-left", + bg_layers: Optional[Union[bool, List[str]]] = False, + ) -> None: + """ + Adds a layer control to the map. + + This function creates and adds a layer switcher control to the map, allowing users to toggle the visibility + of specified layers. The appearance and functionality of the layer control can be customized with parameters + such as theme, CSS styling, and position on the map. + + Args: + layer_ids (Optional[List[str]]): A list of layer IDs to include in the control. If None, all layers + in the map will be included. Defaults to None. + theme (str): The theme for the layer switcher control. Can be "default" or other custom themes. Defaults to "default". + css_text (Optional[str]): Custom CSS text for styling the layer control. If None, a default style will be applied. + Defaults to None. + position (str): The position of the layer control on the map. Can be "top-left", "top-right", "bottom-left", + or "bottom-right". Defaults to "top-left". + bg_layers (bool): If True, background layers will be included in the control. Defaults to False. + + Returns: + None + """ + from maplibre.controls import LayerSwitcherControl + + if layer_ids is None: + layer_ids = list(self.layer_dict.keys()) + if layer_ids[0] == "background": + layer_ids = layer_ids[1:] + + if isinstance(bg_layers, list): + layer_ids = bg_layers + layer_ids + elif bg_layers: + background_ids = list(self.style_dict.keys()) + layer_ids = background_ids + layer_ids + + if css_text is None: + css_text = "padding: 5px; border: 1px solid darkgrey; border-radius: 4px;" + + if len(layer_ids) > 0: + + control = LayerSwitcherControl( + layer_ids=layer_ids, + theme=theme, + css_text=css_text, + ) + self.add_control(control, position=position) + class Container(v.Container): diff --git a/mkdocs.yml b/mkdocs.yml index 74b92098d6..3e6866fe84 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -142,9 +142,15 @@ nav: - maplibre/3d_indoor_mapping.ipynb - maplibre/3d_terrain.ipynb - maplibre/add_a_marker.ipynb + - maplibre/add_colorbar.ipynb + - maplibre/add_components.ipynb - maplibre/add_deckgl_layer.ipynb + - maplibre/add_html.ipynb - maplibre/add_image.ipynb - maplibre/add_image_generated.ipynb + - maplibre/add_legend.ipynb + - maplibre/add_logo.ipynb + - maplibre/add_text.ipynb - maplibre/animate_a_line.ipynb - maplibre/animate_camera_around_point.ipynb - maplibre/animate_images.ipynb @@ -178,6 +184,7 @@ nav: - maplibre/interactive_false.ipynb - maplibre/jump_to.ipynb - maplibre/language_switch.ipynb + - maplibre/layer_control.ipynb - maplibre/line_gradient.ipynb - maplibre/live_geojson.ipynb - maplibre/live_update_feature.ipynb