Skip to content
Merged
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
57 changes: 57 additions & 0 deletions bindings/pydeck/examples/maplibre_globe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
GlobeView
=========

Over 33,000 power plants of the world plotted by their production capacity (given by height)
and fuel type (green if renewable) on a MapLibre globe view.

This example demonstrates using MapLibre's globe projection with deck.gl layers by setting
map_provider='maplibre' and map_projection='globe'. The globe view uses MapLibre's
MapboxOverlay with interleaved rendering for optimal performance.
"""
import pydeck as pdk
import pandas as pd

POWER_PLANTS = "https://raw.githubusercontent.com/ajduberstein/geo_datasets/master/global_power_plant_database.csv"

df = pd.read_csv(POWER_PLANTS)


def is_green(fuel_type):
"""Return a green RGB value if a facility uses a renewable fuel type"""
if fuel_type.lower() in ("nuclear", "water", "wind", "hydro", "biomass", "solar", "geothermal"):
return [10, 230, 120]
return [230, 158, 10]


df["color"] = df["primary_fuel"].apply(is_green)

# Use MapView with a globe projection
view_state = pdk.ViewState(latitude=51.47, longitude=0.45, zoom=0)

layers = [
pdk.Layer(
"ColumnLayer",
id="power-plant",
data=df,
get_elevation="capacity_mw",
get_position=["longitude", "latitude"],
elevation_scale=100,
pickable=True,
auto_highlight=True,
radius=20000,
get_fill_color="color",
),
]

deck = pdk.Deck(
initial_view_state=view_state,
tooltip={"text": "{name}, {primary_fuel} plant, {country}"},
layers=layers,
# Use MapLibre with globe projection
map_provider="maplibre",
map_style="https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
map_projection="globe",
)

deck.to_html("maplibre_globe.html", css_background_color="black", offline=True)
1 change: 1 addition & 0 deletions bindings/pydeck/pydeck/bindings/base_map_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class BaseMapProvider(Enum):
MAPBOX = "mapbox"
GOOGLE_MAPS = "google_maps"
CARTO = "carto"
MAPLIBRE = "maplibre"
8 changes: 7 additions & 1 deletion bindings/pydeck/pydeck/bindings/deck.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(
parameters=None,
widgets=None,
show_error=False,
map_projection=None,
):
"""This is the renderer and configuration for a deck.gl visualization, similar to the
`Deck <https://deck.gl/docs/api-reference/core/deck>`_ class from deck.gl.
Expand All @@ -63,7 +64,7 @@ def __init__(
``MAPBOX_API_KEY``, ``GOOGLE_MAPS_API_KEY``, and ``CARTO_API_KEY`` can be set instead of hardcoding the key here.
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`` or ``google_maps``
Values can be ``carto``, ``mapbox``, ``google_maps``, or ``maplibre``.
map_style : str or dict, default 'dark'
One of 'light', 'dark', 'road', 'satellite', 'dark_no_labels', and 'light_no_labels', a URI for a basemap
style, which varies by provider, or a dict that follows the Mapbox style `specification <https://docs.mapbox.com/mapbox-gl-js/style-spec/>`_.
Expand All @@ -87,6 +88,10 @@ def __init__(
show_error : bool, default False
If ``True``, will display the error in the rendered output.
Otherwise, will only show error in browser console.
map_projection : str, default None
Map projection to use with ``map_provider='maplibre'``.
Values can be ``'globe'`` or ``'mercator'``. Defaults to ``'mercator'`` if not specified.
Only supported with ``map_provider='maplibre'``.

.. _Deck:
https://deck.gl/docs/api-reference/core/deck
Expand All @@ -108,6 +113,7 @@ def __init__(
self.description = description
self.effects = effects
self.map_provider = str(map_provider).lower() if map_provider else None
self.map_projection = map_projection
self._tooltip = tooltip
self._show_error = show_error

Expand Down
11 changes: 6 additions & 5 deletions bindings/pydeck/pydeck/bindings/map_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,19 @@
GOOGLE_ROAD = "roadmap"

styles = {
DARK: {"mapbox": MAPBOX_DARK, "carto": CARTO_DARK},
DARK_NO_LABELS: {"carto": CARTO_DARK_NO_LABELS},
LIGHT: {"mapbox": MAPBOX_LIGHT, "carto": CARTO_LIGHT},
LIGHT_NO_LABELS: {"carto": CARTO_LIGHT_NO_LABELS},
ROAD: {"carto": CARTO_ROAD, "google_maps": GOOGLE_ROAD, "mapbox": MAPBOX_ROAD},
DARK: {"mapbox": MAPBOX_DARK, "carto": CARTO_DARK, "maplibre": CARTO_DARK},
DARK_NO_LABELS: {"carto": CARTO_DARK_NO_LABELS, "maplibre": CARTO_DARK_NO_LABELS},
LIGHT: {"mapbox": MAPBOX_LIGHT, "carto": CARTO_LIGHT, "maplibre": CARTO_LIGHT},
LIGHT_NO_LABELS: {"carto": CARTO_LIGHT_NO_LABELS, "maplibre": CARTO_LIGHT_NO_LABELS},
ROAD: {"carto": CARTO_ROAD, "google_maps": GOOGLE_ROAD, "mapbox": MAPBOX_ROAD, "maplibre": CARTO_ROAD},
SATELLITE: {"mapbox": MAPBOX_SATELLITE, "google_maps": GOOGLE_SATELLITE},
}

_default_map_identifers = {
BaseMapProvider.CARTO: DARK,
BaseMapProvider.MAPBOX: DARK,
BaseMapProvider.GOOGLE_MAPS: GOOGLE_ROAD,
BaseMapProvider.MAPLIBRE: DARK,
}


Expand Down
1 change: 1 addition & 0 deletions bindings/pydeck/pydeck/io/templates/index.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
{% endif %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" />
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/maplibre-gl.css" />
{{ deckgl_jupyter_widget_bundle }}
<link rel="stylesheet" href={{ deckgl_widget_css_url }} />
<style>
Expand Down
4 changes: 3 additions & 1 deletion modules/jupyter-widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@deck.gl/google-maps": "9.2.0-beta.4",
"@deck.gl/json": "9.2.0-beta.4",
"@deck.gl/layers": "9.2.0-beta.4",
"@deck.gl/mapbox": "9.2.0-beta.4",
"@deck.gl/mesh-layers": "9.2.0-beta.4",
"@deck.gl/widgets": "9.2.0-beta.4",
"@jupyter-widgets/base": "^1.1.10 || ^2 || ^3 || ^4",
Expand All @@ -45,7 +46,8 @@
"@luma.gl/core": "^9.2.2",
"@luma.gl/webgl": "^9.2.2",
"d3-dsv": "^1.0.8",
"mapbox-gl": "^1.13.2"
"mapbox-gl": "^1.13.2",
"maplibre-gl": "^5.14.0"
},
"jupyterlab": {
"extension": "src/plugin",
Expand Down
12 changes: 8 additions & 4 deletions modules/jupyter-widget/src/playground/create-deck.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import makeTooltip from './widget-tooltip';
import mapboxgl, {modifyMapboxElements} from './utils/mapbox-utils';
import {loadScript} from './utils/script-utils';
import {createGoogleMapsDeckOverlay} from './utils/google-maps-utils';
import {createMapLibreDeckOverlay} from './utils/maplibre-utils';

import {addSupportComponents} from '../lib/components/index';

Expand Down Expand Up @@ -127,7 +128,6 @@ function missingProps(oldProps, newProps) {
}

function createStandaloneFromProvider({
mapProvider,
props,
mapboxApiKey,
googleMapsKey,
Expand Down Expand Up @@ -161,7 +161,7 @@ function createStandaloneFromProvider({
container
};

switch (mapProvider) {
switch (props.mapProvider) {
case 'mapbox':
log.info('Using Mapbox base maps')();
return new DeckGL({
Expand All @@ -185,6 +185,12 @@ function createStandaloneFromProvider({
...props,
googleMapsKey
});
case 'maplibre':
log.info('Using MapLibre')();
return createMapLibreDeckOverlay({
...sharedProps,
...props
});
default:
log.info('No recognized map provider specified')();
return new DeckGL({
Expand Down Expand Up @@ -240,10 +246,8 @@ function createDeck({
const layersToLoad = missingProps(oldLayers, convertedLayers);
const widgetsToLoad = missingProps(oldWidgets, convertedWidgets);
const getTooltip = makeTooltip(tooltip);
const {mapProvider} = props;

deckgl = createStandaloneFromProvider({
mapProvider,
props,
mapboxApiKey,
googleMapsKey,
Expand Down
91 changes: 91 additions & 0 deletions modules/jupyter-widget/src/playground/utils/maplibre-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

/* eslint-disable import/namespace */
import {log} from '@deck.gl/core';
import {MapboxOverlay} from '@deck.gl/mapbox';
import maplibregl from 'maplibre-gl';

export function createMapLibreDeckOverlay({
container,
onClick,
onHover,
onResize,
onViewStateChange,
onDragStart,
onDrag,
onDragEnd,
onError,
getTooltip,
layers,
mapStyle = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
initialViewState = {latitude: 0, longitude: 0, zoom: 1},
mapProjection = 'mercator'
}) {
log.info('Using MapLibre')();

// Create MapLibre map
const map = new maplibregl.Map({
container,
style: mapStyle,
center: [initialViewState.longitude, initialViewState.latitude],
zoom: initialViewState.zoom,
pitch: initialViewState.pitch || 0,
bearing: initialViewState.bearing || 0
});

// Create deck overlay with interleaved mode for globe
const deckOverlay = new MapboxOverlay({
interleaved: mapProjection === 'globe',
layers,
getTooltip,
onClick,
onHover,
onDragStart,
onDrag,
onDragEnd,
onError
});

// Set up projection and add overlay when map loads
map.on('load', () => {
if (mapProjection === 'globe') {
map.setProjection({type: 'globe'});
}
map.addControl(deckOverlay);
map.addControl(new maplibregl.NavigationControl());
});

// Handle view state change events
if (onViewStateChange) {
map.on('move', () => {
const center = map.getCenter();
const viewState = {
longitude: center.lng,
latitude: center.lat,
zoom: map.getZoom(),
pitch: map.getPitch(),
bearing: map.getBearing()
};
onViewStateChange({viewState});
});
}

// Handle resize events
if (onResize) {
map.on('resize', () => {
const canvas = map.getCanvas();
onResize({width: canvas.width, height: canvas.height});
});
}

// Expose setProps method to update layers
deckOverlay.setProps = function (props) {
if (props.layers) {
deckOverlay.setProps({layers: props.layers});
}
};

return deckOverlay;
}
1 change: 1 addition & 0 deletions modules/jupyter-widget/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
{"path": "../google-maps"},
{"path": "../json"},
{"path": "../layers"},
{"path": "../mapbox"},
{"path": "../mesh-layers"}
]
}
Loading