From 2e0fd4afdb96e7a2e5790a0e7a50db828d010928 Mon Sep 17 00:00:00 2001 From: Chris Gervang <chrisgervang@users.noreply.github.com> Date: Wed, 6 Mar 2024 09:57:27 -0800 Subject: [PATCH] chore(examples) Use MapboxOverlay in mapbox (formerly safegraph) website example (#8558) * chore(examples) Use MapboxOverlay in mapbox website example * rename safegraph folder to mapbox * chore(examples) use react in mapbox website example --------- Signed-off-by: Chris Gervang <chris@gervang.com> --- .../website/{safegraph => mapbox}/README.md | 0 examples/website/mapbox/app.jsx | 150 ++++++++++++++++++ .../website/{safegraph => mapbox}/index.html | 5 +- .../{safegraph => mapbox}/package.json | 7 +- .../{safegraph => mapbox}/vite.config.js | 0 examples/website/safegraph/app.js | 135 ---------------- website/src/examples/mapbox.js | 4 +- 7 files changed, 159 insertions(+), 142 deletions(-) rename examples/website/{safegraph => mapbox}/README.md (100%) create mode 100644 examples/website/mapbox/app.jsx rename examples/website/{safegraph => mapbox}/index.html (73%) rename examples/website/{safegraph => mapbox}/package.json (74%) rename examples/website/{safegraph => mapbox}/vite.config.js (100%) delete mode 100644 examples/website/safegraph/app.js diff --git a/examples/website/safegraph/README.md b/examples/website/mapbox/README.md similarity index 100% rename from examples/website/safegraph/README.md rename to examples/website/mapbox/README.md diff --git a/examples/website/mapbox/app.jsx b/examples/website/mapbox/app.jsx new file mode 100644 index 00000000000..e4556f5c2e3 --- /dev/null +++ b/examples/website/mapbox/app.jsx @@ -0,0 +1,150 @@ +import React, {useCallback, useState, useRef, useMemo} from 'react'; +import {createRoot} from 'react-dom/client'; +import {MapboxOverlay} from '@deck.gl/mapbox'; +import {ArcLayer} from '@deck.gl/layers'; +import {H3HexagonLayer} from '@deck.gl/geo-layers'; +import {scaleLog} from 'd3-scale'; +import {cellToLatLng} from 'h3-js'; +import {load} from '@loaders.gl/core'; +import {CSVLoader} from '@loaders.gl/csv'; +import {Map, NavigationControl, useControl, Layer} from 'react-map-gl'; +import 'mapbox-gl/dist/mapbox-gl.css'; + +const DATA_URL = + 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/safegraph/sf-pois.csv'; + +// Set your mapbox token here +const MAPBOX_TOKEN = process.env.MapboxAccessToken; // eslint-disable-line + +const colorScale = scaleLog() + .domain([10, 100, 1000, 10000]) + .range([ + [255, 255, 178], + [254, 204, 92], + [253, 141, 60], + [227, 26, 28] + ]); + +const buildings3DLayer = { + id: '3d-buildings', + source: 'composite', + 'source-layer': 'building', + filter: ['==', 'extrude', 'true'], + type: 'fill-extrusion', + minzoom: 14, + paint: { + 'fill-extrusion-color': '#ccc', + 'fill-extrusion-height': ['get', 'height'] + } +}; + +function DeckGLOverlay(props) { + const overlay = useControl(() => new MapboxOverlay(props)); + overlay.setProps(props); + return null; +} + +export default function App({data}) { + const [selectedPOI, setSelectedPOI] = useState('8a283082aa17fff'); + const [firstLabelLayerId, setFirstLabelLayerId] = useState(); + const mapRef = useRef(); + + const onMapLoad = useCallback(() => { + setFirstLabelLayerId(getFirstLabelLayerId(mapRef.current.getStyle())); + }, []); + + const selectedPOICentroid = useMemo(() => { + const [lat, lng] = cellToLatLng(selectedPOI); + return [lng, lat]; + }, [selectedPOI]); + + const arcs = useMemo(() => filterArcs(data, selectedPOI), [data, selectedPOI]); + + const hexes = useMemo(() => aggregateHexes(data), [data]); + + const arcLayer = new ArcLayer({ + id: 'deckgl-connections', + data: arcs, + getSourcePosition: d => selectedPOICentroid, + getTargetPosition: d => [d.home_lng, d.home_lat], + getSourceColor: [255, 0, 128], + getTargetColor: [0, 200, 255], + getWidth: d => Math.max(2, d.count / 15) + }); + + const poiLayer = new H3HexagonLayer({ + id: 'deckgl-pois', + data: hexes, + opacity: 0.4, + pickable: true, + autoHighlight: true, + onClick: ({object}) => object && setSelectedPOI(object.hex), + getHexagon: d => d.hex, + getFillColor: d => colorScale(d.count), + extruded: false, + stroked: false, + beforeId: firstLabelLayerId + }); + + return ( + <Map + ref={mapRef} + mapboxAccessToken={MAPBOX_TOKEN} + mapStyle="mapbox://styles/mapbox/light-v9" + antialias={true} + initialViewState={{ + longitude: -122.4034, + latitude: 37.7845, + zoom: 15.5, + bearing: 20, + pitch: 60 + }} + onLoad={onMapLoad} + > + <DeckGLOverlay interleaved={true} layers={[poiLayer, arcLayer]} /> + <NavigationControl /> + <Layer {...buildings3DLayer} /> + </Map> + ); +} + +function filterArcs(data, selectedPOI) { + if (!data) { + return null; + } + return data.filter(d => d.hex === selectedPOI); +} + +function aggregateHexes(data) { + if (!data) { + return null; + } + const result = {}; + for (const object of data) { + if (!result[object.hex]) { + result[object.hex] = {hex: object.hex, count: 0}; + } + result[object.hex].count += object.count; + } + return Object.values(result); +} + +function getFirstLabelLayerId(style) { + const layers = style.layers; + // Find the index of the first symbol (i.e. label) layer in the map style + for (let i = 0; i < layers.length; i++) { + if (layers[i].type === 'symbol') { + return layers[i].id; + } + } + return undefined; +} + +export function renderToDOM(container) { + const root = createRoot(container); + root.render(<App />); + + load(DATA_URL, CSVLoader).then(data => { + root.render(<App data={data.data} />); + }); +} diff --git a/examples/website/safegraph/index.html b/examples/website/mapbox/index.html similarity index 73% rename from examples/website/safegraph/index.html rename to examples/website/mapbox/index.html index 535927f05fd..54527a0ace0 100644 --- a/examples/website/safegraph/index.html +++ b/examples/website/mapbox/index.html @@ -3,7 +3,6 @@ <head> <meta charset='UTF-8' /> <title>mapbox-custom-layer-example</title> - <link href="https://api.mapbox.com/mapbox-gl-js/v1.13.0/mapbox-gl.css" rel="stylesheet" /> <style> body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; @@ -23,7 +22,7 @@ <div id="app"></div> </body> <script type="module"> - import {loadAndRender} from './app.js'; - loadAndRender(document.getElementById('app')); + import {renderToDOM} from './app.jsx'; + renderToDOM(document.getElementById('app')); </script> </html> diff --git a/examples/website/safegraph/package.json b/examples/website/mapbox/package.json similarity index 74% rename from examples/website/safegraph/package.json rename to examples/website/mapbox/package.json index 7a542b50a74..55a9b6ece2c 100644 --- a/examples/website/safegraph/package.json +++ b/examples/website/mapbox/package.json @@ -11,8 +11,11 @@ "dependencies": { "@loaders.gl/csv": "^4.1.0", "d3-scale": "^2.0.0", - "deck.gl": "^9.0.0-alpha", - "mapbox-gl": "^2.0.0" + "deck.gl": "^9.0.0-beta", + "mapbox-gl": "^3.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-map-gl": "^7.0.0" }, "devDependencies": { "typescript": "^4.6.0", diff --git a/examples/website/safegraph/vite.config.js b/examples/website/mapbox/vite.config.js similarity index 100% rename from examples/website/safegraph/vite.config.js rename to examples/website/mapbox/vite.config.js diff --git a/examples/website/safegraph/app.js b/examples/website/safegraph/app.js deleted file mode 100644 index 4a3511123e3..00000000000 --- a/examples/website/safegraph/app.js +++ /dev/null @@ -1,135 +0,0 @@ -import mapboxgl from 'mapbox-gl'; -import {MapboxLayer} from '@deck.gl/mapbox'; -import {ArcLayer} from '@deck.gl/layers'; -import {H3HexagonLayer} from '@deck.gl/geo-layers'; -import {scaleLog} from 'd3-scale'; -import {cellToLatLng} from 'h3-js'; - -import {load} from '@loaders.gl/core'; -import {CSVLoader} from '@loaders.gl/csv'; - -// Set your mapbox token here -mapboxgl.accessToken = process.env.MapboxAccessToken; // eslint-disable-line - -const colorScale = scaleLog() - .domain([10, 100, 1000, 10000]) - .range([ - [255, 255, 178], - [254, 204, 92], - [253, 141, 60], - [227, 26, 28] - ]); - -export function renderToDOM(container, data) { - const map = new mapboxgl.Map({ - container, - useWebGL2: true, - style: 'mapbox://styles/mapbox/light-v9', - antialias: true, - center: [-122.4034, 37.7845], - zoom: 15.5, - bearing: 20, - pitch: 60 - }); - - map.addControl(new mapboxgl.NavigationControl(), 'top-left'); - - map.on('load', () => { - map.addLayer({ - id: '3d-buildings', - source: 'composite', - 'source-layer': 'building', - filter: ['==', 'extrude', 'true'], - type: 'fill-extrusion', - minzoom: 14, - paint: { - 'fill-extrusion-color': '#ccc', - 'fill-extrusion-height': ['get', 'height'] - } - }); - - renderLayers(map, data); - }); - - return { - update: newData => renderLayers(map, newData), - remove: () => { - map.remove(); - } - }; -} - -function renderLayers(map, data) { - if (!data) { - return; - } - let selectedPOICentroid; - - const arcLayer = new MapboxLayer({ - id: 'deckgl-connections', - type: ArcLayer, - data: [], - getSourcePosition: d => selectedPOICentroid, - getTargetPosition: d => [d.home_lng, d.home_lat], - getSourceColor: [255, 0, 128], - getTargetColor: [0, 200, 255], - getWidth: d => Math.max(2, d.count / 15) - }); - - const selectPOI = hex => { - const [lat, lng] = cellToLatLng(hex); - selectedPOICentroid = [lng, lat]; - arcLayer.setProps({ - data: data.filter(d => d.hex === hex) - }); - }; - - const poiLayer = new MapboxLayer({ - id: 'deckgl-pois', - type: H3HexagonLayer, - data: aggregateHexes(data), - opacity: 0.4, - pickable: true, - autoHighlight: true, - onClick: ({object}) => object && selectPOI(object.hex), - getHexagon: d => d.hex, - getFillColor: d => colorScale(d.count), - extruded: false, - stroked: false - }); - - map.addLayer(poiLayer, getFirstLabelLayerId(map.getStyle())); - map.addLayer(arcLayer); - - selectPOI('8a283082aa17fff'); -} - -function aggregateHexes(data) { - const result = {}; - for (const object of data) { - if (!result[object.hex]) { - result[object.hex] = {hex: object.hex, count: 0}; - } - result[object.hex].count += object.count; - } - return Object.values(result); -} - -function getFirstLabelLayerId(style) { - const layers = style.layers; - // Find the index of the first symbol (i.e. label) layer in the map style - for (let i = 0; i < layers.length; i++) { - if (layers[i].type === 'symbol') { - return layers[i].id; - } - } - return undefined; -} - -export async function loadAndRender(container) { - const data = await load( - 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/safegraph/sf-pois.csv', - CSVLoader - ); - renderToDOM(container, data.data); -} diff --git a/website/src/examples/mapbox.js b/website/src/examples/mapbox.js index a05d9e226f8..2cdf2e18139 100644 --- a/website/src/examples/mapbox.js +++ b/website/src/examples/mapbox.js @@ -1,13 +1,13 @@ import React, {Component, createRef} from 'react'; import {DATA_URI, GITHUB_TREE} from '../constants/defaults'; -import {renderToDOM} from 'website-examples/safegraph/app'; +import {renderToDOM} from 'website-examples/mapbox/app'; import {makeExample} from '../components'; class MapboxDemo extends Component { static title = 'Who Is Visiting San Francisco?'; - static code = `${GITHUB_TREE}/examples/website/safegraph`; + static code = `${GITHUB_TREE}/examples/website/mapbox`; static data = { url: `${DATA_URI}/safegraph.txt`,