From 1178d02bbaf0590cb1818c7bf9ae62539da68e88 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Fri, 5 Apr 2024 23:26:17 -0400 Subject: [PATCH 01/18] Add Google map tiles --- geemap/common.py | 117 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/geemap/common.py b/geemap/common.py index 56181b78ce..df849c38da 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -24,7 +24,7 @@ import ee import ipywidgets as widgets from ipytree import Node, Tree -from typing import Union, List, Dict, Optional, Tuple +from typing import Union, List, Dict, Optional, Tuple, Any try: from IPython.display import display, IFrame, Javascript @@ -16158,3 +16158,118 @@ def is_on_aws(): if item.endswith(".aws") or "ec2-user" in item: on_aws = True return on_aws + + +def google_map_tiles( + map_type: str = "roadmap", + language: str = "en-Us", + region: str = "US", + api_key: Optional[str] = None, + **kwargs: Any, +): + """ + Generates Google Map tiles using the provided parameters. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project + + Args: + map_type (str, optional): The type of map to generate. Options are 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'. Defaults to 'roadmap'. + language (str, optional): An IETF language tag that specifies the language used to display information on the tiles, such as 'zh-Cn'. Defaults to 'en-Us'. + region (str, optional): A Common Locale Data Repository region identifier (two uppercase letters) that represents the physical location of the user. Defaults to 'US'. + api_key (str, optional): The API key to use for the Google Maps API. If not provided, it will try to get it from the environment or Colab user data. Defaults to None. + **kwargs: Additional parameters to pass to the map generation. For more info, visit https://developers.google.com/maps/documentation/tile/session_tokens#optional_fields + + Raises: + ValueError: If the API key is not provided and cannot be found in the environment or Colab user data. + ValueError: If the map_type is not one of the allowed types. + + Example: + >>> import geemap + >>> m = geemap.Map() + >>> basemap = google_map_tiles(map_type='roadmap', language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) + >>> m.add_basemap(basemap) + + Returns: + TileProvider: A TileProvider object with the generated map, or None if the map could not be generated. + """ + try: + import googlemaps + except ImportError: + install_package("googlemaps") + import googlemaps + + from xyzservices import TileProvider + + if api_key is None: + + if in_colab_shell(): + from google.colab import userdata + + api_key = userdata.get("MAPS_API_KEY") + else: + api_key = os.environ.get("MAPS_API_KEY") + + if api_key is None: + raise ValueError( + "API key is required to access Google Maps API. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project" + ) + + gmaps = googlemaps.Client(key=api_key) + + map_type = map_type.lower() + if map_type not in [ + "roadmap", + "satellite", + "terrain", + "hybrid", + "traffic", + "streetview", + ]: + raise ValueError( + "mapType must be one of 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'" + ) + + if map_type == "hybrid": + map_type = "satellite" + if "layerTypes" not in kwargs: + kwargs["layerTypes"] = ["layerRoadmap"] + + if map_type == "terrain": + if "layerTypes" not in kwargs: + kwargs["layerTypes"] = ["layerRoadmap"] + + if map_type == "traffic": + map_type = "roadmap" + if "layerTypes" not in kwargs: + kwargs["layerTypes"] = ["layerTraffic"] + + if map_type == "streetview": + map_type = "roadmap" + if "layerTypes" not in kwargs: + kwargs["layerTypes"] = ["layerStreetview"] + + kwargs["mapType"] = map_type + kwargs["language"] = language + kwargs["region"] = region + + response = requests.post( + f"https://tile.googleapis.com/v1/createSession?key={api_key}", + headers={"Content-Type": "application/json"}, + json=kwargs, + ) + + if response.status_code == 200: + res = response.json() + gmap_provider = TileProvider( + { + "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", + "attribution": "© Google Maps", + "accessToken": api_key, + "name": "Google Maps", + "ext": res["imageFormat"], + "tileSize": res["tileWidth"], + } + ) + else: + display(response.text) + gmap_provider = None + + return gmap_provider From 7b498091d2fb1ac58642d2f4c0f18948b130cd7e Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Wed, 10 Apr 2024 14:13:07 -0500 Subject: [PATCH 02/18] Remove googlemaps dependency --- geemap/common.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/geemap/common.py b/geemap/common.py index df849c38da..dad96c6942 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16190,11 +16190,6 @@ def google_map_tiles( Returns: TileProvider: A TileProvider object with the generated map, or None if the map could not be generated. """ - try: - import googlemaps - except ImportError: - install_package("googlemaps") - import googlemaps from xyzservices import TileProvider @@ -16212,8 +16207,6 @@ def google_map_tiles( "API key is required to access Google Maps API. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project" ) - gmaps = googlemaps.Client(key=api_key) - map_type = map_type.lower() if map_type not in [ "roadmap", From b5a2f137e487584c5e2e095d94d013e59c3cab83 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Wed, 10 Apr 2024 17:48:33 -0500 Subject: [PATCH 03/18] Fix name and attribution --- geemap/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geemap/common.py b/geemap/common.py index dad96c6942..e200c0774d 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16254,9 +16254,9 @@ def google_map_tiles( gmap_provider = TileProvider( { "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", - "attribution": "© Google Maps", + "attribution": f"© Google {map_type.capitalize()}", "accessToken": api_key, - "name": "Google Maps", + "name": f"Google {map_type.capitalize()}", "ext": res["imageFormat"], "tileSize": res["tileWidth"], } From 9c5e909ce440a7c38af389a9f9b899c0ce5e4a13 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Wed, 10 Apr 2024 17:59:42 -0500 Subject: [PATCH 04/18] Add name variations --- geemap/common.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/geemap/common.py b/geemap/common.py index e200c0774d..575c7bb7c3 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16182,10 +16182,10 @@ def google_map_tiles( ValueError: If the map_type is not one of the allowed types. Example: - >>> import geemap - >>> m = geemap.Map() - >>> basemap = google_map_tiles(map_type='roadmap', language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) - >>> m.add_basemap(basemap) + import geemap + m = geemap.Map() + basemap = geemap.google_map_tiles(map_type='roadmap', language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) + m.add_basemap(basemap) Returns: TileProvider: A TileProvider object with the generated map, or None if the map could not be generated. @@ -16207,7 +16207,8 @@ def google_map_tiles( "API key is required to access Google Maps API. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project" ) - map_type = map_type.lower() + map_type = map_type.lower().replace("google.", "").replace("google", "").strip() + if map_type not in [ "roadmap", "satellite", From 0ad6584b51b5281e68eaadf896ba6ae6e75f9475 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Wed, 10 Apr 2024 20:28:25 -0500 Subject: [PATCH 05/18] Add Google map tiles to basemap list --- geemap/basemaps.py | 91 +++++++++++++++++++++++++++++++--------------- geemap/common.py | 12 +++--- 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/geemap/basemaps.py b/geemap/basemaps.py index b648e77ae9..099f2137be 100644 --- a/geemap/basemaps.py +++ b/geemap/basemaps.py @@ -23,36 +23,67 @@ import folium import ipyleaflet import xyzservices -from .common import check_package, planet_tiles - -XYZ_TILES = { - "OpenStreetMap": { - "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - "attribution": "OpenStreetMap", - "name": "OpenStreetMap", - }, - "ROADMAP": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldStreetMap", - }, - "SATELLITE": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldImagery", - }, - "TERRAIN": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldTopoMap", - }, - "HYBRID": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldImagery", - }, -} - +from .common import check_package, planet_tiles, in_colab_shell, google_map_tiles + +if in_colab_shell(): + from google.colab import userdata + + MAPS_API_KEY = userdata.get("MAPS_API_KEY") +else: + MAPS_API_KEY = os.environ.get("MAPS_API_KEY") + +if MAPS_API_KEY is None: + + XYZ_TILES = { + "OpenStreetMap": { + "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + "attribution": "OpenStreetMap", + "name": "OpenStreetMap", + }, + "ROADMAP": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldStreetMap", + }, + "SATELLITE": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldImagery", + }, + "TERRAIN": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldTopoMap", + }, + "HYBRID": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldImagery", + }, + } + +else: + XYZ_TILES = { + "OpenStreetMap": { + "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + "attribution": "OpenStreetMap", + "name": "OpenStreetMap", + }, + } + for gmap in [ + "roadmap", + "satellite", + "terrain", + "hybrid", + "traffic", + "streetview", + ]: + provider = google_map_tiles(gmap, api_key=MAPS_API_KEY) + XYZ_TILES[provider.name] = { + "url": provider.build_url(), + "attribution": provider.attribution, + "name": provider.name, + } # Custom WMS tile services. WMS_TILES = { diff --git a/geemap/common.py b/geemap/common.py index 575c7bb7c3..56902a20d1 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16221,8 +16221,10 @@ def google_map_tiles( "mapType must be one of 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'" ) + mapType = map_type + if map_type == "hybrid": - map_type = "satellite" + mapType = "satellite" if "layerTypes" not in kwargs: kwargs["layerTypes"] = ["layerRoadmap"] @@ -16231,16 +16233,16 @@ def google_map_tiles( kwargs["layerTypes"] = ["layerRoadmap"] if map_type == "traffic": - map_type = "roadmap" + mapType = "roadmap" if "layerTypes" not in kwargs: kwargs["layerTypes"] = ["layerTraffic"] if map_type == "streetview": - map_type = "roadmap" + mapType = "roadmap" if "layerTypes" not in kwargs: kwargs["layerTypes"] = ["layerStreetview"] - kwargs["mapType"] = map_type + kwargs["mapType"] = mapType kwargs["language"] = language kwargs["region"] = region @@ -16257,7 +16259,7 @@ def google_map_tiles( "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", "attribution": f"© Google {map_type.capitalize()}", "accessToken": api_key, - "name": f"Google {map_type.capitalize()}", + "name": f"Google.{map_type.capitalize()}", "ext": res["imageFormat"], "tileSize": res["tileWidth"], } From ee3285270eff65639e204fefbef1c1d3dd227da9 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Tue, 16 Apr 2024 20:59:34 -0400 Subject: [PATCH 06/18] Add support for returning all map types as a dict --- geemap/basemaps.py | 18 ++---- geemap/common.py | 134 +++++++++++++++++++++++++++------------------ 2 files changed, 86 insertions(+), 66 deletions(-) diff --git a/geemap/basemaps.py b/geemap/basemaps.py index 099f2137be..55a42374d3 100644 --- a/geemap/basemaps.py +++ b/geemap/basemaps.py @@ -70,19 +70,13 @@ "name": "OpenStreetMap", }, } - for gmap in [ - "roadmap", - "satellite", - "terrain", - "hybrid", - "traffic", - "streetview", - ]: - provider = google_map_tiles(gmap, api_key=MAPS_API_KEY) - XYZ_TILES[provider.name] = { + + gmap_providers = google_map_tiles(api_key=MAPS_API_KEY) + for key, provider in gmap_providers.items(): + XYZ_TILES[provider["name"]] = { "url": provider.build_url(), - "attribution": provider.attribution, - "name": provider.name, + "attribution": provider["attribution"], + "name": provider["name"], } # Custom WMS tile services. diff --git a/geemap/common.py b/geemap/common.py index 3552c6b3bf..9e70044eb6 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16170,17 +16170,19 @@ def is_on_aws(): def google_map_tiles( - map_type: str = "roadmap", + map_type: str = None, language: str = "en-Us", region: str = "US", api_key: Optional[str] = None, **kwargs: Any, ): """ - Generates Google Map tiles using the provided parameters. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project + Generates Google Map tiles using the provided parameters. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project. + You can set the API key using the environment variable `MAPS_API_KEY` or by passing it as an argument. Args: - map_type (str, optional): The type of map to generate. Options are 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'. Defaults to 'roadmap'. + map_type (str, optional): The type of map to generate. Options are 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'. + Defaults to None, which generates all map types, and returns a dictionary of TileProvider objects. language (str, optional): An IETF language tag that specifies the language used to display information on the tiles, such as 'zh-Cn'. Defaults to 'en-Us'. region (str, optional): A Common Locale Data Repository region identifier (two uppercase letters) that represents the physical location of the user. Defaults to 'US'. api_key (str, optional): The API key to use for the Google Maps API. If not provided, it will try to get it from the environment or Colab user data. Defaults to None. @@ -16191,13 +16193,14 @@ def google_map_tiles( ValueError: If the map_type is not one of the allowed types. Example: - import geemap - m = geemap.Map() - basemap = geemap.google_map_tiles(map_type='roadmap', language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) - m.add_basemap(basemap) + >>> import geemap + >>> m = geemap.Map() + >>> basemap = geemap.google_map_tiles(map_type='roadmap', language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) + >>> m.add_basemap(basemap) Returns: - TileProvider: A TileProvider object with the generated map, or None if the map could not be generated. + TileProvider | dict: A TileProvider object with the generated map, or a dictionary of TileProvider objects for all map types, + or None if the map could not be generated. """ from xyzservices import TileProvider @@ -16216,65 +16219,88 @@ def google_map_tiles( "API key is required to access Google Maps API. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project" ) - map_type = map_type.lower().replace("google.", "").replace("google", "").strip() - - if map_type not in [ + allowed_map_types = [ "roadmap", "satellite", "terrain", "hybrid", "traffic", "streetview", - ]: - raise ValueError( - "mapType must be one of 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'" - ) + ] - mapType = map_type + # Support map type as a string with or without 'google.', such as 'Google Roadmap', 'Google.Roadmap', or 'Roadmap' + if isinstance(map_type, str): + map_type = map_type.lower().replace("google.", "").replace("google", "").strip() - if map_type == "hybrid": - mapType = "satellite" - if "layerTypes" not in kwargs: - kwargs["layerTypes"] = ["layerRoadmap"] + if map_type not in allowed_map_types: + raise ValueError( + "mapType must be one of 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'" + ) - if map_type == "terrain": - if "layerTypes" not in kwargs: - kwargs["layerTypes"] = ["layerRoadmap"] + tile_args = {} + + # Define the parameters for each map type + for m_type in allowed_map_types: + + mapType = m_type + layerTypes = None + + if m_type == "hybrid": + mapType = "satellite" + layerTypes = ["layerRoadmap"] + elif m_type == "terrain": + layerTypes = ["layerRoadmap"] + elif m_type == "traffic": + mapType = "roadmap" + layerTypes = ["layerTraffic"] + elif m_type == "streetview": + mapType = "roadmap" + layerTypes = ["layerStreetview"] + + tile_args[m_type] = { + "mapType": mapType, + "language": language, + "region": region, + "layerTypes": layerTypes, + **kwargs, + } - if map_type == "traffic": - mapType = "roadmap" - if "layerTypes" not in kwargs: - kwargs["layerTypes"] = ["layerTraffic"] + if tile_args[m_type].get("layerTypes") is None: + del tile_args[m_type]["layerTypes"] - if map_type == "streetview": - mapType = "roadmap" - if "layerTypes" not in kwargs: - kwargs["layerTypes"] = ["layerStreetview"] + gmap_providers = {} - kwargs["mapType"] = mapType - kwargs["language"] = language - kwargs["region"] = region + for m_type in allowed_map_types: - response = requests.post( - f"https://tile.googleapis.com/v1/createSession?key={api_key}", - headers={"Content-Type": "application/json"}, - json=kwargs, - ) + # If map_type is provided, only generate the specified map + if map_type is not None and m_type != map_type: + continue - if response.status_code == 200: - res = response.json() - gmap_provider = TileProvider( - { - "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", - "attribution": f"© Google {map_type.capitalize()}", - "accessToken": api_key, - "name": f"Google.{map_type.capitalize()}", - "ext": res["imageFormat"], - "tileSize": res["tileWidth"], - } + args = tile_args[m_type] + response = requests.post( + f"https://tile.googleapis.com/v1/createSession?key={api_key}", + headers={"Content-Type": "application/json"}, + json=args, ) - else: - display(response.text) - gmap_provider = None - return gmap_provider + if response.status_code == 200: + res = response.json() + gmap_provider = TileProvider( + { + "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", + "attribution": f"© Google {m_type.capitalize()}", + "accessToken": api_key, + "name": f"Google.{m_type.capitalize()}", + "ext": res["imageFormat"], + "tileSize": res["tileWidth"], + } + ) + gmap_providers[m_type] = gmap_provider + else: + display(response.text) + gmap_provider = None + + if map_type is None: # Return all map types + return gmap_providers + else: # Return the specified map type + return gmap_providers.get(map_type) From 7c598e4725539dc0e17887f769482aea118452cb Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Tue, 16 Apr 2024 21:30:42 -0400 Subject: [PATCH 07/18] Use Google Roadmap by default if API Key is available --- geemap/geemap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/geemap/geemap.py b/geemap/geemap.py index 0cd9f057c6..ae17ba6825 100644 --- a/geemap/geemap.py +++ b/geemap/geemap.py @@ -121,7 +121,8 @@ def __init__(self, **kwargs): kwargs["add_google_map"] = False else: kwargs.pop("basemap") - + elif "Google.Roadmap" in basemaps.keys(): + kwargs["basemap"] = get_basemap("Google.Roadmap") self._xyz_dict = get_xyz_dict() self.baseclass = "ipyleaflet" From ad9806174861ced98d33a41386413a82bbc0445b Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Tue, 16 Apr 2024 22:32:04 -0400 Subject: [PATCH 08/18] Make gmap tiles available in core module --- geemap/core.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/geemap/core.py b/geemap/core.py index 2ef6ac1b8b..ef75e8b7b2 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -402,13 +402,21 @@ class Map(ipyleaflet.Map, MapInterface): "scroll_wheel_zoom": True, } - _BASEMAP_ALIASES: Dict[str, str] = { - "DEFAULT": "OpenStreetMap.Mapnik", - "ROADMAP": "Esri.WorldStreetMap", - "SATELLITE": "Esri.WorldImagery", - "TERRAIN": "Esri.WorldTopoMap", - "HYBRID": "Esri.WorldImagery", - } + if basemaps.MAPS_API_KEY is not None: + _BASEMAP_ALIASES: Dict[str, str] = { + "OpenStreetMap": "OpenStreetMap.Mapnik", + } + for key in basemaps.XYZ_TILES: + if key.startswith("Google"): + _BASEMAP_ALIASES[key] = key + else: + _BASEMAP_ALIASES: Dict[str, str] = { + "OpenStreetMap": "OpenStreetMap.Mapnik", + "ROADMAP": "Esri.WorldStreetMap", + "SATELLITE": "Esri.WorldImagery", + "TERRAIN": "Esri.WorldTopoMap", + "HYBRID": "Esri.WorldImagery", + } _USER_AGENT_PREFIX = "geemap-core" @@ -458,6 +466,15 @@ def _basemap_selector(self) -> Optional[map_widgets.Basemap]: def __init__(self, **kwargs): self._available_basemaps = self._get_available_basemaps() + if "basemap" not in kwargs and "Google.Roadmap" in self._available_basemaps: + kwargs["basemap"] = self._available_basemaps["Google.Roadmap"] + elif ( + "basemap" in kwargs + and isinstance(kwargs["basemap"], str) + and kwargs["basemap"] in self._available_basemaps + ): + kwargs["basemap"] = self._available_basemaps.get(kwargs["basemap"]) + if "width" in kwargs: self.width: str = kwargs.pop("width", "100%") self.height: str = kwargs.pop("height", "600px") @@ -850,6 +867,11 @@ def _get_available_basemaps(self) -> Dict[str, Any]: for tile_info in basemaps.get_xyz_dict().values(): tile_info["url"] = tile_info.build_url() ret_dict[tile_info["name"]] = tile_info + + if "Google.Roadmap" in basemaps.XYZ_TILES: + for key in basemaps.XYZ_TILES: + if key.startswith("Google"): + ret_dict[key] = basemaps.XYZ_TILES[key] extra_dict = {k: ret_dict[v] for k, v in self._BASEMAP_ALIASES.items()} return {**extra_dict, **ret_dict} From 9ecd29d5762cd44b409c468fd672a5a1d14dc5ce Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Tue, 16 Apr 2024 23:09:40 -0400 Subject: [PATCH 09/18] Fix docs build error --- geemap/basemaps.py | 16 ++++++++-------- geemap/common.py | 20 ++++++++++++++++++++ geemap/core.py | 20 ++++++++++++-------- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/geemap/basemaps.py b/geemap/basemaps.py index 55a42374d3..0e62c941dc 100644 --- a/geemap/basemaps.py +++ b/geemap/basemaps.py @@ -23,14 +23,14 @@ import folium import ipyleaflet import xyzservices -from .common import check_package, planet_tiles, in_colab_shell, google_map_tiles - -if in_colab_shell(): - from google.colab import userdata - - MAPS_API_KEY = userdata.get("MAPS_API_KEY") -else: - MAPS_API_KEY = os.environ.get("MAPS_API_KEY") +from .common import ( + check_package, + planet_tiles, + google_map_tiles, + google_maps_api_key, +) + +MAPS_API_KEY = google_maps_api_key() if MAPS_API_KEY is None: diff --git a/geemap/common.py b/geemap/common.py index 9e70044eb6..ae0f77419a 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16304,3 +16304,23 @@ def google_map_tiles( return gmap_providers else: # Return the specified map type return gmap_providers.get(map_type) + + +def google_maps_api_key(token_name="MAPS_API_KEY"): + """ + Retrieves the Google Maps API key from the environment or Colab user data. + + Args: + token_name (str, optional): The name of the environment variable or Colab user data key where the API key is stored. Defaults to 'MAPS_API_KEY'. + + Returns: + str: The API key, or None if it could not be found. + """ + if in_colab_shell(): + from google.colab import userdata + + MAPS_API_KEY = userdata.get(token_name) + else: + MAPS_API_KEY = os.environ.get(token_name, None) + + return MAPS_API_KEY diff --git a/geemap/core.py b/geemap/core.py index ef75e8b7b2..caaf2d15d8 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -402,20 +402,24 @@ class Map(ipyleaflet.Map, MapInterface): "scroll_wheel_zoom": True, } - if basemaps.MAPS_API_KEY is not None: + if common.google_maps_api_key() is not None: _BASEMAP_ALIASES: Dict[str, str] = { "OpenStreetMap": "OpenStreetMap.Mapnik", + "Google.Roadmap": "Google.Roadmap", + "Google.Satellite": "Google.Satellite", + "Google.Terrain": "Google.Terrain", + "Google.Hybrid": "Google.Hybrid", + "Google.Traffic": "Google.Traffic", + "Google.Streetview": "Google.Streetview", } - for key in basemaps.XYZ_TILES: - if key.startswith("Google"): - _BASEMAP_ALIASES[key] = key + else: _BASEMAP_ALIASES: Dict[str, str] = { "OpenStreetMap": "OpenStreetMap.Mapnik", - "ROADMAP": "Esri.WorldStreetMap", - "SATELLITE": "Esri.WorldImagery", - "TERRAIN": "Esri.WorldTopoMap", - "HYBRID": "Esri.WorldImagery", + "Esri.WorldStreetMap": "Esri.WorldStreetMap", + "Esri.WorldImagery": "Esri.WorldImagery", + "Esri.WorldTopoMap": "Esri.WorldTopoMap", + "Esri.WorldImagery": "Esri.WorldImagery", } _USER_AGENT_PREFIX = "geemap-core" From a8dfafa01b1dfd2bd5dfd2f958e26e2be98904fd Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 28 Apr 2024 20:25:39 -0400 Subject: [PATCH 10/18] Remove Google Traffic and Streetview --- geemap/core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/geemap/core.py b/geemap/core.py index caaf2d15d8..f04c2ca99a 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -409,8 +409,6 @@ class Map(ipyleaflet.Map, MapInterface): "Google.Satellite": "Google.Satellite", "Google.Terrain": "Google.Terrain", "Google.Hybrid": "Google.Hybrid", - "Google.Traffic": "Google.Traffic", - "Google.Streetview": "Google.Streetview", } else: From 27ed73f5fed81fa77f0f0784022ebad017e105ed Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 28 Apr 2024 21:52:45 -0400 Subject: [PATCH 11/18] Set line wrap for docstrings --- geemap/common.py | 55 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/geemap/common.py b/geemap/common.py index 1ef0c39aec..f147dfc533 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16177,29 +16177,44 @@ def google_map_tiles( **kwargs: Any, ): """ - Generates Google Map tiles using the provided parameters. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project. - You can set the API key using the environment variable `MAPS_API_KEY` or by passing it as an argument. - - Args: - map_type (str, optional): The type of map to generate. Options are 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'. - Defaults to None, which generates all map types, and returns a dictionary of TileProvider objects. - language (str, optional): An IETF language tag that specifies the language used to display information on the tiles, such as 'zh-Cn'. Defaults to 'en-Us'. - region (str, optional): A Common Locale Data Repository region identifier (two uppercase letters) that represents the physical location of the user. Defaults to 'US'. - api_key (str, optional): The API key to use for the Google Maps API. If not provided, it will try to get it from the environment or Colab user data. Defaults to None. - **kwargs: Additional parameters to pass to the map generation. For more info, visit https://developers.google.com/maps/documentation/tile/session_tokens#optional_fields + Generates Google Map tiles using the provided parameters. To get an API key + and enable Map Tiles API, visit + https://developers.google.com/maps/get-started#create-project. + You can set the API key using the environment variable `MAPS_API_KEY` + or by passing it as an argument. + + Args: + map_type (str, optional): The type of map to generate. Options are + 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'. + Defaults to None, which generates all map types, and returns a + dictionary of TileProvider objects. + language (str, optional): An IETF language tag that specifies the + language used to display information on the tiles, such as 'zh-Cn'. + Defaults to 'en-Us'. + region (str, optional): A Common Locale Data Repository region + identifier (two uppercase letters) that represents the physical + location of the user. Defaults to 'US'. + api_key (str, optional): The API key to use for the Google Maps API. + If not provided, it will try to get it from the environment or + Colab user data. Defaults to None. + **kwargs: Additional parameters to pass to the map generation. For more + info, visit https://bit.ly/3UhbZKU Raises: - ValueError: If the API key is not provided and cannot be found in the environment or Colab user data. + ValueError: If the API key is not provided and cannot be found in the + environment or Colab user data. ValueError: If the map_type is not one of the allowed types. Example: >>> import geemap >>> m = geemap.Map() - >>> basemap = geemap.google_map_tiles(map_type='roadmap', language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) + >>> basemap = geemap.google_map_tiles(map_type='roadmap', + language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) >>> m.add_basemap(basemap) Returns: - TileProvider | dict: A TileProvider object with the generated map, or a dictionary of TileProvider objects for all map types, + TileProvider | dict: A TileProvider object with the generated map, or a + dictionary of TileProvider objects for all map types, or None if the map could not be generated. """ @@ -16216,7 +16231,9 @@ def google_map_tiles( if api_key is None: raise ValueError( - "API key is required to access Google Maps API. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project" + "API key is required to access Google Maps API. To get an API key " + "and enable Map Tiles API, visit " + "https://developers.google.com/maps/get-started#create-project" ) allowed_map_types = [ @@ -16228,13 +16245,15 @@ def google_map_tiles( "streetview", ] - # Support map type as a string with or without 'google.', such as 'Google Roadmap', 'Google.Roadmap', or 'Roadmap' + # Support map type as a string with or without 'google.', + # such as 'Google Roadmap', 'Google.Roadmap', or 'Roadmap' if isinstance(map_type, str): map_type = map_type.lower().replace("google.", "").replace("google", "").strip() if map_type not in allowed_map_types: raise ValueError( - "mapType must be one of 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'" + "mapType must be one of 'roadmap', 'satellite', 'terrain', " + "'hybrid', 'traffic', 'streetview'" ) tile_args = {} @@ -16311,7 +16330,9 @@ def google_maps_api_key(token_name="MAPS_API_KEY"): Retrieves the Google Maps API key from the environment or Colab user data. Args: - token_name (str, optional): The name of the environment variable or Colab user data key where the API key is stored. Defaults to 'MAPS_API_KEY'. + token_name (str, optional): The name of the environment variable or + Colab user data key where the API key is stored. + Defaults to 'MAPS_API_KEY'. Returns: str: The API key, or None if it could not be found. From afa285db08643203989b30a0a057280c979a36c6 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 28 Apr 2024 23:58:15 -0400 Subject: [PATCH 12/18] Improve google_maps_api_key function --- geemap/common.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/geemap/common.py b/geemap/common.py index f147dfc533..a0c6f8ab5c 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16325,12 +16325,12 @@ def google_map_tiles( return gmap_providers.get(map_type) -def google_maps_api_key(token_name="MAPS_API_KEY"): +def google_maps_api_key(lookup_key: str = "MAPS_API_KEY") -> Optional[str]: """ Retrieves the Google Maps API key from the environment or Colab user data. Args: - token_name (str, optional): The name of the environment variable or + lookup_key (str, optional): The name of the environment variable or Colab user data key where the API key is stored. Defaults to 'MAPS_API_KEY'. @@ -16340,8 +16340,8 @@ def google_maps_api_key(token_name="MAPS_API_KEY"): if in_colab_shell(): from google.colab import userdata - MAPS_API_KEY = userdata.get(token_name) + env_value = userdata.get(lookup_key) else: - MAPS_API_KEY = os.environ.get(token_name, None) + env_value = os.environ.get(lookup_key, None) - return MAPS_API_KEY + return env_value From 047a0ce619e52677d77693a76a88024a9c893190 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Tue, 7 May 2024 00:25:53 -0400 Subject: [PATCH 13/18] Add GoogleMapsTileProvider class --- geemap/basemaps.py | 270 ++++++++++++++++++++++++++++++++++++--------- geemap/common.py | 156 -------------------------- geemap/core.py | 9 +- 3 files changed, 225 insertions(+), 210 deletions(-) diff --git a/geemap/basemaps.py b/geemap/basemaps.py index 0e62c941dc..dce7b62f18 100644 --- a/geemap/basemaps.py +++ b/geemap/basemaps.py @@ -20,64 +20,46 @@ import collections import os import requests +import sys import folium import ipyleaflet import xyzservices from .common import ( check_package, planet_tiles, - google_map_tiles, - google_maps_api_key, ) +from typing import Optional, Any +from xyzservices import TileProvider + + +XYZ_TILES = { + "OpenStreetMap": { + "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + "attribution": "OpenStreetMap", + "name": "OpenStreetMap", + }, + "ROADMAP": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldStreetMap", + }, + "SATELLITE": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldImagery", + }, + "TERRAIN": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldTopoMap", + }, + "HYBRID": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", + "attribution": "Esri", + "name": "Esri.WorldImagery", + }, +} -MAPS_API_KEY = google_maps_api_key() - -if MAPS_API_KEY is None: - - XYZ_TILES = { - "OpenStreetMap": { - "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - "attribution": "OpenStreetMap", - "name": "OpenStreetMap", - }, - "ROADMAP": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldStreetMap", - }, - "SATELLITE": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldImagery", - }, - "TERRAIN": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldTopoMap", - }, - "HYBRID": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - "attribution": "Esri", - "name": "Esri.WorldImagery", - }, - } - -else: - XYZ_TILES = { - "OpenStreetMap": { - "url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - "attribution": "OpenStreetMap", - "name": "OpenStreetMap", - }, - } - - gmap_providers = google_map_tiles(api_key=MAPS_API_KEY) - for key, provider in gmap_providers.items(): - XYZ_TILES[provider["name"]] = { - "url": provider.build_url(), - "attribution": provider["attribution"], - "name": provider["name"], - } # Custom WMS tile services. WMS_TILES = { @@ -262,6 +244,194 @@ custom_tiles = {"xyz": XYZ_TILES, "wms": WMS_TILES} +class GoogleMapsTileProvider(TileProvider): + """Google Maps TileProvider.""" + + def __init__( + self, + map_type: str = "Roadmap", + language: str = "en-Us", + region: str = "US", + api_key: Optional[str] = None, + **kwargs: Any, + ): + """ + Generates Google Map tiles using the provided parameters. To get an API key + and enable Map Tiles API, visit + https://developers.google.com/maps/get-started#create-project. + You can set the API key using the environment variable `MAPS_API_KEY` + or by passing it as an argument. + + Args: + map_type (str, optional): The type of map to generate. Options are + 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'. + Defaults to 'roadmap'. + language (str, optional): An IETF language tag that specifies the + language used to display information on the tiles, such as 'zh-Cn'. + Defaults to 'en-Us'. + region (str, optional): A Common Locale Data Repository region + identifier (two uppercase letters) that represents the physical + location of the user. Defaults to 'US'. + api_key (str, optional): The API key to use for the Google Maps API. + If not provided, it will try to get it from the environment or + Colab user data with the key 'MAPS_API_KEY'. Defaults to None. + **kwargs: Additional parameters to pass to the map generation. For more + info, visit https://bit.ly/3UhbZKU + + Raises: + ValueError: If the API key is not provided and cannot be found in the + environment or Colab user data. + ValueError: If the map_type is not one of the allowed types. + + Example: + >>> from geemap.basemaps import GoogleMapsTileProvider + >>> m = geemap.Map() + >>> basemap = GoogleMapsTileProvider(map_type='roadmap', + language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) + >>> m.add_basemap(basemap) + + Returns: + TileProvider object: A TileProvider object with the Google Maps tile. + """ + + if api_key is None: + + if "google.colab" in sys.modules: + from google.colab import userdata + + api_key = userdata.get("MAPS_API_KEY") + else: + api_key = os.environ.get("MAPS_API_KEY") + + if api_key is None: + raise ValueError( + "API key is required to access Google Maps API. To get an API key " + "and enable Map Tiles API, visit " + "https://developers.google.com/maps/get-started#create-project" + ) + + allowed_map_types = [ + "roadmap", + "satellite", + "terrain", + "hybrid", + "traffic", + "streetview", + ] + + # Support map type as a string with or without 'google.', + # such as 'Google Roadmap', 'Google.Roadmap', or 'Roadmap' + if isinstance(map_type, str): + map_type = ( + map_type.lower().replace("google.", "").replace("google", "").strip() + ) + + if map_type not in allowed_map_types: + raise ValueError( + "map_type must be one of 'roadmap', 'satellite', 'terrain', " + "'hybrid', 'traffic', 'streetview'" + ) + else: + raise ValueError("map_type must be a string") + + tile_args = {} + + # Define the parameters for each map type + for m_type in allowed_map_types: + + mapType = m_type + layerTypes = None + + if m_type == "hybrid": + mapType = "satellite" + layerTypes = ["layerRoadmap"] + elif m_type == "terrain": + layerTypes = ["layerRoadmap"] + elif m_type == "traffic": + mapType = "roadmap" + layerTypes = ["layerTraffic"] + elif m_type == "streetview": + mapType = "roadmap" + layerTypes = ["layerStreetview"] + + tile_args[m_type] = { + "mapType": mapType, + "language": language, + "region": region, + "layerTypes": layerTypes, + **kwargs, + } + + if tile_args[m_type].get("layerTypes") is None: + del tile_args[m_type]["layerTypes"] + + args = tile_args[map_type] + response = requests.post( + f"https://tile.googleapis.com/v1/createSession?key={api_key}", + headers={"Content-Type": "application/json"}, + json=args, + ) + + if response.status_code == 200: + res = response.json() + super().__init__( + { + "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", + "attribution": f"© Google {map_type.capitalize()}", + "accessToken": api_key, + "name": f"Google.{map_type.capitalize()}", + "ext": res["imageFormat"], + "tileSize": res["tileWidth"], + } + ) + + +def google_map_tiles( + language: str = "en-Us", + region: str = "US", + api_key: Optional[str] = None, + **kwargs: Any, +): + """ + Generates a dictionary of Google Map tile providers for different map types. + + Args: + language (str, optional): An IETF language tag that specifies the + language used to display information on the tiles, such as 'zh-Cn'. + Defaults to 'en-Us'. + region (str, optional): A Common Locale Data Repository region + identifier (two uppercase letters) that represents the physical + location of the user. Defaults to 'US'. + api_key (str, optional): The API key to use for the Google Maps API. + If not provided, it will try to get it from the environment or + Colab user data with the key 'MAPS_API_KEY'. Defaults to None. + **kwargs: Additional parameters to pass to the map generation. For more + info, visit https://bit.ly/3UhbZKU + + Returns: + dict: A dictionary where the keys are the map types + ('roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview') + and the values are the corresponding GoogleMapsTileProvider objects. + """ + allowed_map_types = [ + "roadmap", + "satellite", + "terrain", + "hybrid", + "traffic", + "streetview", + ] + + gmap_providers = {} + + for m_type in allowed_map_types: + gmap_providers[m_type] = GoogleMapsTileProvider( + map_type=m_type, language=language, region=region, api_key=api_key, **kwargs + ) + + return gmap_providers + + def get_xyz_dict(free_only=True, france=False): """Returns a dictionary of xyz services. diff --git a/geemap/common.py b/geemap/common.py index a0c6f8ab5c..4052fb448c 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16169,162 +16169,6 @@ def is_on_aws(): return on_aws -def google_map_tiles( - map_type: str = None, - language: str = "en-Us", - region: str = "US", - api_key: Optional[str] = None, - **kwargs: Any, -): - """ - Generates Google Map tiles using the provided parameters. To get an API key - and enable Map Tiles API, visit - https://developers.google.com/maps/get-started#create-project. - You can set the API key using the environment variable `MAPS_API_KEY` - or by passing it as an argument. - - Args: - map_type (str, optional): The type of map to generate. Options are - 'roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview'. - Defaults to None, which generates all map types, and returns a - dictionary of TileProvider objects. - language (str, optional): An IETF language tag that specifies the - language used to display information on the tiles, such as 'zh-Cn'. - Defaults to 'en-Us'. - region (str, optional): A Common Locale Data Repository region - identifier (two uppercase letters) that represents the physical - location of the user. Defaults to 'US'. - api_key (str, optional): The API key to use for the Google Maps API. - If not provided, it will try to get it from the environment or - Colab user data. Defaults to None. - **kwargs: Additional parameters to pass to the map generation. For more - info, visit https://bit.ly/3UhbZKU - - Raises: - ValueError: If the API key is not provided and cannot be found in the - environment or Colab user data. - ValueError: If the map_type is not one of the allowed types. - - Example: - >>> import geemap - >>> m = geemap.Map() - >>> basemap = geemap.google_map_tiles(map_type='roadmap', - language="en-Us", region="US", scale="scaleFactor2x", highDpi=True) - >>> m.add_basemap(basemap) - - Returns: - TileProvider | dict: A TileProvider object with the generated map, or a - dictionary of TileProvider objects for all map types, - or None if the map could not be generated. - """ - - from xyzservices import TileProvider - - if api_key is None: - - if in_colab_shell(): - from google.colab import userdata - - api_key = userdata.get("MAPS_API_KEY") - else: - api_key = os.environ.get("MAPS_API_KEY") - - if api_key is None: - raise ValueError( - "API key is required to access Google Maps API. To get an API key " - "and enable Map Tiles API, visit " - "https://developers.google.com/maps/get-started#create-project" - ) - - allowed_map_types = [ - "roadmap", - "satellite", - "terrain", - "hybrid", - "traffic", - "streetview", - ] - - # Support map type as a string with or without 'google.', - # such as 'Google Roadmap', 'Google.Roadmap', or 'Roadmap' - if isinstance(map_type, str): - map_type = map_type.lower().replace("google.", "").replace("google", "").strip() - - if map_type not in allowed_map_types: - raise ValueError( - "mapType must be one of 'roadmap', 'satellite', 'terrain', " - "'hybrid', 'traffic', 'streetview'" - ) - - tile_args = {} - - # Define the parameters for each map type - for m_type in allowed_map_types: - - mapType = m_type - layerTypes = None - - if m_type == "hybrid": - mapType = "satellite" - layerTypes = ["layerRoadmap"] - elif m_type == "terrain": - layerTypes = ["layerRoadmap"] - elif m_type == "traffic": - mapType = "roadmap" - layerTypes = ["layerTraffic"] - elif m_type == "streetview": - mapType = "roadmap" - layerTypes = ["layerStreetview"] - - tile_args[m_type] = { - "mapType": mapType, - "language": language, - "region": region, - "layerTypes": layerTypes, - **kwargs, - } - - if tile_args[m_type].get("layerTypes") is None: - del tile_args[m_type]["layerTypes"] - - gmap_providers = {} - - for m_type in allowed_map_types: - - # If map_type is provided, only generate the specified map - if map_type is not None and m_type != map_type: - continue - - args = tile_args[m_type] - response = requests.post( - f"https://tile.googleapis.com/v1/createSession?key={api_key}", - headers={"Content-Type": "application/json"}, - json=args, - ) - - if response.status_code == 200: - res = response.json() - gmap_provider = TileProvider( - { - "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", - "attribution": f"© Google {m_type.capitalize()}", - "accessToken": api_key, - "name": f"Google.{m_type.capitalize()}", - "ext": res["imageFormat"], - "tileSize": res["tileWidth"], - } - ) - gmap_providers[m_type] = gmap_provider - else: - display(response.text) - gmap_provider = None - - if map_type is None: # Return all map types - return gmap_providers - else: # Return the specified map type - return gmap_providers.get(map_type) - - def google_maps_api_key(lookup_key: str = "MAPS_API_KEY") -> Optional[str]: """ Retrieves the Google Maps API key from the environment or Colab user data. diff --git a/geemap/core.py b/geemap/core.py index f04c2ca99a..f515755f65 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -870,10 +870,11 @@ def _get_available_basemaps(self) -> Dict[str, Any]: tile_info["url"] = tile_info.build_url() ret_dict[tile_info["name"]] = tile_info - if "Google.Roadmap" in basemaps.XYZ_TILES: - for key in basemaps.XYZ_TILES: - if key.startswith("Google"): - ret_dict[key] = basemaps.XYZ_TILES[key] + if common.google_maps_api_key() is not None: + for tile_info in basemaps.google_map_tiles().values(): + tile_info["url"] = tile_info.build_url() + ret_dict[tile_info["name"]] = tile_info + extra_dict = {k: ret_dict[v] for k, v in self._BASEMAP_ALIASES.items()} return {**extra_dict, **ret_dict} From 9c0397e876c6d6cef9d6a25334d61934201a036d Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Tue, 7 May 2024 11:57:52 -0400 Subject: [PATCH 14/18] Add backward compatibility for map alias --- geemap/basemaps.py | 58 +++++++++++++++++++++++++++++++++++++++------- geemap/core.py | 12 +++++----- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/geemap/basemaps.py b/geemap/basemaps.py index dce7b62f18..8f3bc40136 100644 --- a/geemap/basemaps.py +++ b/geemap/basemaps.py @@ -24,11 +24,8 @@ import folium import ipyleaflet import xyzservices -from .common import ( - check_package, - planet_tiles, -) -from typing import Optional, Any +from .common import check_package, planet_tiles, google_maps_api_key +from typing import Optional, Any, Dict, Union from xyzservices import TileProvider @@ -410,7 +407,7 @@ def google_map_tiles( Returns: dict: A dictionary where the keys are the map types - ('roadmap', 'satellite', 'terrain', 'hybrid', 'traffic', 'streetview') + ('roadmap', 'satellite', 'terrain', 'hybrid') and the values are the corresponding GoogleMapsTileProvider objects. """ allowed_map_types = [ @@ -418,8 +415,8 @@ def google_map_tiles( "satellite", "terrain", "hybrid", - "traffic", - "streetview", + # "traffic", + # "streetview", ] gmap_providers = {} @@ -432,6 +429,51 @@ def google_map_tiles( return gmap_providers +def check_basemap_alias( + basemap: str, +) -> str: + """ + Checks if the provided basemap is an alias and returns the corresponding basemap name. + + Args: + basemap (str): The basemap to check. + + Returns: + str: The corresponding basemap name if the input is an alias, otherwise the input itself. + """ + + allowed_aliases = { + "DEFAULT": { + "Google": "Google.Roadmap", + "Esri": "OpenStreetMap.Mapnik", + }, + "ROADMAP": { + "Google": "Google.Roadmap", + "Esri": "Esri.WorldStreetMap", + }, + "SATELLITE": { + "Google": "Google.Satellite", + "Esri": "Esri.WorldImagery", + }, + "TERRAIN": { + "Google": "Google.Terrain", + "Esri": "Esri.WorldTopoMap", + }, + "HYBRID": { + "Google": "Google.Hybrid", + "Esri": "Esri.WorldImagery", + }, + } + + if isinstance(basemap, str) and basemap.upper() in allowed_aliases: + if google_maps_api_key() is not None: + return allowed_aliases[basemap.upper()]["Google"] + else: + return allowed_aliases[basemap.upper()]["Esri"] + else: + return basemap + + def get_xyz_dict(free_only=True, france=False): """Returns a dictionary of xyz services. diff --git a/geemap/core.py b/geemap/core.py index f515755f65..2e9f4e88ec 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -470,12 +470,12 @@ def __init__(self, **kwargs): if "basemap" not in kwargs and "Google.Roadmap" in self._available_basemaps: kwargs["basemap"] = self._available_basemaps["Google.Roadmap"] - elif ( - "basemap" in kwargs - and isinstance(kwargs["basemap"], str) - and kwargs["basemap"] in self._available_basemaps - ): - kwargs["basemap"] = self._available_basemaps.get(kwargs["basemap"]) + elif "basemap" in kwargs and isinstance(kwargs["basemap"], str): + kwargs["basemap"] = basemaps.check_basemap_alias(kwargs["basemap"]) + if kwargs["basemap"] in self._available_basemaps: + kwargs["basemap"] = self._available_basemaps.get(kwargs["basemap"]) + else: + raise ValueError(f"Basemap {kwargs['basemap']} not found.") if "width" in kwargs: self.width: str = kwargs.pop("width", "100%") From 88e53f10c24d5fe6505a4bfd09c6d931983fb854 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Tue, 7 May 2024 12:56:37 -0400 Subject: [PATCH 15/18] Fix add_basemap Google tiles issue --- geemap/geemap.py | 43 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/geemap/geemap.py b/geemap/geemap.py index ae17ba6825..08fdbc333d 100644 --- a/geemap/geemap.py +++ b/geemap/geemap.py @@ -113,16 +113,6 @@ def __init__(self, **kwargs): if "max_zoom" not in kwargs: kwargs["max_zoom"] = 24 - # Use any basemap available through the basemap module, such as 'ROADMAP', 'OpenTopoMap' - if "basemap" in kwargs: - kwargs["basemap"] = check_basemap(kwargs["basemap"]) - if kwargs["basemap"] in basemaps.keys(): - kwargs["basemap"] = get_basemap(kwargs["basemap"]) - kwargs["add_google_map"] = False - else: - kwargs.pop("basemap") - elif "Google.Roadmap" in basemaps.keys(): - kwargs["basemap"] = get_basemap("Google.Roadmap") self._xyz_dict = get_xyz_dict() self.baseclass = "ipyleaflet" @@ -412,34 +402,26 @@ def get_scale(self): getScale = get_scale - def add_basemap(self, basemap="ROADMAP", show=True, **kwargs): + def add_basemap( + self, basemap: Optional[str] = "ROADMAP", show: Optional[bool] = True, **kwargs + ) -> None: """Adds a basemap to the map. Args: basemap (str, optional): Can be one of string from basemaps. Defaults to 'ROADMAP'. - visible (bool, optional): Whether the basemap is visible or not. Defaults to True. + show (bool, optional): Whether the basemap is visible or not. Defaults to True. **kwargs: Keyword arguments for the TileLayer. """ import xyzservices + from .basemaps import check_basemap_alias, GoogleMapsTileProvider try: layer_names = self.get_layer_names() - map_dict = { - "ROADMAP": "Esri.WorldStreetMap", - "SATELLITE": "Esri.WorldImagery", - "TERRAIN": "Esri.WorldTopoMap", - "HYBRID": "Esri.WorldImagery", - } + basemap = check_basemap_alias(basemap) - if isinstance(basemap, str): - if basemap.upper() in map_dict: - if basemap in os.environ: - if "name" in kwargs: - kwargs["name"] = basemap - basemap = os.environ[basemap] - else: - basemap = map_dict[basemap.upper()] + if isinstance(basemap, str) and "Google" in basemap: + basemap = GoogleMapsTileProvider(basemap, **kwargs) if isinstance(basemap, xyzservices.TileProvider): name = basemap.name @@ -947,18 +929,13 @@ def _on_basemap_changed(self, basemap_name): bounds = [bounds[0][1], bounds[0][0], bounds[1][1], bounds[1][0]] self.zoom_to_bounds(bounds) - def add_basemap_widget(self, value="OpenStreetMap", position="topright"): + def add_basemap_widget(self, position="topright"): """Add the Basemap GUI to the map. Args: - value (str): The default value from basemaps to select. Defaults to "OpenStreetMap". position (str, optional): The position of the Inspector GUI. Defaults to "topright". """ - super()._add_basemap_selector( - position, basemaps=list(basemaps.keys()), value=value - ) - if basemap_selector := self._basemap_selector: - basemap_selector.on_basemap_changed = self._on_basemap_changed + super()._add_basemap_selector(position=position) def add_draw_control(self, position="topleft"): """Add a draw control to the map From a555ac1ac419373a4eabc608a9300725a825411c Mon Sep 17 00:00:00 2001 From: Nathaniel Schmitz Date: Fri, 24 May 2024 17:39:00 +0000 Subject: [PATCH 16/18] Reduce dependencies on hardcoded map name prefixes --- .gitignore | 1 + geemap/basemaps.py | 191 ++++++++++++--------------------------------- geemap/common.py | 17 ++-- geemap/core.py | 71 ++++++++--------- geemap/geemap.py | 10 +-- 5 files changed, 96 insertions(+), 194 deletions(-) diff --git a/.gitignore b/.gitignore index fb6887e4c6..ef3ab1d719 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,4 @@ ENV/ # IDE settings .vscode/ docs/changelog_update.py +oryx-build-commands.txt diff --git a/geemap/basemaps.py b/geemap/basemaps.py index 8f3bc40136..315a62e7e5 100644 --- a/geemap/basemaps.py +++ b/geemap/basemaps.py @@ -19,15 +19,14 @@ import collections import os -import requests -import sys +from typing import Any, Optional + import folium import ipyleaflet +import requests import xyzservices -from .common import check_package, planet_tiles, google_maps_api_key -from typing import Optional, Any, Dict, Union -from xyzservices import TileProvider +from .common import check_package, get_google_maps_api_key, planet_tiles XYZ_TILES = { "OpenStreetMap": { @@ -241,12 +240,25 @@ custom_tiles = {"xyz": XYZ_TILES, "wms": WMS_TILES} -class GoogleMapsTileProvider(TileProvider): +class GoogleMapsTileProvider(xyzservices.TileProvider): """Google Maps TileProvider.""" + MAP_TYPE_CONFIG = { + "roadmap": {"mapType": "roadmap"}, + "satellite": {"mapType": "satellite"}, + "terrain": { + "mapType": "terrain", + "layerTypes": ["layerRoadmap"], + }, + "hybrid": { + "mapType": "satellite", + "layerTypes": ["layerRoadmap"], + }, + } + def __init__( self, - map_type: str = "Roadmap", + map_type: str = "roadmap", language: str = "en-Us", region: str = "US", api_key: Optional[str] = None, @@ -256,8 +268,8 @@ def __init__( Generates Google Map tiles using the provided parameters. To get an API key and enable Map Tiles API, visit https://developers.google.com/maps/get-started#create-project. - You can set the API key using the environment variable `MAPS_API_KEY` - or by passing it as an argument. + You can set the API key using the environment variable + `GOOGLE_MAPS_API_KEY` or by passing it as an argument. Args: map_type (str, optional): The type of map to generate. Options are @@ -291,99 +303,50 @@ def __init__( TileProvider object: A TileProvider object with the Google Maps tile. """ - if api_key is None: - - if "google.colab" in sys.modules: - from google.colab import userdata - - api_key = userdata.get("MAPS_API_KEY") - else: - api_key = os.environ.get("MAPS_API_KEY") - - if api_key is None: + key = api_key or get_google_maps_api_key() + if key is None: raise ValueError( - "API key is required to access Google Maps API. To get an API key " - "and enable Map Tiles API, visit " + "API key is required to access Google Maps API. To get an API " + "key and enable Map Tiles API, visit " "https://developers.google.com/maps/get-started#create-project" ) - allowed_map_types = [ - "roadmap", - "satellite", - "terrain", - "hybrid", - "traffic", - "streetview", - ] - - # Support map type as a string with or without 'google.', - # such as 'Google Roadmap', 'Google.Roadmap', or 'Roadmap' - if isinstance(map_type, str): - map_type = ( - map_type.lower().replace("google.", "").replace("google", "").strip() - ) + if map_type not in self.MAP_TYPE_CONFIG: + raise ValueError(f"map_type must be one of: {self.MAP_TYPE_CONFIG.keys()}") - if map_type not in allowed_map_types: - raise ValueError( - "map_type must be one of 'roadmap', 'satellite', 'terrain', " - "'hybrid', 'traffic', 'streetview'" - ) - else: - raise ValueError("map_type must be a string") - - tile_args = {} - - # Define the parameters for each map type - for m_type in allowed_map_types: - - mapType = m_type - layerTypes = None - - if m_type == "hybrid": - mapType = "satellite" - layerTypes = ["layerRoadmap"] - elif m_type == "terrain": - layerTypes = ["layerRoadmap"] - elif m_type == "traffic": - mapType = "roadmap" - layerTypes = ["layerTraffic"] - elif m_type == "streetview": - mapType = "roadmap" - layerTypes = ["layerStreetview"] - - tile_args[m_type] = { - "mapType": mapType, + request_url = f"https://tile.googleapis.com/v1/createSession?key={key}" + response = requests.post( + url=request_url, + headers={"Content-Type": "application/json"}, + json={ + **self.MAP_TYPE_CONFIG[map_type], "language": language, "region": region, - "layerTypes": layerTypes, **kwargs, - } - - if tile_args[m_type].get("layerTypes") is None: - del tile_args[m_type]["layerTypes"] - - args = tile_args[map_type] - response = requests.post( - f"https://tile.googleapis.com/v1/createSession?key={api_key}", - headers={"Content-Type": "application/json"}, - json=args, + }, + timeout=3, ) - if response.status_code == 200: - res = response.json() + if response.status_code == requests.codes.ok: + json = response.json() + map_name = map_type.capitalize() super().__init__( { - "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={res['session']}&key={{accessToken}}", - "attribution": f"© Google {map_type.capitalize()}", - "accessToken": api_key, - "name": f"Google.{map_type.capitalize()}", - "ext": res["imageFormat"], - "tileSize": res["tileWidth"], + "url": f"https://tile.googleapis.com/v1/2dtiles/{{z}}/{{x}}/{{y}}?session={json['session']}&key={{accessToken}}", + "attribution": f"© Google {map_name}", + "accessToken": key, + "name": f"Google.{map_name}", + "ext": json["imageFormat"], + "tileSize": json["tileWidth"], } ) + else: + raise RuntimeError( + f"Error creating a Maps API session:\n{response.json()}." + ) -def google_map_tiles( +def get_google_map_tile_providers( language: str = "en-Us", region: str = "US", api_key: Optional[str] = None, @@ -410,18 +373,9 @@ def google_map_tiles( ('roadmap', 'satellite', 'terrain', 'hybrid') and the values are the corresponding GoogleMapsTileProvider objects. """ - allowed_map_types = [ - "roadmap", - "satellite", - "terrain", - "hybrid", - # "traffic", - # "streetview", - ] - gmap_providers = {} - for m_type in allowed_map_types: + for m_type in GoogleMapsTileProvider.MAP_TYPE_CONFIG: gmap_providers[m_type] = GoogleMapsTileProvider( map_type=m_type, language=language, region=region, api_key=api_key, **kwargs ) @@ -429,51 +383,6 @@ def google_map_tiles( return gmap_providers -def check_basemap_alias( - basemap: str, -) -> str: - """ - Checks if the provided basemap is an alias and returns the corresponding basemap name. - - Args: - basemap (str): The basemap to check. - - Returns: - str: The corresponding basemap name if the input is an alias, otherwise the input itself. - """ - - allowed_aliases = { - "DEFAULT": { - "Google": "Google.Roadmap", - "Esri": "OpenStreetMap.Mapnik", - }, - "ROADMAP": { - "Google": "Google.Roadmap", - "Esri": "Esri.WorldStreetMap", - }, - "SATELLITE": { - "Google": "Google.Satellite", - "Esri": "Esri.WorldImagery", - }, - "TERRAIN": { - "Google": "Google.Terrain", - "Esri": "Esri.WorldTopoMap", - }, - "HYBRID": { - "Google": "Google.Hybrid", - "Esri": "Esri.WorldImagery", - }, - } - - if isinstance(basemap, str) and basemap.upper() in allowed_aliases: - if google_maps_api_key() is not None: - return allowed_aliases[basemap.upper()]["Google"] - else: - return allowed_aliases[basemap.upper()]["Esri"] - else: - return basemap - - def get_xyz_dict(free_only=True, france=False): """Returns a dictionary of xyz services. diff --git a/geemap/common.py b/geemap/common.py index 4052fb448c..6c0ec304c1 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16169,14 +16169,14 @@ def is_on_aws(): return on_aws -def google_maps_api_key(lookup_key: str = "MAPS_API_KEY") -> Optional[str]: +def get_google_maps_api_key(key: str = "GOOGLE_MAPS_API_KEY") -> Optional[str]: """ Retrieves the Google Maps API key from the environment or Colab user data. Args: - lookup_key (str, optional): The name of the environment variable or - Colab user data key where the API key is stored. - Defaults to 'MAPS_API_KEY'. + key (str, optional): The name of the environment variable or Colab user + data key where the API key is stored. Defaults to + 'GOOGLE_MAPS_API_KEY'. Returns: str: The API key, or None if it could not be found. @@ -16184,8 +16184,7 @@ def google_maps_api_key(lookup_key: str = "MAPS_API_KEY") -> Optional[str]: if in_colab_shell(): from google.colab import userdata - env_value = userdata.get(lookup_key) - else: - env_value = os.environ.get(lookup_key, None) - - return env_value + if api_key := userdata.get(key): + return api_key + + return os.environ.get(key, None) diff --git a/geemap/core.py b/geemap/core.py index 2e9f4e88ec..4e5d73e206 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -402,23 +402,13 @@ class Map(ipyleaflet.Map, MapInterface): "scroll_wheel_zoom": True, } - if common.google_maps_api_key() is not None: - _BASEMAP_ALIASES: Dict[str, str] = { - "OpenStreetMap": "OpenStreetMap.Mapnik", - "Google.Roadmap": "Google.Roadmap", - "Google.Satellite": "Google.Satellite", - "Google.Terrain": "Google.Terrain", - "Google.Hybrid": "Google.Hybrid", - } - - else: - _BASEMAP_ALIASES: Dict[str, str] = { - "OpenStreetMap": "OpenStreetMap.Mapnik", - "Esri.WorldStreetMap": "Esri.WorldStreetMap", - "Esri.WorldImagery": "Esri.WorldImagery", - "Esri.WorldTopoMap": "Esri.WorldTopoMap", - "Esri.WorldImagery": "Esri.WorldImagery", - } + _BASEMAP_ALIASES: Dict[str, List[str]] = { + "DEFAULT": ["Google.Roadmap", "OpenStreetMap.Mapnik"], + "ROADMAP": ["Google.Roadmap", "Esri.WorldStreetMap"], + "SATELLITE": ["Google.Satellite", "Esri.WorldImagery"], + "TERRAIN": ["Google.Terrain", "Esri.WorldTopoMap"], + "HYBRID": ["Google.Hybrid", "Esri.WorldImagery"], + } _USER_AGENT_PREFIX = "geemap-core" @@ -468,14 +458,9 @@ def _basemap_selector(self) -> Optional[map_widgets.Basemap]: def __init__(self, **kwargs): self._available_basemaps = self._get_available_basemaps() - if "basemap" not in kwargs and "Google.Roadmap" in self._available_basemaps: - kwargs["basemap"] = self._available_basemaps["Google.Roadmap"] - elif "basemap" in kwargs and isinstance(kwargs["basemap"], str): - kwargs["basemap"] = basemaps.check_basemap_alias(kwargs["basemap"]) - if kwargs["basemap"] in self._available_basemaps: - kwargs["basemap"] = self._available_basemaps.get(kwargs["basemap"]) - else: - raise ValueError(f"Basemap {kwargs['basemap']} not found.") + # Use the first basemap in the list of available basemaps. + if "basemap" not in kwargs: + kwargs["basemap"] = next(iter(self._available_basemaps.values())) if "width" in kwargs: self.width: str = kwargs.pop("width", "100%") @@ -865,27 +850,35 @@ def _replace_basemap(self, basemap_name: str) -> None: def _get_available_basemaps(self) -> Dict[str, Any]: """Convert xyz tile services to a dictionary of basemaps.""" + tile_providers = list(basemaps.get_xyz_dict().values()) + if common.get_google_maps_api_key(): + tile_providers = tile_providers + list( + basemaps.get_google_map_tile_providers().values() + ) + ret_dict = {} - for tile_info in basemaps.get_xyz_dict().values(): + for tile_info in tile_providers: tile_info["url"] = tile_info.build_url() ret_dict[tile_info["name"]] = tile_info - if common.google_maps_api_key() is not None: - for tile_info in basemaps.google_map_tiles().values(): - tile_info["url"] = tile_info.build_url() - ret_dict[tile_info["name"]] = tile_info - - extra_dict = {k: ret_dict[v] for k, v in self._BASEMAP_ALIASES.items()} - return {**extra_dict, **ret_dict} + # Each alias needs to point to a single map. For each alias, pick the + # first aliased map in `self._BASEMAP_ALIASES`. + aliased_maps = {} + for alias, maps in self._BASEMAP_ALIASES.items(): + for map_name in maps: + if provider := ret_dict.get(map_name): + aliased_maps[alias] = provider + break + return {**aliased_maps, **ret_dict} def _get_preferred_basemap_name(self, basemap_name: str) -> str: """Returns the aliased basemap name.""" - try: - return list(self._BASEMAP_ALIASES.keys())[ - list(self._BASEMAP_ALIASES.values()).index(basemap_name) - ] - except ValueError: - return basemap_name + reverse_aliases = {} + for alias, maps in self._BASEMAP_ALIASES.items(): + for map_name in maps: + if map_name not in reverse_aliases: + reverse_aliases[map_name] = alias + return reverse_aliases.get(basemap_name, basemap_name) def _on_layers_change(self, change) -> None: del change # Unused. diff --git a/geemap/geemap.py b/geemap/geemap.py index 08fdbc333d..3e06493219 100644 --- a/geemap/geemap.py +++ b/geemap/geemap.py @@ -413,15 +413,15 @@ def add_basemap( **kwargs: Keyword arguments for the TileLayer. """ import xyzservices - from .basemaps import check_basemap_alias, GoogleMapsTileProvider try: layer_names = self.get_layer_names() - basemap = check_basemap_alias(basemap) - - if isinstance(basemap, str) and "Google" in basemap: - basemap = GoogleMapsTileProvider(basemap, **kwargs) + if isinstance(basemap, str): + for map_name, tile_provider in self._available_basemaps.items(): + if basemap.upper() == map_name.upper(): + basemap = tile_provider + break if isinstance(basemap, xyzservices.TileProvider): name = basemap.name From fe2689d83ce50986d6791fb97bab0ec17537136c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 17:39:53 +0000 Subject: [PATCH 17/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- geemap/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geemap/common.py b/geemap/common.py index 6c0ec304c1..545b4db44c 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -16186,5 +16186,5 @@ def get_google_maps_api_key(key: str = "GOOGLE_MAPS_API_KEY") -> Optional[str]: if api_key := userdata.get(key): return api_key - + return os.environ.get(key, None) From 02a3639674937ac19cb0f4d96d762a9640845460 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Fri, 24 May 2024 21:24:37 -0400 Subject: [PATCH 18/18] Allow basemap as a string --- geemap/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/geemap/core.py b/geemap/core.py index 4e5d73e206..2cf8896e02 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -461,6 +461,9 @@ def __init__(self, **kwargs): # Use the first basemap in the list of available basemaps. if "basemap" not in kwargs: kwargs["basemap"] = next(iter(self._available_basemaps.values())) + elif "basemap" in kwargs and isinstance(kwargs["basemap"], str): + if kwargs["basemap"] in self._available_basemaps: + kwargs["basemap"] = self._available_basemaps.get(kwargs["basemap"]) if "width" in kwargs: self.width: str = kwargs.pop("width", "100%")