Skip to content

Commit

Permalink
Update examples (#289)
Browse files Browse the repository at this point in the history
* Update the trips example to use MapboxOverlay

* Upgrade mapbox example

* deck.gl 8.9 doesn't support mapbox 3.0+ interleaved
  • Loading branch information
chrisgervang authored Jan 31, 2025
1 parent 294ef5f commit ad18a7b
Show file tree
Hide file tree
Showing 19 changed files with 852 additions and 81 deletions.
124 changes: 124 additions & 0 deletions examples/website/basic-basemap-mapbox-legacy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
This is a basic basemap + deck.gl data animation utilizing [react-map-gl](https://visgl.github.io/react-map-gl/) and `PolygonLayer`.

### 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=<mapbox_access_token>
```

Or set `MAPBOX_TOKEN` directly in `app.js`.

Other options can be found at [using with Mapbox GL](https://deck.gl/docs/developer-guide/base-maps/using-with-mapbox).

### Installation

```bash
# install dependencies within hubble.gl root
yarn bootstrap
# To install example go to the folder
cd examples/basic-basemap-mapbox-legacy
# do this once per example
yarn
# To run the example
yarn start-local
```

### Data format
Sample data is stored in [deck.gl Example Data](https://github.com/visgl/deck.gl-data/tree/master/examples). To use your own data, check out
the [documentation of PolygonLayer](https://deck.gl/docs/api-reference/layers/polygon-layer).

### How to add this feature to a hubble.gl example

1. Define hubble.gl react hooks

```jsx
import React, {useRef} from 'react';
import {useDeckAnimation, useHubbleGl} from '@hubble.gl/react';

const initialViewState = {...};

function Map() {
const deckRef = useRef(null);
const mapRef = useRef(null);
const deckAnimation = useDeckAnimation({
getLayers: a =>
a.applyLayerKeyframes([
// move deck layer definitions to here
]),
layerKeyframes: ...
cameraKeyframe: ...
});

const {
deckProps,
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,
mapRef, // optional, use for basemap
deckAnimation,
initialViewState // optional, use for camera animation
});

...
```

2. Define animation and export settings

```js
const formatConfigs = {
// optional, override default quality settings
};

const resolution = {
width: 1280,
height: 720
};

const timecode = {
start: 0,
end: 5000,
framerate: 30
};
```

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;
});
```

4. Add to props of the `DeckGLOverlay ` and `Map` component

```jsx
<Map
ref={mapRef}
{...cameraFrame}
style={{width: resolution.width, height: resolution.height}}
{/* add your props before spreading hubble props */}
{...mapProps}
>
<DeckGLOverlay
ref={deckRef}
{/* add your props before spreading hubble props */}
{...deckProps}
/>
</Map>
```
192 changes: 192 additions & 0 deletions examples/website/basic-basemap-mapbox-legacy/app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/**
* This is based on the deckgl building example that uses the TripsLayer and PolygonLayer
* You can find it here https://deck.gl/examples/trips-layer/
* Source code: https://github.com/visgl/deck.gl/tree/master/examples/website/trips
*/

import React, {useState, useRef, useEffect, forwardRef} from 'react';
import {createRoot} from 'react-dom/client';
import {BasicControls, useHubbleGl, useDeckAnimation} from '@hubble.gl/react';
import {MapboxOverlay} from '@deck.gl/mapbox';
import Map, {useControl} from 'react-map-gl';
import {PolygonLayer} from '@deck.gl/layers';
import {easeInOut} from 'popmotion';
import {setRef} from './set-ref';

// Source data CSV
const BUILDINGS =
'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/trips/buildings.json';

const material = {
ambient: 0.8,
diffuse: 0.6,
shininess: 32,
specularColor: [60, 64, 70]
};

const START = {
longitude: -74,
latitude: 40.72,
zoom: 13,
pitch: 45,
bearing: 0,
maxPitch: 90
};

const END = {
latitude: 40.711793177246946,
longitude: -74.01008557686262,
zoom: 16.88,
bearing: -26,
pitch: 65,
maxPitch: 90
};

const resolution = {
width: 1280,
height: 720
};

/** @type {import('@hubble.gl/core/src/types').FormatConfigs} */
const formatConfigs = {
webm: {
quality: 0.8
},
png: {
archive: 'zip'
},
jpeg: {
archive: 'zip',
quality: 0.8
},
gif: {
sampleInterval: 1000,
width: resolution.width,
height: resolution.height
}
};

const timecode = {
start: 0,
end: 5000,
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}) => (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: '100%',
position: 'relative',
backgroundColor: '#11183c',
overflow: 'hidden'
}}
>
{children}
</div>
);

const randomColor = () => [
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255)
];

export default function App({mapStyle = 'mapbox://styles/mapbox/streets-v11'}) {
const deckRef = useRef(null);
const mapRef = useRef(null);
const [busy, setBusy] = useState(false);

const deckAnimation = useDeckAnimation({
cameraKeyframe: {
timings: [0, timecode.end],
keyframes: [START, END],
easings: [easeInOut],
interpolators: 'flyTo',
width: resolution.width,
height: resolution.height
},
getLayers: a =>
a.applyLayerKeyframes([
new PolygonLayer({
id: 'buildings',
data: BUILDINGS,
extruded: true,
wireframe: false,
opacity: 0.5,
getPolygon: f => f.polygon,
getElevation: f => f.height,
getFillColor: randomColor,
material,
elevationScale: 1
})
]),
layerKeyframes: [
{
id: 'buildings',
timings: [0, timecode.end],
keyframes: [{elevationScale: 0.1}, {elevationScale: 1}],
easings: [easeInOut]
}
]
});

const {deckProps, mapProps, adapter, cameraFrame, setCameraFrame} = useHubbleGl({
deckRef,
mapRef,
deckAnimation,
initialViewState: START
});

const onViewStateChange = ({viewState}) => setCameraFrame(viewState);
useEffect(() => onViewStateChange({viewState: cameraFrame}), []);

return (
<Container>
<div style={{position: 'relative'}}>
<Map
ref={mapRef}
mapStyle={mapStyle}
{...mapProps}
{...cameraFrame}
style={{width: resolution.width, height: resolution.height}}
onMove={onViewStateChange}
// Note: 'reuseMap' prop with gatsby and mapbox extension causes stale reference error.
>
<DeckGLOverlay
ref={deckRef}
{...deckProps}
/>
</Map>
</div>
<BasicControls
adapter={adapter}
busy={busy}
setBusy={setBusy}
formatConfigs={formatConfigs}
timecode={timecode}
/>
</Container>
);
}

export async function renderToDOM(container) {
const root = createRoot(container);
root.render(<App />);
}
19 changes: 19 additions & 0 deletions examples/website/basic-basemap-mapbox-legacy/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>hubble.gl Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {margin: 0; font-family: sans-serif; width: 100vw; height: 100vh; overflow: hidden;}
#app {width: 100%; height: 100%;}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script type="module">
import {renderToDOM} from './app.jsx';
renderToDOM(document.getElementById('app'));
</script>
</html>
24 changes: 24 additions & 0 deletions examples/website/basic-basemap-mapbox-legacy/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "hubblegl-examples-basic-basemap-mapbox-legacy",
"version": "0.0.0",
"private": true,
"license": "MIT",
"scripts": {
"start": "vite --open",
"start-local": "vite --config ../../vite.config.local.mjs",
"build": "vite build"
},
"dependencies": {
"deck.gl": "^8.9",
"hubble.gl": "^1.4.0-alpha.0",
"d3-color": "^1.4.1",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-map-gl": "^7.1.8",
"mapbox-gl": "^1.13.0",
"popmotion": "9.3.1"
},
"devDependencies": {
"vite": "^4.0.0"
}
}
11 changes: 11 additions & 0 deletions examples/website/basic-basemap-mapbox-legacy/set-ref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type {ForwardedRef} from 'react';

// Helper for covering all of the typescript cases for setting a ref
export function setRef<T>(ref: ForwardedRef<T>, value: T) {
if (!ref) return;
if (typeof ref === 'function') {
ref(value);
} else {
ref.current = value;
}
}
5 changes: 5 additions & 0 deletions examples/website/basic-basemap-mapbox-legacy/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
define: {
'process.env.MapboxAccessToken': JSON.stringify(process.env.MapboxAccessToken)
}
};
Loading

0 comments on commit ad18a7b

Please sign in to comment.