From f29c6da9278fa73d3248abdda37c8c3a9d7b1c54 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Wed, 4 Aug 2021 22:31:23 -0400 Subject: [PATCH] Revamped basemap module #91 --- docs/notebooks/00_key_features.ipynb | 6 +- docs/notebooks/02_using_basemaps.ipynb | 1 + docs/notebooks/12_split_map.ipynb | 2 +- docs/notebooks/16_heremap.ipynb | 2 +- examples/notebooks/00_key_features.ipynb | 6 +- examples/notebooks/02_using_basemaps.ipynb | 1 + examples/notebooks/12_split_map.ipynb | 2 +- examples/notebooks/16_heremap.ipynb | 2 +- leafmap/basemaps.py | 843 +++++++++------------ leafmap/common.py | 8 +- leafmap/leafmap.py | 60 +- leafmap/toolbar.py | 42 +- requirements.txt | 3 +- 13 files changed, 414 insertions(+), 564 deletions(-) diff --git a/docs/notebooks/00_key_features.ipynb b/docs/notebooks/00_key_features.ipynb index 93e0ce1e44..bbea69de0b 100644 --- a/docs/notebooks/00_key_features.ipynb +++ b/docs/notebooks/00_key_features.ipynb @@ -138,8 +138,8 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(google_map=\"TERRAIN\")\n", - "m.add_basemap(\"Esri National Geographic\")\n", + "m = leafmap.Map()\n", + "m.add_basemap(\"Esri.NatGeoWorldMap\")\n", "m" ] }, @@ -432,7 +432,7 @@ "outputs": [], "source": [ "m = leafmap.Map()\n", - "m.add_basemap(\"Esri Standard\")\n", + "m.add_basemap(\"Esri.NatGeoWorldMap\")\n", "m" ] }, diff --git a/docs/notebooks/02_using_basemaps.ipynb b/docs/notebooks/02_using_basemaps.ipynb index 02484bcd0b..9114d8523d 100644 --- a/docs/notebooks/02_using_basemaps.ipynb +++ b/docs/notebooks/02_using_basemaps.ipynb @@ -80,6 +80,7 @@ "source": [ "m = leafmap.Map()\n", "m.add_basemap(\"HYBRID\")\n", + "m.add_basemap(\"Esri.NatGeoWorldMap\")\n", "m" ] }, diff --git a/docs/notebooks/12_split_map.ipynb b/docs/notebooks/12_split_map.ipynb index bd325914c3..8423edd347 100644 --- a/docs/notebooks/12_split_map.ipynb +++ b/docs/notebooks/12_split_map.ipynb @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "print(leafmap.basemap_tiles.keys())" + "print(leafmap.leafmap_basemaps.keys())" ] }, { diff --git a/docs/notebooks/16_heremap.ipynb b/docs/notebooks/16_heremap.ipynb index 93dd7590d7..38f35f1df0 100644 --- a/docs/notebooks/16_heremap.ipynb +++ b/docs/notebooks/16_heremap.ipynb @@ -184,7 +184,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.add_basemap(basemap=\"Esri Topo World\")" + "m.add_basemap(basemap=\"Esri.WorldTopoMap\")" ] }, { diff --git a/examples/notebooks/00_key_features.ipynb b/examples/notebooks/00_key_features.ipynb index 93e0ce1e44..bbea69de0b 100644 --- a/examples/notebooks/00_key_features.ipynb +++ b/examples/notebooks/00_key_features.ipynb @@ -138,8 +138,8 @@ "metadata": {}, "outputs": [], "source": [ - "m = leafmap.Map(google_map=\"TERRAIN\")\n", - "m.add_basemap(\"Esri National Geographic\")\n", + "m = leafmap.Map()\n", + "m.add_basemap(\"Esri.NatGeoWorldMap\")\n", "m" ] }, @@ -432,7 +432,7 @@ "outputs": [], "source": [ "m = leafmap.Map()\n", - "m.add_basemap(\"Esri Standard\")\n", + "m.add_basemap(\"Esri.NatGeoWorldMap\")\n", "m" ] }, diff --git a/examples/notebooks/02_using_basemaps.ipynb b/examples/notebooks/02_using_basemaps.ipynb index 02484bcd0b..9114d8523d 100644 --- a/examples/notebooks/02_using_basemaps.ipynb +++ b/examples/notebooks/02_using_basemaps.ipynb @@ -80,6 +80,7 @@ "source": [ "m = leafmap.Map()\n", "m.add_basemap(\"HYBRID\")\n", + "m.add_basemap(\"Esri.NatGeoWorldMap\")\n", "m" ] }, diff --git a/examples/notebooks/12_split_map.ipynb b/examples/notebooks/12_split_map.ipynb index bd325914c3..8423edd347 100644 --- a/examples/notebooks/12_split_map.ipynb +++ b/examples/notebooks/12_split_map.ipynb @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "print(leafmap.basemap_tiles.keys())" + "print(leafmap.leafmap_basemaps.keys())" ] }, { diff --git a/examples/notebooks/16_heremap.ipynb b/examples/notebooks/16_heremap.ipynb index 93dd7590d7..38f35f1df0 100644 --- a/examples/notebooks/16_heremap.ipynb +++ b/examples/notebooks/16_heremap.ipynb @@ -184,7 +184,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.add_basemap(basemap=\"Esri Topo World\")" + "m.add_basemap(basemap=\"Esri.WorldTopoMap\")" ] }, { diff --git a/leafmap/basemaps.py b/leafmap/basemaps.py index ce3786c696..47aeebe539 100644 --- a/leafmap/basemaps.py +++ b/leafmap/basemaps.py @@ -1,6 +1,4 @@ -"""Module for basemaps. Each basemap is defined as item in the ee_basemaps dictionary. For example, to access Google basemaps, use the following: - -ee_basemaps['ROADMAP'], ee_basemaps['SATELLITE'], ee_basemaps['HYBRID']. +"""Module for basemaps. More WMS basemaps can be found at the following websites: @@ -11,537 +9,378 @@ 3. FWS NWI Wetlands data: https://www.fws.gov/wetlands/Data/Web-Map-Services.html """ +import collections import os import folium -from box import Box -from ipyleaflet import TileLayer, WMSLayer, basemap_to_tiles -import ipyleaflet.basemaps as ipybasemaps import here_map_widget -from here_map_widget import ImageTileProvider, DefaultLayers, DefaultLayerNames - +import ipyleaflet +import xyzservices.providers as xyz +from box import Box +from .common import planet_tiles -leaf_basemaps = { - "OpenStreetMap": TileLayer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution="OpenStreetMap", - name="OpenStreetMap", - ), - "ROADMAP": TileLayer( - url="https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Maps", - ), - "SATELLITE": TileLayer( - url="https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", - ), - "TERRAIN": TileLayer( - url="https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Terrain", - ), - "HYBRID": TileLayer( - url="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", - ), - "ESRI": TileLayer( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Satellite", - ), - "Esri Ocean": TileLayer( - url="https://services.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Ocean", - ), - "Esri Satellite": TileLayer( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Satellite", - ), - "Esri Standard": TileLayer( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Standard", - ), - "Esri Terrain": TileLayer( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Terrain", - ), - "Esri Transportation": TileLayer( - url="https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Transportation", - ), - "Esri Topo World": TileLayer( - url="https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Topo World", - ), - "Esri National Geographic": TileLayer( - url="http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri National Geographic", - ), - "Esri Shaded Relief": TileLayer( - url="https://services.arcgisonline.com/arcgis/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Shaded Relief", - ), - "Esri Physical Map": TileLayer( - url="https://services.arcgisonline.com/arcgis/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Physical Map", - ), - "FWS NWI Wetlands": WMSLayer( - url="https://www.fws.gov/wetlands/arcgis/services/Wetlands/MapServer/WMSServer?", - layers="1", - name="FWS NWI Wetlands", - attribution="FWS", - format="image/png", - transparent=True, - ), - "FWS NWI Wetlands Raster": WMSLayer( - url="https://www.fws.gov/wetlands/arcgis/services/Wetlands_Raster/ImageServer/WMSServer?", - layers="0", - name="FWS NWI Wetlands Raster", - attribution="FWS", - format="image/png", - transparent=True, - ), - "Google Maps": TileLayer( - url="https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Maps", - ), - "Google Satellite": TileLayer( - url="https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", - ), - "Google Terrain": TileLayer( - url="https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Terrain", - ), - "Google Satellite Hybrid": TileLayer( - url="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", - ), - "NLCD 2016 CONUS Land Cover": WMSLayer( - url="https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Land_Cover_L48/wms?", - layers="NLCD_2016_Land_Cover_L48", - name="NLCD 2016 CONUS Land Cover", - attribution="MRLC", - format="image/png", - transparent=True, - ), - "NLCD 2013 CONUS Land Cover": WMSLayer( - url="https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2013_Land_Cover_L48/wms?", - layers="NLCD_2013_Land_Cover_L48", - name="NLCD 2013 CONUS Land Cover", - attribution="MRLC", - format="image/png", - transparent=True, - ), - "NLCD 2011 CONUS Land Cover": WMSLayer( - url="https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2011_Land_Cover_L48/wms?", - layers="NLCD_2011_Land_Cover_L48", - name="NLCD 2011 CONUS Land Cover", - attribution="MRLC", - format="image/png", - transparent=True, - ), - "NLCD 2008 CONUS Land Cover": WMSLayer( - url="https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2008_Land_Cover_L48/wms?", - layers="NLCD_2008_Land_Cover_L48", - name="NLCD 2008 CONUS Land Cover", - attribution="MRLC", - format="image/png", - transparent=True, - ), - "NLCD 2006 CONUS Land Cover": WMSLayer( - url="https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2006_Land_Cover_L48/wms?", - layers="NLCD_2006_Land_Cover_L48", - name="NLCD 2006 CONUS Land Cover", - attribution="MRLC", - format="image/png", - transparent=True, - ), - "NLCD 2004 CONUS Land Cover": WMSLayer( - url="https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2004_Land_Cover_L48/wms?", - layers="NLCD_2004_Land_Cover_L48", - name="NLCD 2004 CONUS Land Cover", - attribution="MRLC", - format="image/png", - transparent=True, - ), - "NLCD 2001 CONUS Land Cover": WMSLayer( - url="https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2001_Land_Cover_L48/wms?", - layers="NLCD_2001_Land_Cover_L48", - name="NLCD 2001 CONUS Land Cover", - attribution="MRLC", - format="image/png", - transparent=True, - ), - "USGS NAIP Imagery": WMSLayer( - url="https://services.nationalmap.gov/arcgis/services/USGSNAIPImagery/ImageServer/WMSServer?", - layers="0", - name="USGS NAIP Imagery", - attribution="USGS", - format="image/png", - transparent=True, - ), - "USGS Hydrography": WMSLayer( - url="https://basemap.nationalmap.gov/arcgis/services/USGSHydroCached/MapServer/WMSServer?", - layers="0", - name="USGS Hydrography", - attribution="USGS", - format="image/png", - transparent=True, - ), - "USGS 3DEP Elevation": WMSLayer( - url="https://elevation.nationalmap.gov/arcgis/services/3DEPElevation/ImageServer/WMSServer?", - layers="33DEPElevation:Hillshade Elevation Tinted", - name="USGS 3DEP Elevation", - attribution="USGS", - format="image/png", - transparent=True, - ), +# Custom XYZ tile services. +xyz_tiles = { + "OpenStreetMap": { + "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + "attribution": "OpenStreetMap", + "name": "OpenStreetMap", + }, + "ROADMAP": { + "url": "https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}", + "attribution": "Google", + "name": "Google Maps", + }, + "SATELLITE": { + "url": "https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}", + "attribution": "Google", + "name": "Google Satellite", + }, + "TERRAIN": { + "url": "https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}", + "attribution": "Google", + "name": "Google Terrain", + }, + "HYBRID": { + "url": "https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", + "attribution": "Google", + "name": "Google Satellite", + }, } +# Custom WMS tile services. +wms_tiles = { + "FWS NWI Wetlands": { + "url": "https://www.fws.gov/wetlands/arcgis/services/Wetlands/MapServer/WMSServer?", + "layers": "1", + "name": "FWS NWI Wetlands", + "attribution": "FWS", + "format": "image/png", + "transparent": True, + }, + "FWS NWI Wetlands Raster": { + "url": "https://www.fws.gov/wetlands/arcgis/services/Wetlands_Raster/ImageServer/WMSServer?", + "layers": "0", + "name": "FWS NWI Wetlands Raster", + "attribution": "FWS", + "format": "image/png", + "transparent": True, + }, + "NLCD 2016 CONUS Land Cover": { + "url": "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Land_Cover_L48/wms?", + "layers": "NLCD_2016_Land_Cover_L48", + "name": "NLCD 2016 CONUS Land Cover", + "attribution": "MRLC", + "format": "image/png", + "transparent": True, + }, + "NLCD 2013 CONUS Land Cover": { + "url": "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2013_Land_Cover_L48/wms?", + "layers": "NLCD_2013_Land_Cover_L48", + "name": "NLCD 2013 CONUS Land Cover", + "attribution": "MRLC", + "format": "image/png", + "transparent": True, + }, + "NLCD 2011 CONUS Land Cover": { + "url": "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2011_Land_Cover_L48/wms?", + "layers": "NLCD_2011_Land_Cover_L48", + "name": "NLCD 2011 CONUS Land Cover", + "attribution": "MRLC", + "format": "image/png", + "transparent": True, + }, + "NLCD 2008 CONUS Land Cover": { + "url": "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2008_Land_Cover_L48/wms?", + "layers": "NLCD_2008_Land_Cover_L48", + "name": "NLCD 2008 CONUS Land Cover", + "attribution": "MRLC", + "format": "image/png", + "transparent": True, + }, + "NLCD 2006 CONUS Land Cover": { + "url": "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2006_Land_Cover_L48/wms?", + "layers": "NLCD_2006_Land_Cover_L48", + "name": "NLCD 2006 CONUS Land Cover", + "attribution": "MRLC", + "format": "image/png", + "transparent": True, + }, + "NLCD 2004 CONUS Land Cover": { + "url": "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2004_Land_Cover_L48/wms?", + "layers": "NLCD_2004_Land_Cover_L48", + "name": "NLCD 2004 CONUS Land Cover", + "attribution": "MRLC", + "format": "image/png", + "transparent": True, + }, + "NLCD 2001 CONUS Land Cover": { + "url": "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2001_Land_Cover_L48/wms?", + "layers": "NLCD_2001_Land_Cover_L48", + "name": "NLCD 2001 CONUS Land Cover", + "attribution": "MRLC", + "format": "image/png", + "transparent": True, + }, + "USGS NAIP Imagery": { + "url": "https://services.nationalmap.gov/arcgis/services/USGSNAIPImagery/ImageServer/WMSServer?", + "layers": "0", + "name": "USGS NAIP Imagery", + "attribution": "USGS", + "format": "image/png", + "transparent": True, + }, + "USGS Hydrography": { + "url": "https://basemap.nationalmap.gov/arcgis/services/USGSHydroCached/MapServer/WMSServer?", + "layers": "0", + "name": "USGS Hydrography", + "attribution": "USGS", + "format": "image/png", + "transparent": True, + }, + "USGS 3DEP Elevation": { + "url": "https://elevation.nationalmap.gov/arcgis/services/3DEPElevation/ImageServer/WMSServer?", + "layers": "33DEPElevation:Hillshade Elevation Tinted", + "name": "USGS 3DEP Elevation", + "attribution": "USGS", + "format": "image/png", + "transparent": True, + }, +} -folium_basemaps = { - "ROADMAP": folium.TileLayer( - tiles="https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}", - attr="Google", - name="Google Maps", - overlay=True, - control=True, +# Built-in heremap tile services. +here_tiles = { + "HERE_RASTER_NORMAL_MAP": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.normal.map ), - "SATELLITE": folium.TileLayer( - tiles="https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}", - attr="Google", - name="Google Satellite", - overlay=True, - control=True, + "HERE_RASTER_NORMAL_BASE": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.normal.base ), - "TERRAIN": folium.TileLayer( - tiles="https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}", - attr="Google", - name="Google Terrain", - overlay=True, - control=True, + "HERE_RASTER_NORMAL_BASE_NIGHT": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.normal.basenight ), - "HYBRID": folium.TileLayer( - tiles="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", - attr="Google", - name="Google Satellite", - overlay=True, - control=True, + "HERE_RASTER_NORMAL_LABELS": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.normal.labels ), - "ESRI": folium.TileLayer( - tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Satellite", - overlay=True, - control=True, + "HERE_RASTER_NORMAL_TRANSIT": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.normal.transit ), - "Esri Ocean": folium.TileLayer( - tiles="https://services.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Ocean", - overlay=True, - control=True, + "HERE_RASTER_NORMAL_XBASE": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.normal.xbase ), - "Esri Satellite": folium.TileLayer( - tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Satellite", - overlay=True, - control=True, + "HERE_RASTER_NORMAL_XBASE_NIGHT": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.normal.xbasenight ), - "Esri Standard": folium.TileLayer( - tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Standard", - overlay=True, - control=True, + "HERE_RASTER_SATELLITE_MAP": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.satellite.map ), - "Esri Terrain": folium.TileLayer( - tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Terrain", - overlay=True, - control=True, + "HERE_RASTER_SATELLITE_LABELS": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.satellite.labels ), - "Esri Transportation": folium.TileLayer( - tiles="https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Transportation", - overlay=True, - control=True, + "HERE_RASTER_SATELLITE_BASE": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.satellite.base ), - "Esri Topo World": folium.TileLayer( - tiles="https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Topo World", - overlay=True, - control=True, + "HERE_RASTER_SATELLITE_XBASE": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.satellite.xbase ), - "Esri National Geographic": folium.TileLayer( - tiles="http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri National Geographic", - overlay=True, - control=True, + "HERE_RASTER_TERRAIN_MAP": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.terrain.map ), - "Esri Shaded Relief": folium.TileLayer( - tiles="https://services.arcgisonline.com/arcgis/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Shaded Relief", - overlay=True, - control=True, + "HERE_RASTER_TERRAIN_LABELS": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.terrain.labels ), - "Esri Physical Map": folium.TileLayer( - tiles="https://services.arcgisonline.com/arcgis/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}", - attr="Esri", - name="Esri Physical Map", - overlay=True, - control=True, + "HERE_RASTER_TERRAIN_BASE": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.terrain.base ), - "Bing VirtualEarth": folium.TileLayer( - tiles="http://ecn.t3.tiles.virtualearth.net/tiles/a{q}.jpeg?g=1", - attr="Microsoft", - name="Bing VirtualEarth", - overlay=True, - control=True, + "HERE_RASTER_TERRAIN_XBASE": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.raster.terrain.xbase ), - "USGS 3DEP Elevation": folium.WmsTileLayer( - url="https://elevation.nationalmap.gov/arcgis/services/3DEPElevation/ImageServer/WMSServer?", - layers="3DEPElevation:Hillshade Elevation Tinted", - attr="USGS", - name="USGS 3DEP Elevation", - overlay=True, - control=True, - fmt="image/png", - transparent=True, + "HERE_VECTOR_NORMAL_MAP": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.vector.normal.map ), - "NAIP Imagery": folium.WmsTileLayer( - url="https://services.nationalmap.gov/arcgis/services/USGSNAIPImagery/ImageServer/WMSServer?", - layers="0", - attr="USGS", - name="NAIP Imagery", - overlay=True, - control=True, + "HERE_VECTOR_NORMAL_TRUCK": here_map_widget.DefaultLayers( + layer_name=here_map_widget.DefaultLayerNames.vector.normal.truck ), } -here_basemaps = { - "ROADMAP": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Maps", - ) - ), - "SATELLITE": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", - ) - ), - "TERRAIN": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Terrain", - ) - ), - "HYBRID": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", - ) - ), - "ESRI": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Satellite", - ) - ), - "ESRI Ocean": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://services.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Ocean", - ) - ), - "Esri Satellite": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - naem="Esri Satellite", - ) - ), - "Esri Standard": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Standard", - ) - ), - "Esri Terrain": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Terrain", - ) - ), - "Esri Transportation": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Transportation", - ) - ), - "Esri Topo World": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Topo World", - ) - ), - "Esri National Geographic": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri National Geographic", + +def get_xyz_dict(free_only=True): + """Returns a dictionary of xyz services. + + Args: + free_only (bool, optional): Whether to return only free xyz tile services that do not require an access token. Defaults to True. + + Returns: + dict: A dictionary of xyz services. + """ + + xyz_dict = {} + for item in xyz.values(): + try: + name = item["name"] + tile = eval("xyz." + name) + if eval("xyz." + name + ".requires_token()"): + if free_only: + pass + else: + xyz_dict[name] = tile + else: + xyz_dict[name] = tile + + except Exception: + for sub_item in item: + name = item[sub_item]["name"] + tile = eval("xyz." + name) + if eval("xyz." + name + ".requires_token()"): + if free_only: + pass + else: + xyz_dict[name] = tile + else: + xyz_dict[name] = tile + + xyz_dict = collections.OrderedDict(sorted(xyz_dict.items())) + return xyz_dict + + +def xyz_to_leaflet(): + """Convert xyz tile services to ipyleaflet tile layers. + + Returns: + dict: A dictionary of ipyleaflet tile layers. + """ + leaflet_dict = {} + + for key in xyz_tiles: + name = xyz_tiles[key]["name"] + url = xyz_tiles[key]["url"] + attribution = xyz_tiles[key]["attribution"] + leaflet_dict[key] = ipyleaflet.TileLayer( + url=url, + name=name, + attribution=attribution, ) - ), - "Esri Shaded Relief": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://services.arcgisonline.com/arcgis/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", - name="Esri Shaded Relief", + + for key in wms_tiles: + name = wms_tiles[key]["name"] + url = wms_tiles[key]["url"] + layers = wms_tiles[key]["layers"] + fnt = wms_tiles[key]["format"] + transparent = wms_tiles[key]["transparent"] + attribution = wms_tiles[key]["attribution"] + leaflet_dict[key] = ipyleaflet.WMSLayer( + url=url, + layers=layers, + name=name, + attribution=attribution, + format=fnt, + transparent=transparent, ) - ), - "Esri Physical Map": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://services.arcgisonline.com/arcgis/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}", - attribution="Esri", + + xyz_dict = get_xyz_dict() + for item in xyz_dict: + name = xyz_dict[item].name + url = xyz_dict[item].build_url() + attribution = xyz_dict[item].attribution + if "max_zoom" in xyz_dict[item].keys(): + max_zoom = xyz_dict[item]["max_zoom"] + else: + max_zoom = 22 + leaflet_dict[name] = ipyleaflet.TileLayer( + url=url, name=name, max_zoom=max_zoom, attribution=attribution ) - ), - "Google Maps": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Maps", + + if os.environ.get("PLANET_API_KEY") is not None: + + planet_dict = planet_tiles(tile_format="ipyleaflet") + leaflet_dict.update(planet_dict) + + return leaflet_dict + + +def xyz_to_folium(): + """Convert xyz tile services to folium tile layers. + + Returns: + dict: A dictionary of folium tile layers. + """ + folium_dict = {} + + for key in xyz_tiles: + name = xyz_tiles[key]["name"] + url = xyz_tiles[key]["url"] + attribution = xyz_tiles[key]["attribution"] + folium_dict[key] = folium.TileLayer( + tiles=url, + attr=attribution, + name=name, + overlay=True, + control=True, ) - ), - "Google Satellite": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", + + xyz_dict = get_xyz_dict() + for item in xyz_dict: + name = xyz_dict[item].name + url = xyz_dict[item].build_url() + attribution = xyz_dict[item].attribution + if "max_zoom" in xyz_dict[item].keys(): + max_zoom = xyz_dict[item]["max_zoom"] + else: + max_zoom = 22 + folium_dict[name] = folium.TileLayer( + tiles=url, + attr=attribution, + name=name, + max_zoom=max_zoom, + overlay=True, + control=True, ) - ), - "Google Terrain": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Terrain", + + if os.environ.get("PLANET_API_KEY") is not None: + + planet_dict = planet_tiles(tile_format="folium") + folium_dict.update(planet_dict) + + return folium_dict + + +def xyz_to_heremap(): + """Convert xyz tile services to hermap tile layers. + + Returns: + dict: A dictionary of heremap tile layers. + """ + heremap_dict = {} + + for key in xyz_tiles: + name = xyz_tiles[key]["name"] + url = xyz_tiles[key]["url"] + attribution = xyz_tiles[key]["attribution"] + heremap_dict[key] = here_map_widget.TileLayer( + provider=here_map_widget.ImageTileProvider( + url=url, attribution=attribution, name=name + ) ) - ), - "Google Satellite Hybrid": here_map_widget.TileLayer( - provider=ImageTileProvider( - url="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}", - attribution="Google", - name="Google Satellite", + + xyz_dict = get_xyz_dict() + for item in xyz_dict: + name = xyz_dict[item].name + url = xyz_dict[item].build_url() + attribution = xyz_dict[item].attribution + if "max_zoom" in xyz_dict[item].keys(): + max_zoom = xyz_dict[item]["max_zoom"] + else: + max_zoom = 22 + heremap_dict[name] = here_map_widget.TileLayer( + provider=here_map_widget.ImageTileProvider( + url=url, attribution=attribution, name=name, max_zoom=max_zoom + ) ) - ), - "HERE_RASTER_NORMAL_MAP": DefaultLayers( - layer_name=DefaultLayerNames.raster.normal.map - ), - "HERE_RASTER_NORMAL_BASE": DefaultLayers( - layer_name=DefaultLayerNames.raster.normal.base - ), - "HERE_RASTER_NORMAL_BASE_NIGHT": DefaultLayers( - layer_name=DefaultLayerNames.raster.normal.basenight - ), - "HERE_RASTER_NORMAL_LABELS": DefaultLayers( - layer_name=DefaultLayerNames.raster.normal.labels - ), - "HERE_RASTER_NORMAL_TRANSIT": DefaultLayers( - layer_name=DefaultLayerNames.raster.normal.transit - ), - "HERE_RASTER_NORMAL_XBASE": DefaultLayers( - layer_name=DefaultLayerNames.raster.normal.xbase - ), - "HERE_RASTER_NORMAL_XBASE_NIGHT": DefaultLayers( - layer_name=DefaultLayerNames.raster.normal.xbasenight - ), - "HERE_RASTER_SATELLITE_MAP": DefaultLayers( - layer_name=DefaultLayerNames.raster.satellite.map - ), - "HERE_RASTER_SATELLITE_LABELS": DefaultLayers( - layer_name=DefaultLayerNames.raster.satellite.labels - ), - "HERE_RASTER_SATELLITE_BASE": DefaultLayers( - layer_name=DefaultLayerNames.raster.satellite.base - ), - "HERE_RASTER_SATELLITE_XBASE": DefaultLayers( - layer_name=DefaultLayerNames.raster.satellite.xbase - ), - "HERE_RASTER_TERRAIN_MAP": DefaultLayers( - layer_name=DefaultLayerNames.raster.terrain.map - ), - "HERE_RASTER_TERRAIN_LABELS": DefaultLayers( - layer_name=DefaultLayerNames.raster.terrain.labels - ), - "HERE_RASTER_TERRAIN_BASE": DefaultLayers( - layer_name=DefaultLayerNames.raster.terrain.base - ), - "HERE_RASTER_TERRAIN_XBASE": DefaultLayers( - layer_name=DefaultLayerNames.raster.terrain.xbase - ), - "HERE_VECTOR_NORMAL_MAP": DefaultLayers( - layer_name=DefaultLayerNames.vector.normal.map - ), - "HERE_VECTOR_NORMAL_TRUCK": DefaultLayers( - layer_name=DefaultLayerNames.vector.normal.truck - ), -} + + heremap_dict.update(here_tiles) + + return heremap_dict -# Adds ipyleaflet basemaps -for item in ipybasemaps.values(): - try: - name = item["name"] - basemap = "ipybasemaps.{}".format(name) - leaf_basemaps[name] = basemap_to_tiles(eval(basemap)) - except Exception: - for sub_item in item: - name = item[sub_item]["name"] - basemap = "ipybasemaps.{}".format(name) - basemap = basemap.replace("Mids", "Modis") - leaf_basemaps[name] = basemap_to_tiles(eval(basemap)) - -if os.environ.get("PLANET_API_KEY") is not None: - from .common import planet_tiles - - planet_dict = planet_tiles() - for key in planet_dict: - leaf_basemaps[key] = planet_dict[key] - -basemap_tiles = Box(leaf_basemaps, frozen_box=True) -basemaps = Box( - dict(zip(list(leaf_basemaps.keys()), list(leaf_basemaps.keys()))), frozen_box=True -) +leafmap_basemaps = Box(xyz_to_leaflet(), frozen_box=True) +folium_basemaps = Box(xyz_to_folium(), frozen_box=True) +heremap_basemaps = Box(xyz_to_heremap(), frozen_box=True) diff --git a/leafmap/common.py b/leafmap/common.py index 8c0657e6b6..676472544e 100644 --- a/leafmap/common.py +++ b/leafmap/common.py @@ -2503,15 +2503,15 @@ def basemap_xyz_tiles(): Returns: dict: A dictionary of XYZ tile layers. """ - from .basemaps import basemap_tiles + from .basemaps import leafmap_basemaps layers_dict = {} - keys = dict(basemap_tiles).keys() + keys = dict(leafmap_basemaps).keys() for key in keys: - if isinstance(basemap_tiles[key], ipyleaflet.WMSLayer): + if isinstance(leafmap_basemaps[key], ipyleaflet.WMSLayer): pass else: - layers_dict[key] = basemap_tiles[key] + layers_dict[key] = leafmap_basemaps[key] return layers_dict diff --git a/leafmap/leafmap.py b/leafmap/leafmap.py index 9d0cdc799f..fa835a8b99 100644 --- a/leafmap/leafmap.py +++ b/leafmap/leafmap.py @@ -3,7 +3,7 @@ import os import ipyleaflet from IPython.display import display -from .basemaps import basemap_tiles +from .basemaps import leafmap_basemaps from .common import * from .legends import builtin_legends from .osm import * @@ -98,25 +98,24 @@ def handle_draw(target, action, geo_json): if kwargs["scale_control"]: self.add_control(ipyleaflet.ScaleControl(position="bottomleft")) - self.clear_layers() - self.add_layer(basemap_tiles["OpenStreetMap"]) + self.layers[0].name = "OpenStreetMap" if "google_map" not in kwargs: pass elif kwargs["google_map"] is not None: if kwargs["google_map"].upper() == "ROADMAP": - layer = basemap_tiles["ROADMAP"] + layer = leafmap_basemaps["ROADMAP"] elif kwargs["google_map"].upper() == "HYBRID": - layer = basemap_tiles["HYBRID"] + layer = leafmap_basemaps["HYBRID"] elif kwargs["google_map"].upper() == "TERRAIN": - layer = basemap_tiles["TERRAIN"] + layer = leafmap_basemaps["TERRAIN"] elif kwargs["google_map"].upper() == "SATELLITE": - layer = basemap_tiles["SATELLITE"] + layer = leafmap_basemaps["SATELLITE"] else: print( f'{kwargs["google_map"]} is invalid. google_map must be one of: ["ROADMAP", "HYBRID", "TERRAIN", "SATELLITE"]. Adding the default ROADMAP.' ) - layer = basemap_tiles["ROADMAP"] + layer = leafmap_basemaps["ROADMAP"] self.add_layer(layer) if "toolbar_control" not in kwargs: @@ -195,25 +194,26 @@ def add_basemap(self, basemap="HYBRID"): try: layer_names = self.get_layer_names() if ( - basemap in basemap_tiles - and basemap_tiles[basemap].name not in layer_names + basemap in leafmap_basemaps + and leafmap_basemaps[basemap].name not in layer_names ): - self.add_layer(basemap_tiles[basemap]) + self.add_layer(leafmap_basemaps[basemap]) elif ( - basemap in basemap_tiles and basemap_tiles[basemap].name in layer_names + basemap in leafmap_basemaps + and leafmap_basemaps[basemap].name in layer_names ): print(f"{basemap} has been already added before.") else: print( "Basemap can only be one of the following:\n {}".format( - "\n ".join(basemap_tiles.keys()) + "\n ".join(leafmap_basemaps.keys()) ) ) except Exception: raise ValueError( "Basemap can only be one of the following:\n {}".format( - "\n ".join(basemap_tiles.keys()) + "\n ".join(leafmap_basemaps.keys()) ) ) @@ -893,7 +893,7 @@ def add_minimap(self, zoom=5, position="bottomright"): attribution_control=False, zoom=zoom, center=self.center, - layers=[basemap_tiles["ROADMAP"]], + layers=[leafmap_basemaps["ROADMAP"]], ) minimap.layout.width = "150px" minimap.layout.height = "150px" @@ -944,11 +944,11 @@ def split_map(self, left_layer="HYBRID", right_layer="ESRI"): right_layer (str, optional): The right tile layer. Defaults to 'ESRI'. """ try: - if left_layer in basemap_tiles.keys(): - left_layer = basemap_tiles[left_layer] + if left_layer in leafmap_basemaps.keys(): + left_layer = leafmap_basemaps[left_layer] - if right_layer in basemap_tiles.keys(): - right_layer = basemap_tiles[right_layer] + if right_layer in leafmap_basemaps.keys(): + right_layer = leafmap_basemaps[right_layer] control = ipyleaflet.SplitMapControl( left_layer=left_layer, right_layer=right_layer @@ -962,7 +962,7 @@ def split_map(self, left_layer="HYBRID", right_layer="ESRI"): def basemap_demo(self): """A demo for using leafmap basemaps.""" dropdown = widgets.Dropdown( - options=list(basemap_tiles.keys()), + options=list(leafmap_basemaps.keys()), value="HYBRID", description="Basemaps", ) @@ -970,7 +970,7 @@ def basemap_demo(self): def on_click(change): basemap_name = change["new"] old_basemap = self.layers[-1] - self.substitute_layer(old_basemap, basemap_tiles[basemap_name]) + self.substitute_layer(old_basemap, leafmap_basemaps[basemap_name]) dropdown.observe(on_click, "value") basemap_control = ipyleaflet.WidgetControl(widget=dropdown, position="topright") @@ -2413,8 +2413,8 @@ def linked_maps( **kwargs, ) - if layers[index] in basemap_tiles: - m.add_layer(basemap_tiles[layers[index]]) + if layers[index] in leafmap_basemaps: + m.add_layer(leafmap_basemaps[layers[index]]) else: try: m.add_layer(layers[index]) @@ -2477,10 +2477,10 @@ def split_map( if "scale_control" not in kwargs: kwargs["scale_control"] = False - if left_layer in basemap_tiles: - left_layer = basemap_tiles[left_layer] - if right_layer in basemap_tiles: - right_layer = basemap_tiles[right_layer] + if left_layer in leafmap_basemaps: + left_layer = leafmap_basemaps[left_layer] + if right_layer in leafmap_basemaps: + right_layer = leafmap_basemaps[right_layer] m = Map(**kwargs) @@ -2551,12 +2551,12 @@ def ts_inspector( if layers_dict is None: layers_dict = {} - keys = dict(basemap_tiles).keys() + keys = dict(leafmap_basemaps).keys() for key in keys: - if isinstance(basemap_tiles[key], ipyleaflet.WMSLayer): + if isinstance(leafmap_basemaps[key], ipyleaflet.WMSLayer): pass else: - layers_dict[key] = basemap_tiles[key] + layers_dict[key] = leafmap_basemaps[key] keys = list(layers_dict.keys()) if left_name is None: diff --git a/leafmap/toolbar.py b/leafmap/toolbar.py index ec047aa015..a30d8291eb 100644 --- a/leafmap/toolbar.py +++ b/leafmap/toolbar.py @@ -841,15 +841,21 @@ def change_basemap(m): Args: m (object): leafmap.Map. """ - from .basemaps import leaf_basemaps + from .basemaps import leafmap_basemaps, get_xyz_dict - value = "OpenStreetMap.Mapnik" - if len(m.layers) > 0: - if m.layers[0].name in list(leaf_basemaps.keys()): - value = m.layers[0].name + xyz_dict = get_xyz_dict() + + layers = list(m.layers) + if len(layers) == 1: + layers = [layers[0]] + [leafmap_basemaps["OpenStreetMap"]] + elif len(layers) > 1 and (layers[1].name != "OpenStreetMap"): + layers = [layers[0]] + [leafmap_basemaps["OpenStreetMap"]] + layers[1:] + m.layers = layers + + value = "OpenStreetMap" dropdown = widgets.Dropdown( - options=list(leaf_basemaps.keys()), + options=list(leafmap_basemaps.keys()), value=value, layout=widgets.Layout(width="200px"), ) @@ -865,12 +871,13 @@ def change_basemap(m): def on_click(change): basemap_name = change["new"] - - if len(m.layers) == 1: - old_basemap = m.layers[0] - else: - old_basemap = m.layers[1] - m.substitute_layer(old_basemap, leaf_basemaps[basemap_name]) + old_basemap = m.layers[1] + m.substitute_layer(old_basemap, leafmap_basemaps[basemap_name]) + if basemap_name in xyz_dict: + if "bounds" in xyz_dict[basemap_name]: + bounds = xyz_dict[basemap_name]["bounds"] + bounds = [bounds[0][1], bounds[0][0], bounds[1][1], bounds[1][0]] + m.zoom_to_bounds(bounds) dropdown.observe(on_click, "value") @@ -992,11 +999,11 @@ def split_basemaps( m, layers_dict=None, left_name=None, right_name=None, width="120px", **kwargs ): - from .basemaps import basemap_tiles + from .basemaps import leafmap_basemaps controls = m.controls layers = m.layers - m.layers = [m.layers[0]] + # m.layers = [m.layers[0]] m.clear_controls() add_zoom = True @@ -1004,12 +1011,12 @@ def split_basemaps( if layers_dict is None: layers_dict = {} - keys = dict(basemap_tiles).keys() + keys = dict(leafmap_basemaps).keys() for key in keys: - if isinstance(basemap_tiles[key], ipyleaflet.WMSLayer): + if isinstance(leafmap_basemaps[key], ipyleaflet.WMSLayer): pass else: - layers_dict[key] = basemap_tiles[key] + layers_dict[key] = leafmap_basemaps[key] keys = list(layers_dict.keys()) if left_name is None: @@ -1048,6 +1055,7 @@ def split_basemaps( def close_btn_click(change): if change["new"]: m.controls = controls + m.clear_layers() m.layers = layers close_button.observe(close_btn_click, "value") diff --git a/requirements.txt b/requirements.txt index 45aea4b996..c1e288728b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ colour folium>=0.12.0 geojson googledrivedownloader +here-map-widget-for-jupyter>=1.1.1 ipyevents<=0.9.0 ipyleaflet matplotlib @@ -14,4 +15,4 @@ pyshp>=2.1.3 python-box whitebox whiteboxgui -here-map-widget-for-jupyter>=1.1.1 +xyzservices