Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions bindings/pydeck/pydeck/bindings/deck.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def __init__(
Dictionary of geospatial API service providers, where the keys are ``mapbox``, ``google_maps``, or ``carto``
and the values are the API key. Defaults to None if not set. Any of the environment variables
``MAPBOX_API_KEY``, ``GOOGLE_MAPS_API_KEY``, and ``CARTO_API_KEY`` can be set instead of hardcoding the key here.
If using Google Maps, you can also provide a ``google_maps_map_id`` in this dictionary to enable
Vector Map features (like 3D tilt and rotation), or set the ``GOOGLE_MAPS_MAP_ID`` environment variable.
map_provider : str, default 'carto'
If multiple API keys are set (e.g., both Mapbox and Google Maps), inform pydeck which basemap provider to prefer.
Values can be ``carto``, ``mapbox``, ``google_maps``, or ``maplibre``.
Expand Down Expand Up @@ -151,16 +153,24 @@ def selected_data(self):

def _set_api_keys(self, api_keys: dict = None):
"""Sets API key for base map provider for both HTML embedding and the Jupyter widget"""
valid_providers = [p.value for p in BaseMapProvider]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old code called BaseMapProvider(k) on every key, which would raise ValueError for unknown keys — acting as input validation. The new code silently ignores unknown keys. This is needed because google_maps_map_id isn't a provider, but it also means typos like api_keys={"mapbx": "key"} will silently be ignored instead of raising an error. A better approach would be to define an allowlist of known non-provider keys:

  NON_PROVIDER_KEYS = {"google_maps_map_id"}
  for k in api_keys:
      if k not in NON_PROVIDER_KEYS:
          BaseMapProvider(k)  # will raise ValueError for unknown keys

for k in api_keys:
k and BaseMapProvider(k)
if k in valid_providers:
BaseMapProvider(k)
for provider in BaseMapProvider:
attr_name = f"{provider.value}_key"
provider_env_var = f"{provider.name}_API_KEY"
attr_value = api_keys.get(provider.value) or os.getenv(provider_env_var)
attr_value = api_keys.get(provider.value) or api_keys.get(attr_name) or os.getenv(provider_env_var)
setattr(self, attr_name, attr_value)
if has_jupyter_extra():
setattr(self.deck_widget, attr_name, attr_value)

# Handle google_maps_map_id specifically
gm_map_id = api_keys.get("google_maps_map_id") or os.getenv("GOOGLE_MAPS_MAP_ID")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, in general it is a bit weird to set the mapId in_set_api_keys as it isn't an API key. Better to do something like:

def _set_google_maps_map_id(self, map_id=None):
      self.google_maps_map_id = map_id or os.getenv("GOOGLE_MAPS_MAP_ID")
      if has_jupyter_extra():
          self.deck_widget.google_maps_map_id = self.google_maps_map_id

self.google_maps_map_id = gm_map_id
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use setattr like the current code?

if has_jupyter_extra():
self.deck_widget.google_maps_map_id = gm_map_id

def show(self):
"""Display current Deck object for a Jupyter notebook"""
# TODO: Jupyter-specific features not currently supported in pydeck v0.9.
Expand Down Expand Up @@ -238,6 +248,7 @@ def to_html(
deck_json,
mapbox_key=self.mapbox_key,
google_maps_key=self.google_maps_key,
google_maps_map_id=self.google_maps_map_id,
filename=filename,
open_browser=open_browser,
notebook_display=notebook_display,
Expand Down
1 change: 1 addition & 0 deletions bindings/pydeck/pydeck/bindings/json_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
IGNORE_KEYS = [
"mapbox_key",
"google_maps_key",
"google_maps_map_id",
"deck_widget",
"binary_data_sets",
"_binary_data",
Expand Down
4 changes: 4 additions & 0 deletions bindings/pydeck/pydeck/io/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def render_json_to_html(
json_input,
mapbox_key=None,
google_maps_key=None,
google_maps_map_id=None,
tooltip=True,
css_background_color=None,
custom_libraries=None,
Expand All @@ -72,6 +73,7 @@ def render_json_to_html(
html_str = js.render(
mapbox_key=mapbox_key,
google_maps_key=google_maps_key,
google_maps_map_id=google_maps_map_id,
json_input=json_input,
deckgl_jupyter_widget_bundle=cdn_picker(offline=offline),
deckgl_widget_css_url=CDN_CSS_URL,
Expand Down Expand Up @@ -126,6 +128,7 @@ def deck_to_html(
deck_json,
mapbox_key=None,
google_maps_key=None,
google_maps_map_id=None,
filename=None,
open_browser=False,
notebook_display=None,
Expand All @@ -144,6 +147,7 @@ def deck_to_html(
deck_json,
mapbox_key=mapbox_key,
google_maps_key=google_maps_key,
google_maps_map_id=google_maps_map_id,
tooltip=tooltip,
css_background_color=css_background_color,
custom_libraries=custom_libraries,
Expand Down
3 changes: 3 additions & 0 deletions bindings/pydeck/pydeck/io/templates/index.j2
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
{% if google_maps_key %}
googleMapsKey: '{{google_maps_key}}',
{% endif %}
{% if google_maps_map_id %}
googleMapsMapId: '{{google_maps_map_id}}',
{% endif %}
container,
jsonInput,
tooltip,
Expand Down
3 changes: 3 additions & 0 deletions bindings/pydeck/pydeck/widget/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class DeckGLWidget(DOMWidget):
See the ``Deck`` constructor.
google_maps_key : str, default ''
API key for Google Maps
google_maps_map_id : str, default ''
Optional ID for Google Maps Vector Maps
selected_data : list of dict, default []
Data selected on click, if the pydeck Jupyter widget is enabled for server use
"""
Expand All @@ -57,6 +59,7 @@ class DeckGLWidget(DOMWidget):
carto_key = Unicode("", allow_none=True).tag(sync=True)
mapbox_key = Unicode("", allow_none=True).tag(sync=True)
google_maps_key = Unicode("", allow_none=True).tag(sync=True)
google_maps_map_id = Unicode("", allow_none=True).tag(sync=True)

json_input = Unicode("").tag(sync=True)
data_buffer = Any(default_value=None, allow_none=True).tag(sync=True, **data_buffer_serialization)
Expand Down
2 changes: 2 additions & 0 deletions modules/jupyter-widget/src/lib/jupyter-transport-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ if (DOMWidgetModel) {
custom_libraries: [],
json_input: null,
mapbox_key: null,
google_maps_key: null,
google_maps_map_id: null,
selected_data: [],
data_buffer: null,
tooltip: null,
Expand Down
10 changes: 7 additions & 3 deletions modules/jupyter-widget/src/playground/create-deck.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ function createStandaloneFromProvider({
props,
mapboxApiKey,
googleMapsKey,
googleMapsMapId,
handleEvent,
getTooltip,
container,
Expand Down Expand Up @@ -183,7 +184,8 @@ function createStandaloneFromProvider({
return createGoogleMapsDeckOverlay({
...sharedProps,
...props,
googleMapsKey
googleMapsKey,
googleMapsMapId
});
case 'maplibre':
log.info('Using MapLibre')();
Expand All @@ -202,9 +204,10 @@ function createStandaloneFromProvider({
}
}

function createDeck({
export function createDeck({
mapboxApiKey,
googleMapsKey,
googleMapsMapId,
container,
jsonInput,
tooltip,
Expand Down Expand Up @@ -251,6 +254,7 @@ function createDeck({
props,
mapboxApiKey,
googleMapsKey,
googleMapsMapId,
handleEvent,
getTooltip,
container,
Expand Down Expand Up @@ -284,4 +288,4 @@ function createDeck({
return deckgl;
}

export {createDeck, updateDeck, jsonConverter};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change?

export {updateDeck, jsonConverter};
16 changes: 14 additions & 2 deletions modules/jupyter-widget/src/playground/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,16 @@ export function initPlayground() {
Transport.setCallbacks({
onInitialize({transport}) {
// Extract "deck.gl playground" props
const {width, height, customLibraries, mapboxApiKey, jsonInput, tooltip} =
getPlaygroundProps(transport);
const {
width,
height,
customLibraries,
mapboxApiKey,
googleMapsKey,
googleMapsMapId,
jsonInput,
tooltip
} = getPlaygroundProps(transport);

// Load mapbox CSS
loadMapboxCSS();
Expand All @@ -31,6 +39,8 @@ export function initPlayground() {

const deck = createDeck({
mapboxApiKey,
googleMapsKey,
googleMapsMapId,
container: deckContainer,
jsonInput: jsonProps,
tooltip,
Expand Down Expand Up @@ -120,6 +130,8 @@ function getPlaygroundProps(transport) {
height: jupyterModel.get('height'),
customLibraries: jupyterModel.get('custom_libraries'),
mapboxApiKey: jupyterModel.get('mapbox_key'),
googleMapsKey: jupyterModel.get('google_maps_key'),
googleMapsMapId: jupyterModel.get('google_maps_map_id'),
jsonInput: jupyterModel.get('json_input'),
tooltip: jupyterModel.get('tooltip')
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function createGoogleMapsDeckOverlay({
onComplete,
getTooltip,
googleMapsKey,
googleMapsMapId,
layers,
mapStyle = 'satellite',
initialViewState = {latitude: 0, longitude: 0, zoom: 1}
Expand All @@ -27,6 +28,9 @@ export function createGoogleMapsDeckOverlay({
mapTypeId: mapStyle,
zoom: initialViewState.zoom
};
if (googleMapsMapId) {
view.mapId = googleMapsMapId;
}

const map = new window.google.maps.Map(container, view);
deckOverlay.setMap(map);
Expand Down