examples/website/highway/app.js (160 lines of code) (raw):
import React, {useState, useMemo} from 'react';
import {render} from 'react-dom';
import {StaticMap} from 'react-map-gl';
import DeckGL from '@deck.gl/react';
import {GeoJsonLayer} from '@deck.gl/layers';
import {scaleLinear, scaleThreshold} from 'd3-scale';
// Set your mapbox token here
const MAPBOX_TOKEN = process.env.MapboxAccessToken; // eslint-disable-line
// Source data GeoJSON
const DATA_URL = {
ACCIDENTS:
'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/highway/accidents.csv',
ROADS: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/highway/roads.json'
};
function getKey({state, type, id}) {
return `${state}-${type}-${id}`;
}
export const COLOR_SCALE = scaleThreshold()
.domain([0, 4, 8, 12, 20, 32, 52, 84, 136, 220])
.range([
[26, 152, 80],
[102, 189, 99],
[166, 217, 106],
[217, 239, 139],
[255, 255, 191],
[254, 224, 139],
[253, 174, 97],
[244, 109, 67],
[215, 48, 39],
[168, 0, 0]
]);
const WIDTH_SCALE = scaleLinear()
.clamp(true)
.domain([0, 200])
.range([10, 2000]);
const INITIAL_VIEW_STATE = {
latitude: 38,
longitude: -100,
zoom: 4,
minZoom: 2,
maxZoom: 8
};
function aggregateAccidents(accidents) {
const incidents = {};
const fatalities = {};
if (accidents) {
accidents.forEach(a => {
const r = (incidents[a.year] = incidents[a.year] || {});
const f = (fatalities[a.year] = fatalities[a.year] || {});
const key = getKey(a);
r[key] = a.incidents;
f[key] = a.fatalities;
});
}
return {incidents, fatalities};
}
function renderTooltip({fatalities, incidents, year, hoverInfo}) {
const {object, x, y} = hoverInfo;
if (!object) {
return null;
}
const props = object.properties;
const key = getKey(props);
const f = fatalities[year][key];
const r = incidents[year][key];
const content = r ? (
<div>
<b>{f}</b> people died in <b>{r}</b> crashes on{' '}
{props.type === 'SR' ? props.state : props.type}-{props.id} in <b>{year}</b>
</div>
) : (
<div>
no accidents recorded in <b>{year}</b>
</div>
);
return (
<div className="tooltip" style={{left: x, top: y}}>
<big>
{props.name} ({props.state})
</big>
{content}
</div>
);
}
export default function App({
roads = DATA_URL.ROADS,
year,
accidents,
mapStyle = 'mapbox://styles/mapbox/dark-v9'
}) {
const [hoverInfo, setHoverInfo] = useState({});
const {incidents, fatalities} = useMemo(() => aggregateAccidents(accidents), [accidents]);
const getLineColor = f => {
if (!fatalities[year]) {
return [200, 200, 200];
}
const key = getKey(f.properties);
const fatalitiesPer1KMile = ((fatalities[year][key] || 0) / f.properties.length) * 1000;
return COLOR_SCALE(fatalitiesPer1KMile);
};
const getLineWidth = f => {
if (!incidents[year]) {
return 10;
}
const key = getKey(f.properties);
const incidentsPer1KMile = ((incidents[year][key] || 0) / f.properties.length) * 1000;
return WIDTH_SCALE(incidentsPer1KMile);
};
const layers = [
new GeoJsonLayer({
id: 'geojson',
data: roads,
stroked: false,
filled: false,
lineWidthMinPixels: 0.5,
parameters: {
depthTest: false
},
getLineColor,
getLineWidth,
pickable: true,
onHover: setHoverInfo,
updateTriggers: {
getLineColor: {year},
getLineWidth: {year}
},
transitions: {
getLineColor: 1000,
getLineWidth: 1000
}
})
];
return (
<DeckGL
layers={layers}
pickingRadius={5}
initialViewState={INITIAL_VIEW_STATE}
controller={true}
>
<StaticMap
reuseMaps
mapStyle={mapStyle}
preventStyleDiffing={true}
mapboxApiAccessToken={MAPBOX_TOKEN}
/>
{renderTooltip({incidents, fatalities, year, hoverInfo})}
</DeckGL>
);
}
export function renderToDOM(container) {
render(<App />, container);
const formatRow = d => ({
...d,
incidents: Number(d.incidents),
fatalities: Number(d.fatalities)
});
require('d3-request').csv(DATA_URL.ACCIDENTS, formatRow, (error, response) => {
if (!error) {
render(<App accidents={response} year={response[0].year} />, container);
}
});
}