examples/website/brushing/app.js (163 lines of code) (raw):

/* global fetch */ import React, {useMemo} from 'react'; import {render} from 'react-dom'; import {StaticMap} from 'react-map-gl'; import DeckGL from '@deck.gl/react'; import {ScatterplotLayer, ArcLayer} from '@deck.gl/layers'; import {BrushingExtension} from '@deck.gl/extensions'; import {scaleLinear} from 'd3-scale'; // Set your mapbox token here const MAPBOX_TOKEN = process.env.MapboxAccessToken; // eslint-disable-line // Source data GeoJSON const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/arc/counties.json'; // eslint-disable-line export const inFlowColors = [[35, 181, 184]]; export const outFlowColors = [[166, 3, 3]]; // migrate out const SOURCE_COLOR = [166, 3, 3]; // migrate in const TARGET_COLOR = [35, 181, 184]; const INITIAL_VIEW_STATE = { longitude: -100, latitude: 40.7, zoom: 3, maxZoom: 15, pitch: 0, bearing: 0 }; const brushingExtension = new BrushingExtension(); /* eslint-disable max-nested-callbacks */ function getLayerData(data) { if (!data || !data.length) { return {}; } const arcs = []; const targets = []; const sources = []; const pairs = {}; data.forEach((county, i) => { const {flows, centroid: targetCentroid} = county.properties; const value = {gain: 0, loss: 0}; Object.keys(flows).forEach(toId => { value[flows[toId] > 0 ? 'gain' : 'loss'] += flows[toId]; // if number too small, ignore it if (Math.abs(flows[toId]) < 50) { return; } const pairKey = [i, Number(toId)].sort((a, b) => a - b).join('-'); const sourceCentroid = data[toId].properties.centroid; const gain = Math.sign(flows[toId]); // add point at arc source sources.push({ position: sourceCentroid, target: targetCentroid, name: data[toId].properties.name, radius: 3, gain: -gain }); // eliminate duplicates arcs if (pairs[pairKey]) { return; } pairs[pairKey] = true; arcs.push({ target: gain > 0 ? targetCentroid : sourceCentroid, source: gain > 0 ? sourceCentroid : targetCentroid, value: flows[toId] }); }); // add point at arc target targets.push({ ...value, position: [targetCentroid[0], targetCentroid[1], 10], net: value.gain + value.loss, name: county.properties.name }); }); // sort targets by radius large -> small targets.sort((a, b) => Math.abs(b.net) - Math.abs(a.net)); const sizeScale = scaleLinear() .domain([0, Math.abs(targets[0].net)]) .range([36, 400]); targets.forEach(pt => { pt.radius = Math.sqrt(sizeScale(Math.abs(pt.net))); }); return {arcs, targets, sources}; } function getTooltip({object}) { return ( object && `\ ${object.name} Net gain: ${object.net}` ); } /* eslint-disable react/no-deprecated */ export default function App({ data, enableBrushing = true, brushRadius = 100000, strokeWidth = 2, opacity = 0.7, mapStyle = 'mapbox://styles/mapbox/light-v9' }) { const {arcs, targets, sources} = useMemo(() => getLayerData(data), [data]); const layers = arcs && targets && [ new ScatterplotLayer({ id: 'sources', data: sources, brushingRadius: brushRadius, brushingEnabled: enableBrushing, // only show source points when brushing radiusScale: enableBrushing ? 3000 : 0, getFillColor: d => (d.gain > 0 ? TARGET_COLOR : SOURCE_COLOR), extensions: [brushingExtension] }), new ScatterplotLayer({ id: 'targets-ring', data: targets, brushingRadius: brushRadius, lineWidthMinPixels: 2, stroked: true, filled: false, brushingEnabled: enableBrushing, // only show rings when brushing radiusScale: enableBrushing ? 4000 : 0, getLineColor: d => (d.net > 0 ? TARGET_COLOR : SOURCE_COLOR), extensions: [brushingExtension] }), new ScatterplotLayer({ id: 'targets', data: targets, brushingRadius: brushRadius, brushingEnabled: enableBrushing, pickable: true, radiusScale: 3000, getFillColor: d => (d.net > 0 ? TARGET_COLOR : SOURCE_COLOR), extensions: [brushingExtension] }), new ArcLayer({ id: 'arc', data: arcs, getWidth: strokeWidth, opacity, brushingRadius: brushRadius, brushingEnabled: enableBrushing, getSourcePosition: d => d.source, getTargetPosition: d => d.target, getSourceColor: SOURCE_COLOR, getTargetColor: TARGET_COLOR, extensions: [brushingExtension] }) ]; return ( <DeckGL layers={layers} initialViewState={INITIAL_VIEW_STATE} controller={true} getTooltip={getTooltip} > <StaticMap reuseMaps mapStyle={mapStyle} preventStyleDiffing={true} mapboxApiAccessToken={MAPBOX_TOKEN} /> </DeckGL> ); } export function renderToDOM(container) { render(<App />, container); fetch(DATA_URL) .then(response => response.json()) .then(({features}) => { render(<App data={features} />, container); }); }