diff --git a/examples/website/basic-basemap/README.md b/examples/website/basic-basemap/README.md index f4b47098..c67d5bc4 100644 --- a/examples/website/basic-basemap/README.md +++ b/examples/website/basic-basemap/README.md @@ -2,15 +2,7 @@ This is a basic basemap + deck.gl data animation utilizing [react-map-gl](https: ### Usage -Copy the content of this folder to your project. - -To see the base map, you need a [Mapbox access token](https://docs.mapbox.com/help/how-mapbox-works/access-tokens/). You can either set an environment variable: - -```bash -export MapboxAccessToken= -``` - -Or set `MAPBOX_TOKEN` directly in `app.js`. +Copy the content of this folder to your project. Other options can be found at [using with Mapbox GL](https://deck.gl/docs/developer-guide/base-maps/using-with-mapbox). @@ -41,9 +33,9 @@ import {useDeckAnimation, useHubbleGl} from '@hubble.gl/react'; const initialViewState = {...}; -function Map() { +function Visualization() { const deckRef = useRef(null); - const staticMapRef = useRef(null); + const mapRef = useRef(null); const deckAnimation = useDeckAnimation({ getLayers: a => a.applyLayerKeyframes([ @@ -55,13 +47,13 @@ function Map() { const { deckProps, - staticMapProps, // optional, use for basemap + mapProps, // optional, use for basemap adapter, // optional, use to modify animation at run time cameraFrame, // optional, use for camera animation setCameraFrame // optional, use for camera animation } = useHubbleGl({ deckRef, - staticMapRef, // optional, use for basemap + mapRef, // optional, use for basemap deckAnimation, initialViewState // optional, use for camera animation }); @@ -88,9 +80,41 @@ const timecode = { }; ``` -3. Add to props of the `DeckGl ` and `StaticMap` component +3. Define an interleaved deck.gl MapboxOverlay ```jsx +import {forwardRef} from 'react'; +import Map, {useControl} from 'react-map-gl'; +import {MapboxOverlay} from '@deck.gl/mapbox'; + +const DeckGLOverlay = forwardRef((props, ref) => { + // MapboxOverlay handles a variety of props differently than the Deck class. + // https://deck.gl/docs/api-reference/mapbox/mapbox-overlay#constructor + const deck = useControl(() => new MapboxOverlay({...props, interleaved: true})); + deck.setProps(props); + ref.current = deck._deck; + return null; +}); +``` + + +3. Add to props of the `DeckGl ` and `Map` component + +```jsx + + + + {/* optional base map */} - {staticMapProps.gl && ( - )} diff --git a/examples/website/basic-basemap/app.jsx b/examples/website/basic-basemap/app.jsx index 7d1173c2..0f452157 100644 --- a/examples/website/basic-basemap/app.jsx +++ b/examples/website/basic-basemap/app.jsx @@ -4,13 +4,15 @@ * Source code: https://github.com/visgl/deck.gl/tree/master/examples/website/trips */ -import React, {useState, useRef, useEffect} from 'react'; +import React, {useState, useRef, useEffect, forwardRef} from 'react'; import {createRoot} from 'react-dom/client'; -import DeckGL from '@deck.gl/react'; import {BasicControls, useHubbleGl, useDeckAnimation} from '@hubble.gl/react'; -import {StaticMap} from 'react-map-gl'; +import {MapboxOverlay} from '@deck.gl/mapbox'; +import Map, {useControl} from 'react-map-gl'; import {PolygonLayer} from '@deck.gl/layers'; import {easeInOut} from 'popmotion'; +import maplibregl from 'maplibre-gl'; +import {setRef} from './set-ref'; // Source data CSV const BUILDINGS = @@ -71,6 +73,18 @@ const timecode = { framerate: 30 }; +const DeckGLOverlay = forwardRef((props, ref) => { + // MapboxOverlay handles a variety of props differently than the Deck class. + // https://deck.gl/docs/api-reference/mapbox/mapbox-overlay#constructor + const deck = useControl(() => new MapboxOverlay({...props, interleaved: true})); + + deck.setProps(props); + + // @ts-expect-error private property + setRef(ref, deck._deck); + return null; +}); + const Container = ({children}) => (
[ Math.floor(Math.random() * 255) ]; -export default function App({mapStyle = 'mapbox://styles/mapbox/streets-v11'}) { +export default function App({mapStyle = 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json'}) { const deckRef = useRef(null); - const staticMapRef = useRef(null); + const mapRef = useRef(null); const [busy, setBusy] = useState(false); const deckAnimation = useDeckAnimation({ @@ -133,9 +147,9 @@ export default function App({mapStyle = 'mapbox://styles/mapbox/streets-v11'}) { ] }); - const {deckProps, staticMapProps, adapter, cameraFrame, setCameraFrame} = useHubbleGl({ + const {deckProps, mapProps, adapter, cameraFrame, setCameraFrame} = useHubbleGl({ deckRef, - staticMapRef, + mapRef, deckAnimation, initialViewState: START }); @@ -146,25 +160,21 @@ export default function App({mapStyle = 'mapbox://styles/mapbox/streets-v11'}) { return (
- - {staticMapProps.gl && ( - - )} - + +
(ref: ForwardedRef, value: T) { + if (!ref) return; + if (typeof ref === 'function') { + ref(value); + } else { + ref.current = value; + } +} diff --git a/examples/website/basic-basemap/vite.config.js b/examples/website/basic-basemap/vite.config.js deleted file mode 100644 index a32c2043..00000000 --- a/examples/website/basic-basemap/vite.config.js +++ /dev/null @@ -1,5 +0,0 @@ -export default { - define: { - 'process.env.MapboxAccessToken': JSON.stringify(process.env.MapboxAccessToken) - } -}; diff --git a/modules/react/src/hooks.ts b/modules/react/src/hooks.ts index 073a8662..2d1f939c 100644 --- a/modules/react/src/hooks.ts +++ b/modules/react/src/hooks.ts @@ -4,9 +4,7 @@ import {useState, useCallback, useMemo, RefObject} from 'react'; import {DeckAdapter, DeckAnimation, DeckAnimationConstructor} from '@hubble.gl/core'; -import {MapboxLayer} from '@deck.gl/mapbox/typed'; -import type {Layer, MapViewState} from '@deck.gl/core/typed'; -import type {DeckGLRef} from '@deck.gl/react/typed'; +import type {Layer, MapViewState, Deck} from '@deck.gl/core/typed'; import type {MapRef} from 'react-map-gl'; export function useNextFrame() { @@ -39,47 +37,35 @@ export function useDeckAnimation(params: DeckAnimationConstructor) { export function useHubbleGl({ deckRef, - staticMapRef = undefined, + mapRef = undefined, deckAnimation, initialViewState = undefined }: { - deckRef: RefObject; - staticMapRef?: RefObject; + deckRef: RefObject; + mapRef?: RefObject; deckAnimation: DeckAnimation; initialViewState?: MapViewState; }) { - const deck = useMemo(() => deckRef.current && deckRef.current.deck, [deckRef.current]); + const deck = useMemo(() => deckRef.current, [deckRef.current]); const nextFrame = useNextFrame(); const {adapter, layers, cameraFrame, setCameraFrame} = useDeckAdapter( deckAnimation, initialViewState ); - const onStaticMapLoad = useCallback(() => { - if (staticMapRef) { - const map = staticMapRef.current.getMap(); - // If there aren't any layers, combine map and deck with a fake layer. - if (!layers.length) { - // @ts-expect-error maplibre and mapbox have different types - map.addLayer(new MapboxLayer({id: '%%blank-layer', deck})); - } - for (let i = 0; i < layers.length; i++) { - // Adds DeckGL layers to Mapbox so Mapbox can be the bottom layer. Removing this clips DeckGL layers - // @ts-expect-error maplibre and mapbox have different types - map.addLayer(new MapboxLayer({id: layers[i].id, deck})); - } + const onMapLoad = useCallback(() => { + if (mapRef) { + const map = mapRef.current.getMap(); map.on('render', () => adapter.onAfterRender(nextFrame, map.areTilesLoaded())); } - }, [deck]); - - const [glContext, setGLContext] = useState(); + }, [adapter, nextFrame]); - if (!staticMapRef) { + if (!mapRef) { return { adapter, cameraFrame, setCameraFrame, - staticMapProps: {}, + mapProps: {}, deckProps: adapter.getProps({ deck, onNextFrame: nextFrame, @@ -94,16 +80,14 @@ export function useHubbleGl({ adapter, cameraFrame, setCameraFrame, - onStaticMapLoad, - staticMapProps: { - gl: glContext, - onLoad: onStaticMapLoad, + onMapLoad, + mapProps: { + onLoad: onMapLoad, preventStyleDiffing: true }, deckProps: adapter.getProps({ deck, extraProps: { - onWebGLInitialized: setGLContext, layers } })