Skip to content

Conversation

kylebarron
Copy link
Member

@kylebarron kylebarron commented Sep 24, 2025

This revisits and supersedes #437.

image

Change list

  • Add @deck.gl/mapbox dependency
  • Move deck.gl map as a child of Maplibre map.
  • Add FullscreenControl and NavigationControl. These are placed on the top-left of the map for now, because the bounding box selection is already on the top right. But perhaps we should move the bbox selection to the left side? 🤷‍♂️

Questions

  • Does it make sense to use controls from Maplibre instead of the related "widgets" from Deck.gl v9?

See also more background information in #437:

There's a wider question here: should Maplibre be a child of Deck or should Deck be a child of Maplibre? The Deck docs list some limitations:

  • When using deck.gl's multi-view system, only one of the views can match the base map and receive interaction. See using MapboxOverlay with multi-views for details.
  • When using deck.gl as Mapbox layers or controls, Deck only receives a subset of user inputs delegated by Map. Therefore, certain interactive callbacks like onDrag, onInteractionStateChange are not available.
  • Mapbox/Maplibre's terrain features are partially supported. When a terrain is used, the camera of deck.gl and the base map should synchronize, however the deck.gl data with z=0 are rendered at the sea level and not aligned with the terrain surface.
  • Only Mercator projection is supported. Mapbox adaptive projection is not supported as their API doesn't expose the projection used.
  • The position property in viewState has no equivalent in mapbox-gl.

Maplibre as a child of Deck (not using MapboxOverlay)

  • We should be able to have more low level control over visualization and be less tied to maplibre.
  • For example, it would be really cool to visualize a side-by-side map, where you could swipe to see a before-and-after with two datasets. This option is required for this feature. Otherwise, when using MapboxOverlay you can't have multiple interactive deck views.
  • This also would keep us less tied to geospatial. So in theory non-geospatial users could use lonboard without a basemap, and still get to use all of lonboard's utilities for data serialization.

Deck as a child of Maplibre (using MapboxOverlay)

  • Can use all Maplibre and Maplibre-compatible controls. In theory this should even include things like mapbox-gl-draw. (Upstream example). In theory we wouldn't have to write our own, like we're doing in Select by bounding box #417
  • Note that fly_to is now broken. This is because deck is no longer maintaining the view state itself.

I think eventually it may make sense to allow rendering deck.gl without any sort of geospatial basemap, but that's a future topic.

Closes #436, closes #842, closes #437

@github-actions github-actions bot added the feat label Sep 24, 2025
Comment on lines +49 to +54
function DeckGLOverlay(props: MapboxOverlayProps) {
const overlay = useControl(() => new DeckOverlay(props));

overlay.setProps(props);
return null;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Is this the standard way to use DeckOverlay in react? @felixpalmer

Copy link
Collaborator

Choose a reason for hiding this comment

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

Comment on lines +294 to +310
onViewStateChange={(event) => {
const { viewState } = event;

// This condition is necessary to confirm that the viewState is
// of type MapViewState.
if ("latitude" in viewState) {
const { longitude, latitude, zoom, pitch, bearing } =
viewState;
setViewState({
longitude,
latitude,
zoom,
pitch,
bearing,
});
}
}}
Copy link
Member Author

Choose a reason for hiding this comment

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

todo: I need to check that deck.gl still sends these events even when the view state is managed by maplibre.

For background: This sets the current view state on the model so that it can be updated on the Python side, which can be accessed via Map.view_state.

@felixpalmer
Copy link
Collaborator

felixpalmer commented Sep 24, 2025

Does it make sense to use controls from Maplibre instead of the related "widgets" from Deck.gl v9?

Up to you, but worth noting many more widgets are coming in 9.2 visgl/deck.gl#9784

@kylebarron
Copy link
Member Author

Is it possible to use the deck.gl widgets even with MapboxOverlay?

Also, with MapboxOverlay it's not possible to use two views, right? Or at least they can't both have a maplibre map?

@kylebarron
Copy link
Member Author

From @felixpalmer, suggestion on how to switch ctrl and shift:

diff --git a/examples/get-started/react/maplibre/app.jsx b/examples/get-started/react/maplibre/app.jsx
index 238df7a16..c08899c13 100644
--- a/examples/get-started/react/maplibre/app.jsx
+++ b/examples/get-started/react/maplibre/app.jsx
@@ -22,6 +22,14 @@ const INITIAL_VIEW_STATE = {
 };
 
 const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';
+
+function patchDragMoveHandler(handler) {
+  const originalCorrectEvent = handler._moveStateManager._correctEvent;
+  handler._moveStateManager._correctEvent = e => {
+    return originalCorrectEvent({button: e.button, ctrlKey: e.shiftKey});
+  };
+}
+
 function DeckGLOverlay(props) {
   const overlay = useControl(() => new DeckOverlay(props));
   overlay.setProps(props);
@@ -31,6 +39,12 @@ function DeckGLOverlay(props) {
 function Root() {
   const [selected, setSelected] = useState(null);
 
+  const handleMapLoad = (event) => {
+    const map = event.target;
+    patchDragMoveHandler(map.dragRotate._mousePitch);
+    patchDragMoveHandler(map.dragRotate._mouseRotate);
+  };
+
   const layers = [
     new GeoJsonLayer({
       id: 'airports',
@@ -61,7 +75,7 @@ function Root() {
   ];
 
   return (
-    <Map initialViewState={INITIAL_VIEW_STATE} mapStyle={MAP_STYLE}>
+    <Map initialViewState={INITIAL_VIEW_STATE} mapStyle={MAP_STYLE} onLoad={handleMapLoad} boxZoom={false}>
       {selected && (
         <Popup
           key={selected.properties.name}

@felixpalmer
Copy link
Collaborator

@HarelM is there any better way to switch the SHIFT/CTRL keys?

@felixpalmer
Copy link
Collaborator

@kylebarron regarding multiview: https://deck.gl/docs/api-reference/mapbox/mapbox-overlay#multi-view-usage, correct only one can have the maplibre map.

It turns out you can add the deck widgets, just not the ones used to control the view state. Not sure how well supported this is though

   return (
    <Map initialViewState={INITIAL_VIEW_STATE} mapStyle={MAP_STYLE} onLoad={handleMapLoad} boxZoom={false}>
       {selected && (
         <Popup
           key={selected.properties.name}
@@ -73,7 +101,10 @@ function Root() {
           {selected.properties.name} ({selected.properties.abbrev})
         </Popup>
       )}
      <DeckGLOverlay layers={layers} /* interleaved*/ widgets={[
        new FullscreenWidget({placement: 'bottom-left'})
      ]
      }/>
       <NavigationControl position="top-left" />
     </Map>
   );

@HarelM
Copy link

HarelM commented Sep 25, 2025

@HarelM is there any better way to switch the SHIFT/CTRL keys?

Not at the moment, but I think there was a PR that requested it, but didn't make it to the finish line. I think that configurable preset might be needed as configuring for each handler can lead to "keys configuration collision".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fullscreen Import MapLibre CSS
3 participants