-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Mask Extension example for website (#7681)
Co-authored-by: Xiaoji Chen <cxiaoji@gmail.com>
- Loading branch information
1 parent
d1188b0
commit bed6396
Showing
14 changed files
with
87,115 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
This is a standalone version of the MaskExtension example on [deck.gl](http://deck.gl) website. | ||
|
||
### Usage | ||
|
||
Copy the content of this folder to your project. | ||
|
||
```bash | ||
# install dependencies | ||
npm install | ||
# or | ||
yarn | ||
# bundle and serve the app with vite | ||
npm start | ||
``` | ||
|
||
### Data format | ||
|
||
Sample data is stored in [deck.gl Example Data](https://github.com/visgl/deck.gl-data/tree/master/examples/globe), showing air traffic data on selected dates collected by [The OpenSky Network](https://opensky-network.org). [Source](https://zenodo.org/record/3974209) under the Creative Commons CC-BY license. | ||
|
||
For more information about using the MaskExtension, check out the [documentation](../../../docs/api-reference/extensions/mask-extension.md). |
59 changes: 59 additions & 0 deletions
59
examples/website/mask-extension/animated-arc-group-layer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import {CompositeLayer} from '@deck.gl/core'; | ||
import AnimatedArcLayer from './animated-arc-layer'; | ||
|
||
const MAX_ARCS_PER_LAYER = 2500; | ||
|
||
/** Same effect as the AnimatedArcLayer, but perf optimized. | ||
* Data is divided into smaller groups, and one sub layer is rendered for each group. | ||
* This allows us to cheaply cull invisible arcs by turning layers off and on. | ||
*/ | ||
export default class AnimatedArcGroupLayer extends CompositeLayer { | ||
updateState({props, changeFlags}) { | ||
if (changeFlags.dataChanged) { | ||
// Sort and group data | ||
const {data, getSourceTimestamp, getTargetTimestamp} = props; | ||
const groups = sortAndGroup(data, getSourceTimestamp, getTargetTimestamp, MAX_ARCS_PER_LAYER); | ||
this.setState({groups}); | ||
} | ||
} | ||
|
||
renderLayers() { | ||
const {timeRange} = this.props; | ||
const {groups = []} = this.state; | ||
|
||
return groups.map( | ||
(group, index) => | ||
new AnimatedArcLayer(this.props, this.getSubLayerProps({ | ||
id: index.toString(), | ||
data: group.data, | ||
visible: group.startTime < timeRange[1] && group.endTime > timeRange[0], | ||
timeRange | ||
})) | ||
); | ||
} | ||
} | ||
|
||
AnimatedArcGroupLayer.layerName = 'AnimatedArcGroupLayer'; | ||
AnimatedArcGroupLayer.defaultProps = AnimatedArcLayer.defaultProps; | ||
|
||
function sortAndGroup(data, getStartTime, getEndTime, groupSize) { | ||
const groups = []; | ||
let group = null; | ||
|
||
data.sort((d1, d2) => getStartTime(d1) - getStartTime(d2)); | ||
|
||
for (const d of data) { | ||
if (!group || group.data.length >= groupSize) { | ||
group = { | ||
startTime: Infinity, | ||
endTime: -Infinity, | ||
data: [] | ||
}; | ||
groups.push(group); | ||
} | ||
group.data.push(d); | ||
group.startTime = Math.min(group.startTime, getStartTime(d)); | ||
group.endTime = Math.max(group.endTime, getEndTime(d)); | ||
} | ||
return groups; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import {ArcLayer} from '@deck.gl/layers'; | ||
|
||
export default class AnimatedArcLayer extends ArcLayer { | ||
getShaders() { | ||
const shaders = super.getShaders(); | ||
shaders.inject = { | ||
'vs:#decl': `\ | ||
uniform vec2 timeRange; | ||
attribute float instanceSourceTimestamp; | ||
attribute float instanceTargetTimestamp; | ||
varying float vTimestamp; | ||
`, | ||
'vs:#main-end': `\ | ||
vTimestamp = mix(instanceSourceTimestamp, instanceTargetTimestamp, segmentRatio); | ||
`, | ||
'fs:#decl': `\ | ||
uniform vec2 timeRange; | ||
varying float vTimestamp; | ||
`, | ||
'fs:#main-start': `\ | ||
if (vTimestamp < timeRange.x || vTimestamp > timeRange.y) { | ||
discard; | ||
} | ||
`, | ||
// Shape trail into teardrop | ||
'fs:DECKGL_FILTER_COLOR': `\ | ||
float f = (vTimestamp - timeRange.x) / (timeRange.y - timeRange.x); | ||
color.a *= pow(f, 5.0); | ||
float cap = 10.0 * (f - 0.9); | ||
float w = pow(f, 4.0) - smoothstep(0.89, 0.91, f) * pow(cap, 4.0); | ||
color.a *= smoothstep(1.1 * w, w, abs(geometry.uv.y)); | ||
` | ||
}; | ||
return shaders; | ||
} | ||
|
||
initializeState() { | ||
super.initializeState(); | ||
this.getAttributeManager().addInstanced({ | ||
instanceSourceTimestamp: { | ||
size: 1, | ||
accessor: 'getSourceTimestamp' | ||
}, | ||
instanceTargetTimestamp: { | ||
size: 1, | ||
accessor: 'getTargetTimestamp' | ||
} | ||
}); | ||
} | ||
|
||
draw(params) { | ||
params.uniforms = Object.assign({}, params.uniforms, { | ||
timeRange: this.props.timeRange | ||
}); | ||
super.draw(params); | ||
} | ||
} | ||
|
||
AnimatedArcLayer.layerName = 'AnimatedArcLayer'; | ||
AnimatedArcLayer.defaultProps = { | ||
getSourceTimestamp: {type: 'accessor', value: 0}, | ||
getTargetTimestamp: {type: 'accessor', value: 1}, | ||
timeRange: {type: 'array', compare: true, value: [0, 1]} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import React from 'react'; | ||
import {useState, useMemo} from 'react'; | ||
import {createRoot} from 'react-dom/client'; | ||
import {Map} from 'react-map-gl'; | ||
import maplibregl from 'maplibre-gl'; | ||
|
||
import DeckGL from '@deck.gl/react'; | ||
import {GeoJsonLayer} from '@deck.gl/layers'; | ||
import {MaskExtension} from '@deck.gl/extensions'; | ||
|
||
import {load} from '@loaders.gl/core'; | ||
import {CSVLoader} from '@loaders.gl/csv'; | ||
|
||
import AnimatedArcLayer from './animated-arc-group-layer'; | ||
import RangeInput from './range-input'; | ||
|
||
// Data source | ||
const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/globe/2020-01-14.csv'; | ||
const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; | ||
|
||
const INITIAL_VIEW_STATE = { | ||
longitude: -40, | ||
latitude: 40, | ||
zoom: 2, | ||
maxZoom: 6 | ||
}; | ||
|
||
/* eslint-disable react/no-deprecated */ | ||
export default function App({ | ||
data, | ||
mapStyle = MAP_STYLE, | ||
showFlights = true, | ||
timeWindow = 30, | ||
animationSpeed = 3 | ||
}) { | ||
const [currentTime, setCurrentTime] = useState(0); | ||
|
||
const citiesLayers = useMemo( | ||
() => [ | ||
new GeoJsonLayer({ | ||
id: 'cities', | ||
data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_populated_places_simple.geojson', | ||
|
||
pointType: 'circle', | ||
pointRadiusUnits: 'pixels', | ||
getFillColor: [255, 232, 180] | ||
}), | ||
|
||
new GeoJsonLayer({ | ||
id: 'cities-highlight', | ||
data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_populated_places_simple.geojson', | ||
|
||
pointType: 'circle', | ||
pointRadiusUnits: 'common', | ||
pointRadiusScale: 0.3, | ||
pointRadiusMinPixels: 2, | ||
pointRadiusMaxPixels: 30, | ||
getLineColor: [255, 232, 180, 90], | ||
getLineWidth: 3, | ||
lineWidthUnits: 'pixels', | ||
filled: false, | ||
stroked: true, | ||
|
||
extensions: [new MaskExtension()], | ||
maskId: 'flight-mask' | ||
}) | ||
], | ||
[] | ||
); | ||
|
||
const flightLayerProps = { | ||
data, | ||
greatCircle: true, | ||
getSourcePosition: d => [d.lon1, d.lat1], | ||
getTargetPosition: d => [d.lon2, d.lat2], | ||
getSourceTimestamp: d => d.time1, | ||
getTargetTimestamp: d => d.time2, | ||
getHeight: 0 | ||
}; | ||
|
||
const flightPathsLayer = showFlights && new AnimatedArcLayer({ | ||
...flightLayerProps, | ||
id: 'flight-paths', | ||
timeRange: [currentTime - 600, currentTime], // 10 minutes | ||
getWidth: 0.2, | ||
widthMinPixels: 1, | ||
widthMaxPixels: 4, | ||
widthUnits: 'common', | ||
getSourceColor: [180, 232, 255], | ||
getTargetColor: [180, 232, 255], | ||
parameters: {depthTest: false} | ||
}); | ||
|
||
const flightMaskLayer = new AnimatedArcLayer({ | ||
...flightLayerProps, | ||
id: 'flight-mask', | ||
timeRange: [currentTime - timeWindow * 60, currentTime], | ||
operation: 'mask', | ||
getWidth: 5000, | ||
widthUnits: 'meters', | ||
}); | ||
|
||
return ( | ||
<> | ||
<DeckGL | ||
initialViewState={INITIAL_VIEW_STATE} | ||
controller={true} | ||
layers={[flightPathsLayer, flightMaskLayer, citiesLayers]} | ||
> | ||
<Map reuseMaps mapLib={maplibregl} mapStyle={mapStyle} preventStyleDiffing={true} /> | ||
</DeckGL> | ||
{data && ( | ||
<RangeInput | ||
min={0} | ||
max={86400} | ||
value={currentTime} | ||
animationSpeed={animationSpeed} | ||
formatLabel={formatTimeLabel} | ||
onChange={setCurrentTime} | ||
/> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
function formatTimeLabel(seconds) { | ||
const h = Math.floor(seconds / 3600); | ||
const m = Math.floor(seconds / 60) % 60; | ||
const s = seconds % 60; | ||
return [h, m, s].map(x => x.toString().padStart(2, '0')).join(':'); | ||
} | ||
|
||
export function renderToDOM(container) { | ||
const root = createRoot(container); | ||
root.render(<App />); | ||
|
||
load(DATA_URL, CSVLoader) | ||
.then(flights => { | ||
root.render(<App data={flights} showFlights />); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<title>deck.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; background: #111;} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
</body> | ||
<script type="module"> | ||
import {renderToDOM} from './app.jsx'; | ||
renderToDOM(document.getElementById('app')); | ||
</script> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "deckgl-examples-mask-extension", | ||
"version": "0.0.0", | ||
"private": true, | ||
"license": "MIT", | ||
"scripts": { | ||
"start": "vite --open", | ||
"start-local": "vite --config ../../vite.config.local.mjs", | ||
"build": "vite build" | ||
}, | ||
"dependencies": { | ||
"@loaders.gl/csv": "^3.3.1", | ||
"@material-ui/core": "^4.10.2", | ||
"@material-ui/icons": "^4.9.1", | ||
"deck.gl": "^8.8.0", | ||
"maplibre-gl": "^2.4.0", | ||
"react": "^18.0.0", | ||
"react-dom": "^18.0.0", | ||
"react-map-gl": "^7.0.0" | ||
}, | ||
"devDependencies": { | ||
"typescript": "^4.6.0", | ||
"vite": "^4.0.0" | ||
} | ||
} |
Oops, something went wrong.