diff --git a/examples/website/3d-heatmap/app.jsx b/examples/website/3d-heatmap/app.tsx similarity index 72% rename from examples/website/3d-heatmap/app.jsx rename to examples/website/3d-heatmap/app.tsx index 2a61c329bf2..99c5af37a20 100644 --- a/examples/website/3d-heatmap/app.jsx +++ b/examples/website/3d-heatmap/app.tsx @@ -4,8 +4,10 @@ import {Map} from 'react-map-gl/maplibre'; import {AmbientLight, PointLight, LightingEffect} from '@deck.gl/core'; import {HexagonLayer} from '@deck.gl/aggregation-layers'; import DeckGL from '@deck.gl/react'; +import {CSVLoader} from '@loaders.gl/csv'; +import {load} from '@loaders.gl/core'; -import {csv} from 'd3-request'; +import type {Color, PickingInfo, MapViewState} from '@deck.gl/core'; // Source data CSV const DATA_URL = @@ -30,14 +32,7 @@ const pointLight2 = new PointLight({ const lightingEffect = new LightingEffect({ambientLight, pointLight1, pointLight2}); -const material = { - ambient: 0.64, - diffuse: 0.6, - shininess: 32, - specularColor: [51, 51, 51] -}; - -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -1.415727, latitude: 52.232395, zoom: 6.6, @@ -49,7 +44,7 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; -export const colorRange = [ +export const colorRange: Color[] = [ [1, 152, 189], [73, 227, 206], [216, 254, 181], @@ -58,7 +53,7 @@ export const colorRange = [ [209, 55, 78] ]; -function getTooltip({object}) { +function getTooltip({object}: PickingInfo) { if (!object) { return null; } @@ -72,16 +67,23 @@ function getTooltip({object}) { ${count} Accidents`; } -/* eslint-disable react/no-deprecated */ +type DataPoint = [longitude: number, latitude: number]; + export default function App({ - data, + data = null, mapStyle = MAP_STYLE, radius = 1000, upperPercentile = 100, coverage = 1 +}: { + data?: DataPoint[] | null; + mapStyle?: string; + radius?: number; + upperPercentile?: number; + coverage?: number; }) { const layers = [ - new HexagonLayer({ + new HexagonLayer({ id: 'heatmap', colorRange, coverage, @@ -93,7 +95,12 @@ export default function App({ pickable: true, radius, upperPercentile, - material, + material: { + ambient: 0.64, + diffuse: 0.6, + shininess: 32, + specularColor: [51, 51, 51] + }, transitions: { elevationScale: 3000 @@ -114,14 +121,11 @@ export default function App({ ); } -export function renderToDOM(container) { +export async function renderToDOM(container: HTMLDivElement) { const root = createRoot(container); root.render(); - csv(DATA_URL, (error, response) => { - if (!error) { - const data = response.map(d => [Number(d.lng), Number(d.lat)]); - root.render(); - } - }); + const data = (await load(DATA_URL, CSVLoader)).data; + const points: DataPoint[] = data.map(d => [d.lng, d.lat]); + root.render(); } diff --git a/examples/website/3d-heatmap/index.html b/examples/website/3d-heatmap/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/3d-heatmap/index.html +++ b/examples/website/3d-heatmap/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/3d-heatmap/package.json b/examples/website/3d-heatmap/package.json index 6545e88cf42..c1ea12004a3 100644 --- a/examples/website/3d-heatmap/package.json +++ b/examples/website/3d-heatmap/package.json @@ -9,7 +9,9 @@ "build": "vite build" }, "dependencies": { - "d3-request": "^1.0.5", + "@loaders.gl/csv": "^4.1.4", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/3d-heatmap/tsconfig.json b/examples/website/3d-heatmap/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/3d-heatmap/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/3d-tiles/app.jsx b/examples/website/3d-tiles/app.tsx similarity index 84% rename from examples/website/3d-tiles/app.jsx rename to examples/website/3d-tiles/app.tsx index 893efca1712..bcce04568cd 100644 --- a/examples/website/3d-tiles/app.jsx +++ b/examples/website/3d-tiles/app.tsx @@ -3,15 +3,17 @@ import {createRoot} from 'react-dom/client'; import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {Tile3DLayer} from '@deck.gl/geo-layers'; - import {CesiumIonLoader} from '@loaders.gl/3d-tiles'; +import type {MapViewState} from '@deck.gl/core'; +import type {Tileset3D} from '@loaders.gl/tiles'; + const ION_ASSET_ID = 43978; const ION_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3NjEwMjA4Ni00YmVkLTQyMjgtYjRmZS1lY2M3ZWFiMmFmNTYiLCJpZCI6MjYxMzMsImlhdCI6MTY3NTM2ODY4NX0.chGkGL6DkDNv5wYJQDMzWIvi9iDoVa27dgng_5ARDmo'; const TILESET_URL = `https://assets.ion.cesium.com/${ION_ASSET_ID}/tileset.json`; -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { latitude: 40, longitude: -75, pitch: 45, @@ -25,10 +27,13 @@ const INITIAL_VIEW_STATE = { export default function App({ mapStyle = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json', updateAttributions +}: { + mapStyle?: string; + updateAttributions?: (attributions: any) => void; }) { const [initialViewState, setInitialViewState] = useState(INITIAL_VIEW_STATE); - const onTilesetLoad = tileset => { + const onTilesetLoad = (tileset: Tileset3D) => { // Recenter view to cover the new tileset const {cartographicCenter, zoom} = tileset; setInitialViewState({ @@ -59,6 +64,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/3d-tiles/index.html b/examples/website/3d-tiles/index.html index 4860b609546..6c2aa762dec 100644 --- a/examples/website/3d-tiles/index.html +++ b/examples/website/3d-tiles/index.html @@ -19,7 +19,7 @@
diff --git a/examples/website/3d-tiles/package.json b/examples/website/3d-tiles/package.json index 6f98ed34c46..e7ab02b7381 100644 --- a/examples/website/3d-tiles/package.json +++ b/examples/website/3d-tiles/package.json @@ -9,8 +9,10 @@ "build": "vite build" }, "dependencies": { - "deck.gl": "^9.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "@loaders.gl/3d-tiles": "^4.1.4", + "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", diff --git a/examples/website/3d-tiles/tsconfig.json b/examples/website/3d-tiles/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/3d-tiles/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/arc/app.jsx b/examples/website/arc/app.tsx similarity index 58% rename from examples/website/arc/app.jsx rename to examples/website/arc/app.tsx index 6f54d23f194..cc446222a5b 100644 --- a/examples/website/arc/app.jsx +++ b/examples/website/arc/app.tsx @@ -6,11 +6,14 @@ import DeckGL from '@deck.gl/react'; import {GeoJsonLayer, ArcLayer} from '@deck.gl/layers'; import {scaleQuantile} from 'd3-scale'; +import type {Color, PickingInfo, MapViewState} from '@deck.gl/core'; +import type {Feature, Polygon, MultiPolygon} from 'geojson'; + // 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 = [ +export const inFlowColors: Color[] = [ [255, 255, 204], [199, 233, 180], [127, 205, 187], @@ -20,7 +23,7 @@ export const inFlowColors = [ [12, 44, 132] ]; -export const outFlowColors = [ +export const outFlowColors: Color[] = [ [255, 255, 178], [254, 217, 118], [254, 178, 76], @@ -30,7 +33,7 @@ export const outFlowColors = [ [177, 0, 38] ]; -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -100, latitude: 40.7, zoom: 3, @@ -41,21 +44,41 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json'; -function calculateArcs(data, selectedCounty) { +type CountyProperties = { + /** county name */ + name: string; + /** county index -> net flow */ + flows: Record; + /** geographical centroid */ + centroid: [lon: number, lat: number]; +}; + +type County = Feature; + +type MigrationFlow = { + source: County; + target: County; + /** Number of migrants */ + value: number; + quantile: number; +} + +function calculateArcs(data: County[] | undefined, selectedCounty?: County) { if (!data || !data.length) { return null; } if (!selectedCounty) { - selectedCounty = data.find(f => f.properties.name === 'Los Angeles, CA'); + selectedCounty = data.find(f => f.properties.name === 'Los Angeles, CA')!; } - const {flows, centroid} = selectedCounty.properties; + const {flows} = selectedCounty.properties; - const arcs = Object.keys(flows).map(toId => { - const f = data[toId]; + const arcs: MigrationFlow[] = Object.keys(flows).map(toId => { + const f = data[Number(toId)]; return { - source: centroid, - target: f.properties.centroid, - value: flows[toId] + source: selectedCounty, + target: f, + value: flows[toId], + quantile: 0 }; }); @@ -64,25 +87,28 @@ function calculateArcs(data, selectedCounty) { .range(inFlowColors.map((c, i) => i)); arcs.forEach(a => { - a.gain = Math.sign(a.value); a.quantile = scale(Math.abs(a.value)); }); return arcs; } -function getTooltip({object}) { +function getTooltip({object}: PickingInfo) { return object && object.properties.name; } /* eslint-disable react/no-deprecated */ -export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { - const [selectedCounty, selectCounty] = useState(null); +export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}: { + data?: County[]; + strokeWidth?: number; + mapStyle?: string; +}) { + const [selectedCounty, selectCounty] = useState(); const arcs = useMemo(() => calculateArcs(data, selectedCounty), [data, selectedCounty]); const layers = [ - new GeoJsonLayer({ + new GeoJsonLayer({ id: 'geojson', data, stroked: false, @@ -91,13 +117,13 @@ export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { onClick: ({object}) => selectCounty(object), pickable: true }), - new ArcLayer({ + new ArcLayer({ id: 'arc', data: arcs, - getSourcePosition: d => d.source, - getTargetPosition: d => d.target, - getSourceColor: d => (d.gain > 0 ? inFlowColors : outFlowColors)[d.quantile], - getTargetColor: d => (d.gain > 0 ? outFlowColors : inFlowColors)[d.quantile], + getSourcePosition: d => d.source.properties.centroid, + getTargetPosition: d => d.target.properties.centroid, + getSourceColor: d => (d.value > 0 ? inFlowColors : outFlowColors)[d.quantile], + getTargetColor: d => (d.value > 0 ? outFlowColors : inFlowColors)[d.quantile], getWidth: strokeWidth }) ]; @@ -114,7 +140,7 @@ export default function App({data, strokeWidth = 1, mapStyle = MAP_STYLE}) { ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { const root = createRoot(container); root.render(); diff --git a/examples/website/arc/index.html b/examples/website/arc/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/arc/index.html +++ b/examples/website/arc/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/arc/package.json b/examples/website/arc/package.json index c0dce76f899..5e692496a88 100644 --- a/examples/website/arc/package.json +++ b/examples/website/arc/package.json @@ -9,7 +9,10 @@ "build": "vite build" }, "dependencies": { - "d3-scale": "^2.0.0", + "@types/d3-scale": "^4.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/arc/tsconfig.json b/examples/website/arc/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/arc/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/brushing/app.jsx b/examples/website/brushing/app.jsx deleted file mode 100644 index 0678d056125..00000000000 --- a/examples/website/brushing/app.jsx +++ /dev/null @@ -1,196 +0,0 @@ -/* global fetch */ -import React, {useMemo} from 'react'; -import {createRoot} from 'react-dom/client'; -import {Map} from 'react-map-gl/maplibre'; -import DeckGL from '@deck.gl/react'; -import {ScatterplotLayer, ArcLayer} from '@deck.gl/layers'; -import {BrushingExtension} from '@deck.gl/extensions'; -import {scaleLinear} from 'd3-scale'; - -// 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 MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json'; - -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 = 1, - opacity = 0.7, - mapStyle = MAP_STYLE -}) { - 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 ( - - - - ); -} - -export function renderToDOM(container) { - const root = createRoot(container); - root.render(); - - fetch(DATA_URL) - .then(response => response.json()) - .then(({features}) => { - root.render(); - }); -} diff --git a/examples/website/brushing/app.tsx b/examples/website/brushing/app.tsx new file mode 100644 index 00000000000..35ffc4683a5 --- /dev/null +++ b/examples/website/brushing/app.tsx @@ -0,0 +1,209 @@ +/* global fetch */ +import React, {useMemo} from 'react'; +import {createRoot} from 'react-dom/client'; +import {Map} from 'react-map-gl/maplibre'; +import DeckGL from '@deck.gl/react'; +import {ScatterplotLayer, ArcLayer} from '@deck.gl/layers'; +import {BrushingExtension} from '@deck.gl/extensions'; +import {scaleSqrt} from 'd3-scale'; + +import type {Color, PickingInfo, MapViewState} from '@deck.gl/core'; +import type {BrushingExtensionProps} from '@deck.gl/extensions'; +import type {Feature, Polygon, MultiPolygon} from 'geojson'; + +// Source data GeoJSON +const DATA_URL = + 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/arc/counties.json'; // eslint-disable-line + +export const inFlowColor: Color = [35, 181, 184]; +export const outFlowColor: Color = [166, 3, 3]; + +const INITIAL_VIEW_STATE: MapViewState = { + longitude: -100, + latitude: 40.7, + zoom: 3, + maxZoom: 15, + pitch: 0, + bearing: 0 +}; + +const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json'; + +type CountyProperties = { + /** county name */ + name: string; + /** to another county index -> net flow */ + flows: {[id: number]: number}; + /** geographical centroid */ + centroid: [lon: number, lat: number]; +}; + +type County = Feature; + +type MigrationFlow = { + source: County; + target: County; + /** Number of migrants */ + value: number; +} + +type MigrationDestination = { + county: County; + /** Population net gain */ + netGain: number; +}; + +const brushingExtension = new BrushingExtension(); + +/* eslint-disable max-nested-callbacks */ +function getLayerData(data?: County[]): { + arcs: MigrationFlow[]; + points: MigrationDestination[]; +} | null { + if (!data || !data.length) { + return null; + } + const arcs: MigrationFlow[] = []; + const points: MigrationDestination[] = data.map((county, fromId) => { + const {flows} = county.properties; + let netGain = 0; + + Object.keys(flows).forEach(key => { + const toId = Number(key); + const value = flows[toId]; + netGain -= value; + + // if number too small, ignore it + if (value >= 50) { + arcs.push({ + source: county, + target: data[toId], + value + }); + } + }); + + return {county, netGain}; + }) + // sort points by radius large -> small + .sort((a, b) => Math.abs(b.netGain) - Math.abs(a.netGain)); + + return {arcs, points}; +} + +function getTooltip({object}: PickingInfo) { + return ( + object && + `\ + ${object.county.properties.name} + Net gain: ${object.netGain}` + ); +} + +/* eslint-disable react/no-deprecated */ +export default function App({ + data, + enableBrushing = true, + brushRadius = 100000, + strokeWidth = 1, + opacity = 0.7, + mapStyle = MAP_STYLE +}: { + data?: County[]; + enableBrushing?: boolean; + brushRadius?: number; + strokeWidth?: number; + opacity?: number; + mapStyle?: string; +}) { + const layerData = useMemo(() => getLayerData(data), [data]); + const radiusScale = useMemo(() => { + return layerData && scaleSqrt() + .domain([0, Math.abs(layerData.points[0].netGain)]) + .range([1, 20]); + }, [layerData]); + + const layers = layerData && [ + new ScatterplotLayer>({ + id: 'sources-with-gain', + data: layerData.arcs, + brushingRadius: brushRadius, + brushingEnabled: enableBrushing, + brushingTarget: 'custom', + getPosition: d => d.target.properties.centroid, + getRadius: d => radiusScale(d.value), + getBrushingTarget: d => d.source.properties.centroid, + getFillColor: inFlowColor, + // only show source points when brushing + visible: enableBrushing, + radiusScale: 3000, + extensions: [brushingExtension] + }), + new ScatterplotLayer>({ + id: 'sources-with-loss', + data: layerData.arcs, + brushingRadius: brushRadius, + brushingEnabled: enableBrushing, + brushingTarget: 'custom', + getPosition: d => d.source.properties.centroid, + getRadius: d => radiusScale(d.value), + getBrushingTarget: d => d.target.properties.centroid, + getFillColor: outFlowColor, + // only show source points when brushing + visible: enableBrushing, + radiusScale: 3000, + extensions: [brushingExtension] + }), + new ScatterplotLayer({ + id: 'destinations', + data: layerData.points, + brushingRadius: brushRadius, + lineWidthMinPixels: 2, + stroked: true, + pickable: true, + brushingEnabled: enableBrushing, + radiusScale: 3000, + getPosition: d => d.county.properties.centroid, + getLineColor: d => d.netGain > 0 ? inFlowColor : outFlowColor, + getFillColor: [0, 0, 0, 0], + getRadius: d => radiusScale(d.netGain) + 4, + extensions: [brushingExtension] + }), + new ArcLayer({ + id: 'arc', + data: layerData.arcs, + getWidth: strokeWidth, + opacity, + brushingRadius: brushRadius, + brushingEnabled: enableBrushing, + brushingTarget: 'source_target', + getSourcePosition: d => d.source.properties.centroid, + getTargetPosition: d => d.target.properties.centroid, + getSourceColor: outFlowColor, + getTargetColor: inFlowColor, + extensions: [brushingExtension] + }) + ]; + + return ( + + + + ); +} + +export function renderToDOM(container: HTMLDivElement) { + const root = createRoot(container); + root.render(); + + fetch(DATA_URL) + .then(response => response.json()) + .then(({features}) => { + root.render(); + }); +} diff --git a/examples/website/brushing/index.html b/examples/website/brushing/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/brushing/index.html +++ b/examples/website/brushing/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/brushing/package.json b/examples/website/brushing/package.json index 560cea5a81a..2a0b7799451 100644 --- a/examples/website/brushing/package.json +++ b/examples/website/brushing/package.json @@ -9,7 +9,10 @@ "build": "vite build" }, "dependencies": { - "d3-scale": "^2.0.0", + "@types/d3-scale": "^4.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/brushing/tsconfig.json b/examples/website/brushing/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/brushing/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/contour/app.jsx b/examples/website/contour/app.tsx similarity index 76% rename from examples/website/contour/app.jsx rename to examples/website/contour/app.tsx index 2eff482a742..cceba4dd665 100644 --- a/examples/website/contour/app.jsx +++ b/examples/website/contour/app.tsx @@ -4,17 +4,20 @@ import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {ContourLayer} from '@deck.gl/aggregation-layers'; +import type {ContourLayerProps} from '@deck.gl/aggregation-layers'; +import type {PickingInfo, MapViewState} from '@deck.gl/core'; + const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/contour/covid-by-county.json'; // eslint-disable-line -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -100, latitude: 40, zoom: 3, maxZoom: 10 }; -export const BANDS = [ +export const BANDS: ContourLayerProps["contours"] = [ {threshold: [0.1, 1], color: [255, 255, 178]}, {threshold: [1, 10], color: [254, 204, 92]}, {threshold: [10, 100], color: [253, 141, 60]}, @@ -23,7 +26,7 @@ export const BANDS = [ {threshold: [2000, 10000], color: [159, 0, 80]} ]; -export const LINES = [ +export const LINES: ContourLayerProps["contours"] = [ {threshold: 1, color: [255, 255, 178], strokeWidth: 2}, {threshold: 10, color: [254, 204, 92], strokeWidth: 2}, {threshold: 100, color: [253, 141, 60], strokeWidth: 2}, @@ -34,15 +37,30 @@ export const LINES = [ const MS_PER_WEEK = 1000 * 60 * 60 * 24 * 7; +type CaseReport = { + state: string; + county: string; + longitude: number; + latitude: number; + population: number; + casesByWeek: {[week: number]: number}; +}; + export default function App({ data = DATA_URL, week = 35, contours = BANDS, cellSize = 60000, mapStyle = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json' +}: { + data?: string | CaseReport[]; + week?: number; + contours?: ContourLayerProps["contours"]; + cellSize?: number; + mapStyle?: string; }) { const layers = [ - new ContourLayer({ + new ContourLayer({ data, id: 'contour-layer', getPosition: d => [d.longitude, d.latitude], @@ -57,7 +75,7 @@ export default function App({ }) ]; - const getTooltip = info => { + const getTooltip = (info: PickingInfo) => { if (!info.object) { return null; } @@ -90,6 +108,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/contour/index.html b/examples/website/contour/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/contour/index.html +++ b/examples/website/contour/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/contour/package.json b/examples/website/contour/package.json index fc033aa8acb..fe706306a6e 100644 --- a/examples/website/contour/package.json +++ b/examples/website/contour/package.json @@ -8,6 +8,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/contour/tsconfig.json b/examples/website/contour/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/contour/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/data-filter/app.jsx b/examples/website/data-filter/app.tsx similarity index 70% rename from examples/website/data-filter/app.jsx rename to examples/website/data-filter/app.tsx index e4b314d3f23..835128135b5 100644 --- a/examples/website/data-filter/app.jsx +++ b/examples/website/data-filter/app.tsx @@ -1,14 +1,16 @@ -import React, {useState} from 'react'; +import React, {useState, useMemo} from 'react'; import {createRoot} from 'react-dom/client'; import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {ScatterplotLayer} from '@deck.gl/layers'; import {DataFilterExtension} from '@deck.gl/extensions'; import {MapView} from '@deck.gl/core'; +import {CSVLoader} from '@loaders.gl/csv'; +import {load} from '@loaders.gl/core'; import RangeInput from './range-input'; -import {useMemo} from 'react'; -import {csv} from 'd3-request'; +import type {PickingInfo, MapViewState} from '@deck.gl/core'; +import type {DataFilterExtensionProps} from '@deck.gl/extensions'; // Source data GeoJSON const DATA_URL = @@ -19,11 +21,12 @@ const DATA_URL = // circles at the depth of the earthquakes, i.e. below sea level, we need to // push the far plane away to avoid clipping them. const MAP_VIEW = new MapView({ + repeat: true, // 1 is the distance between the camera and the ground farZMultiplier: 100 }); -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { latitude: 36.5, longitude: -120, zoom: 5.5, @@ -35,6 +38,14 @@ const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/s const MS_PER_DAY = 8.64e7; +type Earthquake = { + timestamp: number; + latitude: number; + longitude: number; + depth: number; + magnitude: number; +}; + const dataFilter = new DataFilterExtension({ filterSize: 1, // Enable for higher precision, e.g. 1 second granularity @@ -42,12 +53,12 @@ const dataFilter = new DataFilterExtension({ fp64: false }); -function formatLabel(t) { - const date = new Date(t); +function formatLabel(timestamp: number) { + const date = new Date(timestamp); return `${date.getUTCFullYear()}/${date.getUTCMonth() + 1}`; } -function getTimeRange(data) { +function getTimeRange(data?: Earthquake[]): [minTime: number, maxTime: number] | null { if (!data) { return null; } @@ -62,7 +73,7 @@ function getTimeRange(data) { ); } -function getTooltip({object}) { +function getTooltip({object}: PickingInfo) { return ( object && `\ @@ -73,15 +84,18 @@ function getTooltip({object}) { ); } -export default function App({data, mapStyle = MAP_STYLE}) { - const [filter, setFilter] = useState(null); +export default function App({data, mapStyle = MAP_STYLE}: { + data?: Earthquake[]; + mapStyle?: string; +}) { + const [filter, setFilter] = useState<[start: number, end: number] | null>(null); const timeRange = useMemo(() => getTimeRange(data), [data]); const filterValue = filter || timeRange; const layers = [ - data && - new ScatterplotLayer({ + filterValue && + new ScatterplotLayer>({ id: 'earthquakes', data, opacity: 0.8, @@ -134,19 +148,18 @@ export default function App({data, mapStyle = MAP_STYLE}) { ); } -export function renderToDOM(container) { +export async function renderToDOM(container: HTMLDivElement) { const root = createRoot(container); root.render(); - csv(DATA_URL, (error, response) => { - if (!error) { - const data = response.map(row => ({ - timestamp: new Date(`${row.DateTime} UTC`).getTime(), - latitude: Number(row.Latitude), - longitude: Number(row.Longitude), - depth: Number(row.Depth), - magnitude: Number(row.Magnitude) - })); - root.render(); - } - }); + + const data = (await load(DATA_URL, CSVLoader)).data; + + const earthquakes = data.map(row => ({ + timestamp: new Date(`${row.DateTime} UTC`).getTime(), + latitude: row.Latitude, + longitude: row.Longitude, + depth: row.Depth, + magnitude: row.Magnitude + })); + root.render(); } diff --git a/examples/website/data-filter/index.html b/examples/website/data-filter/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/data-filter/index.html +++ b/examples/website/data-filter/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/data-filter/package.json b/examples/website/data-filter/package.json index 42e36b96695..87d4e77a5ad 100644 --- a/examples/website/data-filter/package.json +++ b/examples/website/data-filter/package.json @@ -11,7 +11,9 @@ "dependencies": { "@material-ui/core": "^4.10.2", "@material-ui/icons": "^4.9.1", - "d3-request": "^1.0.5", + "@loaders.gl/csv": "^4.1.4", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/data-filter/range-input.jsx b/examples/website/data-filter/range-input.tsx similarity index 77% rename from examples/website/data-filter/range-input.jsx rename to examples/website/data-filter/range-input.tsx index a568c5966e6..ca808d5e285 100644 --- a/examples/website/data-filter/range-input.jsx +++ b/examples/website/data-filter/range-input.tsx @@ -29,9 +29,18 @@ const SliderInput = withStyles({ } })(Slider); -export default function RangeInput({min, max, value, animationSpeed, onChange, formatLabel}) { +export default function RangeInput({min, max, value, animationSpeed, onChange, formatLabel}: { + min: number; + max: number; + value: [start: number, end: number]; + animationSpeed: number; + onChange: (value: [start: number, end: number]) => void; + formatLabel: (value: number) => string; +}) { const [isPlaying, setIsPlaying] = useState(false); - const [animation] = useState({}); + const [animation] = useState<{ + id?: number; + }>({}); // prettier-ignore useEffect(() => { @@ -54,14 +63,14 @@ export default function RangeInput({min, max, value, animationSpeed, onChange, f return ( - onChange(newValue)} + onChange={(_, newValue: [number, number]) => onChange(newValue)} valueLabelDisplay="auto" valueLabelFormat={formatLabel} /> diff --git a/examples/website/data-filter/tsconfig.json b/examples/website/data-filter/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/data-filter/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/election/package.json b/examples/website/election/package.json index 8cd0eb90c82..b8c2768fc84 100644 --- a/examples/website/election/package.json +++ b/examples/website/election/package.json @@ -9,7 +9,7 @@ "build": "vite build" }, "dependencies": { - "d3-scale": "^2.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0" }, "devDependencies": { diff --git a/examples/website/geojson/app.jsx b/examples/website/geojson/app.tsx similarity index 80% rename from examples/website/geojson/app.jsx rename to examples/website/geojson/app.tsx index 669880eb7d1..ce68e1b9bf9 100644 --- a/examples/website/geojson/app.jsx +++ b/examples/website/geojson/app.tsx @@ -6,11 +6,14 @@ import {GeoJsonLayer, PolygonLayer} from '@deck.gl/layers'; import {LightingEffect, AmbientLight, _SunLight as SunLight} from '@deck.gl/core'; import {scaleThreshold} from 'd3-scale'; +import type {Color, Position, PickingInfo, MapViewState} from '@deck.gl/core'; +import type {Feature, Geometry} from 'geojson'; + // Source data GeoJSON const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json'; // eslint-disable-line -export const COLOR_SCALE = scaleThreshold() +export const COLOR_SCALE = scaleThreshold() .domain([-0.6, -0.45, -0.3, -0.15, 0, 0.15, 0.3, 0.45, 0.6, 0.75, 0.9, 1.05, 1.2]) .range([ [65, 182, 196], @@ -29,7 +32,7 @@ export const COLOR_SCALE = scaleThreshold() [128, 0, 38] ]); -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { latitude: 49.254, longitude: -123.13, zoom: 11, @@ -52,7 +55,7 @@ const dirLight = new SunLight({ _shadow: true }); -const landCover = [ +const landCover: Position[][] = [ [ [-123.0, 49.196], [-123.0, 49.324], @@ -61,7 +64,13 @@ const landCover = [ ] ]; -function getTooltip({object}) { +type BlockProperties = { + valuePerParcel: number; + valuePerSqm: number; + growth: number; +}; + +function getTooltip({object}: PickingInfo>) { return ( object && { html: `\ @@ -75,7 +84,10 @@ function getTooltip({object}) { ); } -export default function App({data = DATA_URL, mapStyle = MAP_STYLE}) { +export default function App({data = DATA_URL, mapStyle = MAP_STYLE}: { + data?: string | Feature[]; + mapStyle?: string; +}) { const [effects] = useState(() => { const lightingEffect = new LightingEffect({ambientLight, dirLight}); lightingEffect.shadowColor = [0, 0, 0, 0.5]; @@ -84,14 +96,14 @@ export default function App({data = DATA_URL, mapStyle = MAP_STYLE}) { const layers = [ // only needed when using shadows - a plane for shadows to drop on - new PolygonLayer({ + new PolygonLayer({ id: 'ground', data: landCover, stroked: false, getPolygon: f => f, getFillColor: [0, 0, 0, 0] }), - new GeoJsonLayer({ + new GeoJsonLayer({ id: 'geojson', data, opacity: 0.8, @@ -119,6 +131,6 @@ export default function App({data = DATA_URL, mapStyle = MAP_STYLE}) { ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/geojson/index.html b/examples/website/geojson/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/geojson/index.html +++ b/examples/website/geojson/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/geojson/package.json b/examples/website/geojson/package.json index f896fee7312..1628f71a7ef 100644 --- a/examples/website/geojson/package.json +++ b/examples/website/geojson/package.json @@ -9,7 +9,10 @@ "build": "vite build" }, "dependencies": { - "d3-scale": "^2.0.0", + "@types/d3-scale": "^4.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/geojson/tsconfig.json b/examples/website/geojson/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/geojson/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/google-3d-tiles/package.json b/examples/website/google-3d-tiles/package.json index eb14e343581..a0f60ea5a76 100644 --- a/examples/website/google-3d-tiles/package.json +++ b/examples/website/google-3d-tiles/package.json @@ -9,7 +9,7 @@ "build": "vite build" }, "dependencies": { - "d3-scale": "^2.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0" }, "devDependencies": { diff --git a/examples/website/heatmap/app.jsx b/examples/website/heatmap/app.tsx similarity index 74% rename from examples/website/heatmap/app.jsx rename to examples/website/heatmap/app.tsx index 7a591537eba..21353b037d9 100644 --- a/examples/website/heatmap/app.jsx +++ b/examples/website/heatmap/app.tsx @@ -4,10 +4,12 @@ import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {HeatmapLayer} from '@deck.gl/aggregation-layers'; +import type {MapViewState} from '@deck.gl/core'; + const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/screen-grid/uber-pickup-locations.json'; // eslint-disable-line -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -73.75, latitude: 40.73, zoom: 9, @@ -18,15 +20,23 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; +type DataPoint = [longitude: number, latitude: number, count: number]; + export default function App({ data = DATA_URL, intensity = 1, threshold = 0.03, radiusPixels = 30, mapStyle = MAP_STYLE +}: { + data?: string | DataPoint[]; + intensity?: number; + threshold?: number; + radiusPixels?: number; + mapStyle?: string; }) { const layers = [ - new HeatmapLayer({ + new HeatmapLayer({ data, id: 'heatmap-layer', pickable: false, @@ -45,6 +55,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/heatmap/index.html b/examples/website/heatmap/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/heatmap/index.html +++ b/examples/website/heatmap/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/heatmap/package.json b/examples/website/heatmap/package.json index 7034ab4754c..10d47176060 100644 --- a/examples/website/heatmap/package.json +++ b/examples/website/heatmap/package.json @@ -8,6 +8,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/heatmap/tsconfig.json b/examples/website/heatmap/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/heatmap/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/highway/app.jsx b/examples/website/highway/app.tsx similarity index 55% rename from examples/website/highway/app.jsx rename to examples/website/highway/app.tsx index 3342494cec4..a3c7d720715 100644 --- a/examples/website/highway/app.jsx +++ b/examples/website/highway/app.tsx @@ -4,8 +4,11 @@ import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {GeoJsonLayer} from '@deck.gl/layers'; import {scaleLinear, scaleThreshold} from 'd3-scale'; +import {CSVLoader} from '@loaders.gl/csv'; +import {load} from '@loaders.gl/core'; -import {csv} from 'd3-request'; +import {Feature, LineString, MultiLineString} from 'geojson'; +import type {Color, PickingInfo, MapViewState} from '@deck.gl/core'; // Source data GeoJSON const DATA_URL = { @@ -14,11 +17,7 @@ const DATA_URL = { 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() +export const COLOR_SCALE = scaleThreshold() .domain([0, 4, 8, 12, 20, 32, 52, 84, 136, 220]) .range([ [26, 152, 80], @@ -35,7 +34,7 @@ export const COLOR_SCALE = scaleThreshold() const WIDTH_SCALE = scaleLinear().clamp(true).domain([0, 200]).range([10, 2000]); -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { latitude: 38, longitude: -100, zoom: 4, @@ -45,29 +44,56 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; -function aggregateAccidents(accidents) { - const incidents = {}; - const fatalities = {}; +type Accident = { + state: string; + type: string; + id: string; + year: number; + incidents: number; + fatalities: number; +}; + +type RoadProperties = { + state: string; + type: string; + id: string; + name: string; + length: number; +}; + +type Road = Feature; + +function getKey({state, type, id}: Accident | RoadProperties) { + return `${state}-${type}-${id}`; +} + +function aggregateAccidents(accidents?: Accident[]) { + const incidents: {[year: number]: Record} = {}; + const fatalities: {[year: number]: Record} = {}; if (accidents) { - accidents.forEach(a => { + for (const a of accidents) { 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) { +function renderTooltip({fatalities, incidents, year, hoverInfo}: { + fatalities: {[year: number]: Record}; + incidents: {[year: number]: Record}; + year: number; + hoverInfo?: PickingInfo; +}) { + if (!hoverInfo?.object) { return null; } + const {object, x, y} = hoverInfo; const props = object.properties; const key = getKey(props); const f = fatalities[year][key]; @@ -94,41 +120,43 @@ function renderTooltip({fatalities, incidents, year, hoverInfo}) { ); } -export default function App({roads = DATA_URL.ROADS, year, accidents, mapStyle = MAP_STYLE}) { - const [hoverInfo, setHoverInfo] = useState({}); +export default function App({ + roads = DATA_URL.ROADS, + year, + accidents, + mapStyle = MAP_STYLE +}: { + roads?: string | Road[]; + accidents?: Accident[]; + year?: number; + mapStyle?: string; +}) { + 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({ + new GeoJsonLayer({ id: 'geojson', data: roads, - stroked: false, - filled: false, lineWidthMinPixels: 0.5, - parameters: { - depthTest: false - }, - getLineColor, - getLineWidth, + getLineColor: (f: Road) => { + 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); + }, + getLineWidth: (f: Road) => { + if (!incidents[year]) { + return 10; + } + const key = getKey(f.properties); + const incidentsPer1KMile = ((incidents[year][key] || 0) / f.properties.length) * 1000; + return WIDTH_SCALE(incidentsPer1KMile); + }, pickable: true, onHover: setHoverInfo, @@ -159,19 +187,11 @@ export default function App({roads = DATA_URL.ROADS, year, accidents, mapStyle = ); } -export function renderToDOM(container) { +export async function renderToDOM(container: HTMLDivElement) { const root = createRoot(container); root.render(); - const formatRow = d => ({ - ...d, - incidents: Number(d.incidents), - fatalities: Number(d.fatalities) - }); + const accidents = (await load(DATA_URL.ACCIDENTS, CSVLoader)).data; - csv(DATA_URL.ACCIDENTS, formatRow, (error, response) => { - if (!error) { - root.render(); - } - }); + root.render(); } diff --git a/examples/website/highway/index.html b/examples/website/highway/index.html index 3301ff8265b..a3ac6516a54 100644 --- a/examples/website/highway/index.html +++ b/examples/website/highway/index.html @@ -14,7 +14,7 @@
diff --git a/examples/website/highway/package.json b/examples/website/highway/package.json index 4bc0dae4c1f..4dcba2539d0 100644 --- a/examples/website/highway/package.json +++ b/examples/website/highway/package.json @@ -9,8 +9,11 @@ "build": "vite build" }, "dependencies": { - "d3-request": "^1.0.5", - "d3-scale": "^2.0.0", + "@loaders.gl/csv": "^4.1.4", + "@types/d3-scale": "^4.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/highway/tsconfig.json b/examples/website/highway/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/highway/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/icon/app.tsx b/examples/website/icon/app.tsx index 9a6d88156f7..a8e859f429b 100644 --- a/examples/website/icon/app.tsx +++ b/examples/website/icon/app.tsx @@ -1,18 +1,21 @@ -import React, {useState} from 'react'; +import React, {useState, useCallback} from 'react'; import {createRoot} from 'react-dom/client'; import {Map} from 'react-map-gl//maplibre'; import DeckGL from '@deck.gl/react'; -import {MapView, PickingInfo} from '@deck.gl/core'; +import {MapView} from '@deck.gl/core'; import {IconLayer} from '@deck.gl/layers'; import IconClusterLayer from './icon-cluster-layer'; +import type {IconClusterLayerPickingInfo} from './icon-cluster-layer'; +import type {PickingInfo, MapViewState} from '@deck.gl/core'; +import type {IconLayerProps} from '@deck.gl/layers'; // Source data CSV const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/icon/meteorites.json'; // eslint-disable-line const MAP_VIEW = new MapView({repeat: true}); -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -35, latitude: 36.7, zoom: 1.8, @@ -23,10 +26,16 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; -type CustomPickingInfo = PickingInfo & {objects?: any} | null; +type Meterite = { + coordinates: [longitude: number, latitude: number]; + name: string; + class: string; + mass: number; + year: number; +}; -function renderTooltip(info: CustomPickingInfo) { - const {object, objects, x, y} = info || {}; +function renderTooltip(info: IconClusterLayerPickingInfo) { + const {object, objects, x, y} = info; if (objects) { return ( @@ -49,7 +58,7 @@ function renderTooltip(info: CustomPickingInfo) { return null; } - return object.cluster ? ( + return 'cluster' in object && object.cluster ? (
{object.point_count} records
@@ -68,20 +77,21 @@ export default function App({ showCluster = true, mapStyle = MAP_STYLE }) { - const [hoverInfo, setHoverInfo] = useState(null); + const [hoverInfo, setHoverInfo] = useState | null>(null); - const hideTooltip = () => { + const hideTooltip = useCallback(() => { setHoverInfo(null); - }; - const expandTooltip = info => { + }, []); + const expandTooltip = useCallback((info: PickingInfo) => { if (info.picked && showCluster) { setHoverInfo(info); } else { setHoverInfo(null); } - }; + }, []); - const layerProps: Partial = { + const layerProps: IconLayerProps = { + id: 'icon', data, pickable: true, getPosition: d => d.coordinates, @@ -114,11 +124,11 @@ export default function App({ > - {renderTooltip(hoverInfo)} + {hoverInfo && renderTooltip(hoverInfo)} ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/icon/icon-cluster-layer.ts b/examples/website/icon/icon-cluster-layer.ts index f7ea85b87fb..850c88865f3 100644 --- a/examples/website/icon/icon-cluster-layer.ts +++ b/examples/website/icon/icon-cluster-layer.ts @@ -2,7 +2,15 @@ import {CompositeLayer} from '@deck.gl/core'; import {IconLayer, IconLayerProps} from '@deck.gl/layers'; import Supercluster from 'supercluster'; -function getIconName(size) { +import type {PointFeature, ClusterFeature, ClusterProperties} from 'supercluster'; +import type {UpdateParameters, PickingInfo} from '@deck.gl/core'; + +export type IconClusterLayerPickingInfo = PickingInfo< + DataT | (DataT & ClusterProperties), + {objects?: DataT[];} +>; + +function getIconName(size: number): string { if (size === 0) { return ''; } @@ -15,32 +23,33 @@ function getIconName(size) { return 'marker-100'; } -function getIconSize(size) { +function getIconSize(size: number): number { return Math.min(100, size) / 100 + 1; } -export default class IconClusterLayer extends CompositeLayer< - Required & ExtraProps +export default class IconClusterLayer extends CompositeLayer< + Required> & ExtraProps > { state!: { - data: any; - index: any; + data: (PointFeature | ClusterFeature)[]; + index: Supercluster; z: number; }; - shouldUpdateState({changeFlags}) { + shouldUpdateState({changeFlags}: UpdateParameters) { return changeFlags.somethingChanged; } - updateState({props, oldProps, changeFlags}) { + updateState({props, oldProps, changeFlags}: UpdateParameters) { const rebuildIndex = changeFlags.dataChanged || props.sizeScale !== oldProps.sizeScale; if (rebuildIndex) { - const index = new Supercluster({maxZoom: 16, radius: props.sizeScale * Math.sqrt(2)}); + const index = new Supercluster({maxZoom: 16, radius: props.sizeScale * Math.sqrt(2)}); index.load( - props.data.map(d => ({ - geometry: {coordinates: props.getPosition(d)}, + // @ts-ignore Supercluster expects proper GeoJSON feature + (props.data as DataT[]).map(d => ({ + geometry: {coordinates: (props.getPosition as Function)(d)}, properties: d })) ); @@ -56,33 +65,35 @@ export default class IconClusterLayer extends Compos } } - getPickingInfo({info, mode}) { - const pickedObject = info.object && info.object.properties; + getPickingInfo({info, mode}: {info: PickingInfo | ClusterFeature>; mode: string;}): IconClusterLayerPickingInfo { + const pickedObject = info.object?.properties; if (pickedObject) { + let objects: DataT[] | undefined; if (pickedObject.cluster && mode !== 'hover') { - info.objects = this.state.index + objects = this.state.index .getLeaves(pickedObject.cluster_id, 25) .map(f => f.properties); } - info.object = pickedObject; + return {...info, object: pickedObject, objects}; } - return info; + return {...info, object: undefined}; } renderLayers() { const {data} = this.state; const {iconAtlas, iconMapping, sizeScale} = this.props; - return new IconLayer( + return new IconLayer | ClusterFeature>({ + data, + iconAtlas, + iconMapping, + sizeScale, + getPosition: d => d.geometry.coordinates as [number, number], + getIcon: d => getIconName(d.properties.cluster ? d.properties.point_count : 1), + getSize: d => getIconSize(d.properties.cluster ? d.properties.point_count : 1) + }, this.getSubLayerProps({ id: 'icon', - data, - iconAtlas, - iconMapping, - sizeScale, - getPosition: d => d.geometry.coordinates, - getIcon: d => getIconName(d.properties.cluster ? d.properties.point_count : 1), - getSize: d => getIconSize(d.properties.cluster ? d.properties.point_count : 1) }) ); } diff --git a/examples/website/icon/package.json b/examples/website/icon/package.json index a4cfcc61935..78b3783f124 100644 --- a/examples/website/icon/package.json +++ b/examples/website/icon/package.json @@ -9,12 +9,14 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", "react-map-gl": "^7.1.0", - "supercluster": "^6.0.1" + "supercluster": "^8.0.1" }, "devDependencies": { "typescript": "^4.6.0", diff --git a/examples/website/icon/tsconfig.json b/examples/website/icon/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/icon/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/image-tile/app.jsx b/examples/website/image-tile/app.tsx similarity index 69% rename from examples/website/image-tile/app.jsx rename to examples/website/image-tile/app.tsx index 8b6cae6f886..5a7212149ae 100644 --- a/examples/website/image-tile/app.jsx +++ b/examples/website/image-tile/app.tsx @@ -3,13 +3,17 @@ import React, {useState, useEffect} from 'react'; import {createRoot} from 'react-dom/client'; import DeckGL from '@deck.gl/react'; -import {OrthographicView, COORDINATE_SYSTEM} from '@deck.gl/core'; +import {OrthographicView} from '@deck.gl/core'; import {TileLayer} from '@deck.gl/geo-layers'; import {BitmapLayer} from '@deck.gl/layers'; import {load} from '@loaders.gl/core'; import {clamp} from '@math.gl/core'; -const INITIAL_VIEW_STATE = { +import type {PickingInfo, OrthographicViewState} from '@deck.gl/core'; +import type {TileLayerPickingInfo} from '@deck.gl/geo-layers'; +import type {BitmapLayerPickingInfo} from '@deck.gl/layers'; + +const INITIAL_VIEW_STATE: OrthographicViewState = { target: [13000, 13000, 0], zoom: -7 }; @@ -17,7 +21,7 @@ const INITIAL_VIEW_STATE = { const ROOT_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/image-tiles/moon.image'; -function getTooltip({tile, bitmap}) { +function getTooltip({tile, bitmap}: TileLayerPickingInfo) { if (tile && bitmap) { const {x, y, z} = tile.index; return `\ @@ -27,8 +31,11 @@ function getTooltip({tile, bitmap}) { return null; } -export default function App({autoHighlight = true, onTilesLoad}) { - const [dimensions, setDimensions] = useState(null); +export default function App({autoHighlight = true, onTilesLoad}: { + autoHighlight?: boolean; + onTilesLoad?: () => void; +}) { + const [dimensions, setDimensions] = useState<{width: number; height: number; tileSize: number;}>(); useEffect(() => { const getMetaData = async () => { @@ -37,14 +44,14 @@ export default function App({autoHighlight = true, onTilesLoad}) { const xmlText = await response.text(); const dziXML = new DOMParser().parseFromString(xmlText, 'text/xml'); - if (Number(dziXML.getElementsByTagName('Image')[0].attributes.Overlap.value) !== 0) { + if (Number(dziXML.getElementsByTagName('Image')[0].attributes.getNamedItem('Overlap')?.value) !== 0) { // eslint-disable-next-line no-undef, no-console console.warn('Overlap parameter is nonzero and should be 0'); } setDimensions({ - height: Number(dziXML.getElementsByTagName('Size')[0].attributes.Height.value), - width: Number(dziXML.getElementsByTagName('Size')[0].attributes.Width.value), - tileSize: Number(dziXML.getElementsByTagName('Image')[0].attributes.TileSize.value) + height: Number(dziXML.getElementsByTagName('Size')[0].attributes.getNamedItem('Height')?.value), + width: Number(dziXML.getElementsByTagName('Size')[0].attributes.getNamedItem('Width')?.value), + tileSize: Number(dziXML.getElementsByTagName('Image')[0].attributes.getNamedItem('TileSize')?.value) }); }; getMetaData(); @@ -52,28 +59,25 @@ export default function App({autoHighlight = true, onTilesLoad}) { const tileLayer = dimensions && - new TileLayer({ + new TileLayer({ pickable: autoHighlight, tileSize: dimensions.tileSize, autoHighlight, highlightColor: [60, 60, 60, 100], minZoom: -7, maxZoom: 0, - coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, extent: [0, 0, dimensions.width, dimensions.height], getTileData: ({index}) => { const {x, y, z} = index; - return load(`${ROOT_URL}/moon.image_files/${15 + z}/${x}_${y}.jpeg`); + return load(`${ROOT_URL}/moon.image_files/${15 + z}/${x}_${y}.jpeg`) as Promise; }, onViewportLoad: onTilesLoad, renderSubLayers: props => { - const { - bbox: {left, bottom, right, top} - } = props.tile; + const [[left, bottom], [right, top]] = props.tile.boundingBox; const {width, height} = dimensions; - return new BitmapLayer(props, { - data: null, + const {data, ...otherProps} = props; + return new BitmapLayer(otherProps, { image: props.data, bounds: [ clamp(left, 0, width), @@ -96,6 +100,6 @@ export default function App({autoHighlight = true, onTilesLoad}) { ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/image-tile/index.html b/examples/website/image-tile/index.html index fb3b0cd5bdd..44eaa6d1bd5 100644 --- a/examples/website/image-tile/index.html +++ b/examples/website/image-tile/index.html @@ -19,7 +19,7 @@
diff --git a/examples/website/image-tile/package.json b/examples/website/image-tile/package.json index 06d4d62318c..7da7162e7d6 100644 --- a/examples/website/image-tile/package.json +++ b/examples/website/image-tile/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/examples/website/image-tile/tsconfig.json b/examples/website/image-tile/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/image-tile/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/line/app.jsx b/examples/website/line/app.tsx similarity index 60% rename from examples/website/line/app.jsx rename to examples/website/line/app.tsx index 943d35fa488..c8e7667281e 100644 --- a/examples/website/line/app.jsx +++ b/examples/website/line/app.tsx @@ -4,6 +4,8 @@ import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {LineLayer, ScatterplotLayer} from '@deck.gl/layers'; +import type {PickingInfo, MapViewState} from '@deck.gl/core'; + // Source data CSV const DATA_URL = { AIRPORTS: @@ -12,7 +14,21 @@ const DATA_URL = { 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/line/heathrow-flights.json' // eslint-disable-line }; -const INITIAL_VIEW_STATE = { +type Airport = { + type: 'major' | 'mid' | 'small'; + name: string; + abbrev: string; // airport code + coordinates: [longitude: number, latitude: number]; +}; + +type FlightPath = { + start: [longitude: number, latitude: number, altitude: number]; + end: [longitude: number, latitude: number, altitude: number]; + country: string; + name: string; // tail number +}; + +const INITIAL_VIEW_STATE: MapViewState = { latitude: 47.65, longitude: 7, zoom: 4.5, @@ -23,28 +39,11 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; -function getColor(d) { - const z = d.start[2]; - const r = z / 10000; - - return [255 * (1 - r * 2), 128 * r, 255 * r, 255 * (1 - r)]; -} - -function getSize(type) { - if (type.search('major') >= 0) { - return 100; - } - if (type.search('small') >= 0) { - return 30; - } - return 60; -} - -function getTooltip({object}) { +function getTooltip({object}: PickingInfo) { return ( object && `\ - ${object.country || object.abbrev || ''} + ${(object as FlightPath).country || (object as Airport).abbrev || ''} ${object.name.indexOf('0x') >= 0 ? '' : object.name}` ); } @@ -52,27 +51,44 @@ function getTooltip({object}) { export default function App({ airports = DATA_URL.AIRPORTS, flightPaths = DATA_URL.FLIGHT_PATHS, - getWidth = 3, + lineWidth = 3, mapStyle = MAP_STYLE +}: { + airports?: string | Airport[]; + flightPaths?: string | FlightPath[]; + lineWidth?: number; + mapStyle?: string; }) { const layers = [ - new ScatterplotLayer({ + new ScatterplotLayer({ id: 'airports', data: airports, radiusScale: 20, getPosition: d => d.coordinates, getFillColor: [255, 140, 0], - getRadius: d => getSize(d.type), + getRadius: d => { + if (d.type.search('major') >= 0) { + return 100; + } + if (d.type.search('small') >= 0) { + return 30; + } + return 60; + }, pickable: true }), - new LineLayer({ + new LineLayer({ id: 'flight-paths', data: flightPaths, opacity: 0.8, getSourcePosition: d => d.start, getTargetPosition: d => d.end, - getColor, - getWidth, + getColor: d => { + const z = d.start[2]; + const r = z / 10000; + return [255 * (1 - r * 2), 128 * r, 255 * r, 255 * (1 - r)]; + }, + getWidth: lineWidth, pickable: true }) ]; @@ -98,6 +114,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/line/package.json b/examples/website/line/package.json index 924f9a0fe52..3dbb152050e 100644 --- a/examples/website/line/package.json +++ b/examples/website/line/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/line/tsconfig.json b/examples/website/line/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/line/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/map-tile/app.jsx b/examples/website/map-tile/app.tsx similarity index 75% rename from examples/website/map-tile/app.jsx rename to examples/website/map-tile/app.tsx index d9b3d64c90a..ecf33c52132 100644 --- a/examples/website/map-tile/app.jsx +++ b/examples/website/map-tile/app.tsx @@ -6,7 +6,10 @@ import {MapView} from '@deck.gl/core'; import {TileLayer} from '@deck.gl/geo-layers'; import {BitmapLayer, PathLayer} from '@deck.gl/layers'; -const INITIAL_VIEW_STATE = { +import type {Position, PickingInfo, MapViewState} from '@deck.gl/core'; +import type {TileLayerPickingInfo} from '@deck.gl/geo-layers'; + +const INITIAL_VIEW_STATE: MapViewState = { latitude: 47.65, longitude: 7, zoom: 4.5, @@ -15,7 +18,7 @@ const INITIAL_VIEW_STATE = { bearing: 0 }; -const COPYRIGHT_LICENSE_STYLE = { +const COPYRIGHT_LICENSE_STYLE: React.CSSProperties = { position: 'absolute', right: 0, bottom: 0, @@ -24,7 +27,7 @@ const COPYRIGHT_LICENSE_STYLE = { font: '12px/20px Helvetica Neue,Arial,Helvetica,sans-serif' }; -const LINK_STYLE = { +const LINK_STYLE: React.CSSProperties = { textDecoration: 'none', color: 'rgba(0,0,0,.75)', cursor: 'grab' @@ -33,7 +36,7 @@ const LINK_STYLE = { /* global window */ const devicePixelRatio = (typeof window !== 'undefined' && window.devicePixelRatio) || 1; -function getTooltip({tile}) { +function getTooltip({tile}: TileLayerPickingInfo) { if (tile) { const {x, y, z} = tile.index; return `tile: x: ${x}, y: ${y}, z: ${z}`; @@ -41,8 +44,11 @@ function getTooltip({tile}) { return null; } -export default function App({showBorder = false, onTilesLoad = null}) { - const tileLayer = new TileLayer({ +export default function App({showBorder = false, onTilesLoad}: { + showBorder?: boolean; + onTilesLoad?: () => void; +}) { + const tileLayer = new TileLayer({ // https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers data: [ 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' @@ -62,18 +68,16 @@ export default function App({showBorder = false, onTilesLoad = null}) { tileSize: 256, zoomOffset: devicePixelRatio === 1 ? -1 : 0, renderSubLayers: props => { - const { - bbox: {west, south, east, north} - } = props.tile; + const [[west, south], [east, north]] = props.tile.boundingBox; + const {data, ...otherProps} = props; return [ - new BitmapLayer(props, { - data: null, - image: props.data, + new BitmapLayer(otherProps, { + image: data, bounds: [west, south, east, north] }), showBorder && - new PathLayer({ + new PathLayer({ id: `${props.id}-border`, data: [ [ @@ -110,6 +114,6 @@ export default function App({showBorder = false, onTilesLoad = null}) { ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/map-tile/index.html b/examples/website/map-tile/index.html index edefb08043a..fd90c8650d3 100644 --- a/examples/website/map-tile/index.html +++ b/examples/website/map-tile/index.html @@ -18,7 +18,7 @@
diff --git a/examples/website/map-tile/package.json b/examples/website/map-tile/package.json index 80089ea9289..18b6023331e 100644 --- a/examples/website/map-tile/package.json +++ b/examples/website/map-tile/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/examples/website/map-tile/tsconfig.json b/examples/website/map-tile/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/map-tile/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/mapbox/package.json b/examples/website/mapbox/package.json index ce1a6679539..87853f8a9e2 100644 --- a/examples/website/mapbox/package.json +++ b/examples/website/mapbox/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@loaders.gl/csv": "^4.1.4", - "d3-scale": "^2.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "mapbox-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/orthographic/package.json b/examples/website/orthographic/package.json index 4c3284c1d95..4f05cdc336d 100644 --- a/examples/website/orthographic/package.json +++ b/examples/website/orthographic/package.json @@ -9,7 +9,7 @@ "build": "vite build" }, "dependencies": { - "d3-scale": "^2.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/examples/website/plot/package.json b/examples/website/plot/package.json index 8fcbf6c70c9..8b4379d8926 100644 --- a/examples/website/plot/package.json +++ b/examples/website/plot/package.json @@ -9,7 +9,7 @@ "build": "vite build" }, "dependencies": { - "d3-scale": "^2.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/examples/website/point-cloud/app.jsx b/examples/website/point-cloud/app.tsx similarity index 66% rename from examples/website/point-cloud/app.jsx rename to examples/website/point-cloud/app.tsx index c1d89b92247..2849f0e379b 100644 --- a/examples/website/point-cloud/app.jsx +++ b/examples/website/point-cloud/app.tsx @@ -1,17 +1,21 @@ /* eslint-disable no-unused-vars */ -import React, {useState, useEffect} from 'react'; +import React, {useState, useEffect, useCallback} from 'react'; import {createRoot} from 'react-dom/client'; import DeckGL from '@deck.gl/react'; -import {COORDINATE_SYSTEM, OrbitView, LinearInterpolator} from '@deck.gl/core'; +import {OrbitView, LinearInterpolator} from '@deck.gl/core'; import {PointCloudLayer} from '@deck.gl/layers'; import {LASWorkerLoader} from '@loaders.gl/las'; +import type {OrbitViewState} from '@deck.gl/core'; + +// TODO - export from loaders? +type LASMesh = (typeof LASWorkerLoader)['dataType']; // Data source: kaarta.com const LAZ_SAMPLE = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/point-cloud-laz/indoor.0.1.laz'; -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: OrbitViewState = { target: [0, 0, 0], rotationX: 0, rotationOrbit: 0, @@ -22,9 +26,11 @@ const INITIAL_VIEW_STATE = { const transitionInterpolator = new LinearInterpolator(['rotationOrbit']); -export default function App({onLoad}) { - const [viewState, updateViewState] = useState(INITIAL_VIEW_STATE); - const [isLoaded, setIsLoaded] = useState(false); +export default function App({onLoad}: { + onLoad?: (data: {count: number; progress: number;}) => void; +}) { + const [viewState, updateViewState] = useState(INITIAL_VIEW_STATE); + const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { if (!isLoaded) { @@ -33,7 +39,7 @@ export default function App({onLoad}) { const rotateCamera = () => { updateViewState(v => ({ ...v, - rotationOrbit: v.rotationOrbit + 120, + rotationOrbit: v.rotationOrbit! + 120, transitionDuration: 2400, transitionInterpolator, onTransitionEnd: rotateCamera @@ -42,7 +48,8 @@ export default function App({onLoad}) { rotateCamera(); }, [isLoaded]); - const onDataLoad = ({header}) => { + const onDataLoad = useCallback((data: any) => { + const header = (data as LASMesh).header!; if (header.boundingBox) { const [mins, maxs] = header.boundingBox; // File contains bounding box info @@ -58,14 +65,13 @@ export default function App({onLoad}) { if (onLoad) { onLoad({count: header.vertexCount, progress: 1}); } - }; + }, []); const layers = [ - new PointCloudLayer({ + new PointCloudLayer({ id: 'laz-point-cloud-layer', data: LAZ_SAMPLE, onDataLoad, - coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, getNormal: [0, 1, 0], getColor: [255, 255, 255], opacity: 0.5, @@ -77,18 +83,14 @@ export default function App({onLoad}) { return ( updateViewState(v.viewState)} layers={layers} - parameters={{ - clearColor: [0.93, 0.86, 0.81, 1] - }} /> ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/point-cloud/index.html b/examples/website/point-cloud/index.html index d41b8fe6e92..e38dad88a07 100644 --- a/examples/website/point-cloud/index.html +++ b/examples/website/point-cloud/index.html @@ -11,6 +11,7 @@ margin:0; padding:0; overflow:hidden; + background:#ecdbce; } @@ -18,7 +19,7 @@
diff --git a/examples/website/point-cloud/package.json b/examples/website/point-cloud/package.json index 836c2cd9d95..0f8b4a8acd5 100644 --- a/examples/website/point-cloud/package.json +++ b/examples/website/point-cloud/package.json @@ -10,6 +10,8 @@ }, "dependencies": { "@loaders.gl/las": "^4.1.4", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/examples/website/point-cloud/tsconfig.json b/examples/website/point-cloud/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/point-cloud/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/radio/package.json b/examples/website/radio/package.json index f06798200e7..b00fd0be36a 100644 --- a/examples/website/radio/package.json +++ b/examples/website/radio/package.json @@ -12,7 +12,7 @@ "@loaders.gl/csv": "^4.1.4", "@material-ui/core": "^4.11.3", "@material-ui/lab": "^4.0.0-alpha.57", - "d3-scale": "^2.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/scatterplot/app.jsx b/examples/website/scatterplot/app.tsx similarity index 71% rename from examples/website/scatterplot/app.jsx rename to examples/website/scatterplot/app.tsx index cd4f9ea5ac1..38bfbc37589 100644 --- a/examples/website/scatterplot/app.jsx +++ b/examples/website/scatterplot/app.tsx @@ -4,14 +4,16 @@ import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {ScatterplotLayer} from '@deck.gl/layers'; -const MALE_COLOR = [0, 128, 255]; -const FEMALE_COLOR = [255, 0, 128]; +import type {Color, MapViewState} from '@deck.gl/core'; + +const MALE_COLOR: Color = [0, 128, 255]; +const FEMALE_COLOR: Color = [255, 0, 128]; // Source data CSV const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/scatterplot/manhattan.json'; // eslint-disable-line -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -74, latitude: 40.7, zoom: 11, @@ -20,15 +22,23 @@ const INITIAL_VIEW_STATE = { bearing: 0 }; +type DataPoint = [longitude: number, latitude: number, gender: number]; + export default function App({ data = DATA_URL, radius = 30, maleColor = MALE_COLOR, femaleColor = FEMALE_COLOR, mapStyle = 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json' +}: { + data?: string | DataPoint[]; + radius?: number; + maleColor?: Color; + femaleColor?: Color; + mapStyle?: string; }) { const layers = [ - new ScatterplotLayer({ + new ScatterplotLayer({ id: 'scatter-plot', data, radiusScale: radius, @@ -49,6 +59,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/scatterplot/index.html b/examples/website/scatterplot/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/scatterplot/index.html +++ b/examples/website/scatterplot/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/scatterplot/package.json b/examples/website/scatterplot/package.json index 4f412ad3f84..ae81dd56410 100644 --- a/examples/website/scatterplot/package.json +++ b/examples/website/scatterplot/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/scatterplot/tsconfig.json b/examples/website/scatterplot/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/scatterplot/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/scenegraph/app.jsx b/examples/website/scenegraph/app.jsx deleted file mode 100644 index 8909a77b679..00000000000 --- a/examples/website/scenegraph/app.jsx +++ /dev/null @@ -1,135 +0,0 @@ -/* global fetch, setTimeout, clearTimeout */ -import React, {useEffect, useState} from 'react'; -import {createRoot} from 'react-dom/client'; -import {Map} from 'react-map-gl/maplibre'; -import DeckGL from '@deck.gl/react'; -import {ScenegraphLayer} from '@deck.gl/mesh-layers'; - -// Data provided by the OpenSky Network, http://www.opensky-network.org -const DATA_URL = 'https://opensky-network.org/api/states/all'; -const MODEL_URL = - 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/scenegraph-layer/airplane.glb'; -const REFRESH_TIME = 30000; - -const ANIMATIONS = { - '*': {speed: 1} -}; - -const INITIAL_VIEW_STATE = { - latitude: 39.1, - longitude: -94.57, - zoom: 3.8, - maxZoom: 16, - pitch: 0, - bearing: 0 -}; - -const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; - -const DATA_INDEX = { - UNIQUE_ID: 0, - CALL_SIGN: 1, - ORIGIN_COUNTRY: 2, - LONGITUDE: 5, - LATITUDE: 6, - BARO_ALTITUDE: 7, - VELOCITY: 9, - TRUE_TRACK: 10, - VERTICAL_RATE: 11, - GEO_ALTITUDE: 13, - POSITION_SOURCE: 16 -}; - -function verticalRateToAngle(object) { - // Return: -90 looking up, +90 looking down - const verticalRate = object[DATA_INDEX.VERTICAL_RATE] || 0; - const velocity = object[DATA_INDEX.VELOCITY] || 0; - return (-Math.atan2(verticalRate, velocity) * 180) / Math.PI; -} - -function getTooltip({object}) { - return ( - object && - `\ - Call Sign: ${object[DATA_INDEX.CALL_SIGN] || ''} - Country: ${object[DATA_INDEX.ORIGIN_COUNTRY] || ''} - Vertical Rate: ${object[DATA_INDEX.VERTICAL_RATE] || 0} m/s - Velocity: ${object[DATA_INDEX.VELOCITY] || 0} m/s - Direction: ${object[DATA_INDEX.TRUE_TRACK] || 0}` - ); -} - -export default function App({sizeScale = 25, onDataLoad, mapStyle = MAP_STYLE}) { - const [data, setData] = useState(null); - const [timer, setTimer] = useState({}); - - useEffect(() => { - fetch(DATA_URL) - .then(resp => resp.json()) - .then(resp => { - if (resp && resp.states && timer.id !== null) { - // In order to keep the animation smooth we need to always return the same - // objects in the exact same order. This function will discard new objects - // and only update existing ones. - let sortedData = resp.states; - if (data) { - const dataAsObj = {}; - sortedData.forEach(entry => (dataAsObj[entry[DATA_INDEX.UNIQUE_ID]] = entry)); - sortedData = data.map(entry => dataAsObj[entry[DATA_INDEX.UNIQUE_ID]] || entry); - } - - setData(sortedData); - - if (onDataLoad) { - onDataLoad(sortedData.length); - } - } - }) - .finally(() => { - timer.nextTimeoutId = setTimeout(() => setTimer({id: timer.nextTimeoutId}), REFRESH_TIME); - }); - - return () => { - clearTimeout(timer.nextTimeoutId); - timer.id = null; - }; - }, [timer]); - - const layer = - data && - new ScenegraphLayer({ - id: 'scenegraph-layer', - data, - pickable: true, - sizeScale, - scenegraph: MODEL_URL, - _animations: ANIMATIONS, - sizeMinPixels: 0.1, - sizeMaxPixels: 1.5, - getPosition: d => [ - d[DATA_INDEX.LONGITUDE] || 0, - d[DATA_INDEX.LATITUDE] || 0, - d[DATA_INDEX.GEO_ALTITUDE] || 0 - ], - getOrientation: d => [verticalRateToAngle(d), -d[DATA_INDEX.TRUE_TRACK] || 0, 90] - // TODO(v9) Re-enable once attribute transitions working (#8392) - // transitions: { - // getPosition: REFRESH_TIME * 0.9 - // } - }); - - return ( - - - - ); -} - -export function renderToDOM(container) { - createRoot(container).render(); -} diff --git a/examples/website/scenegraph/app.tsx b/examples/website/scenegraph/app.tsx new file mode 100644 index 00000000000..01a1af3f281 --- /dev/null +++ b/examples/website/scenegraph/app.tsx @@ -0,0 +1,177 @@ +/* global fetch, setTimeout, clearTimeout */ +import React, {useEffect, useState} from 'react'; +import {createRoot} from 'react-dom/client'; +import {Map} from 'react-map-gl/maplibre'; +import DeckGL from '@deck.gl/react'; +import {ScenegraphLayer} from '@deck.gl/mesh-layers'; + +import type {ScenegraphLayerProps} from '@deck.gl/mesh-layers'; +import type {PickingInfo, MapViewState} from '@deck.gl/core'; + +// Data provided by the OpenSky Network, http://www.opensky-network.org +const DATA_URL = 'https://opensky-network.org/api/states/all'; +// For local debugging +// const DATA_URL = './all.json'; +const MODEL_URL = + 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/scenegraph-layer/airplane.glb'; +const REFRESH_TIME_SECONDS = 60; +const DROP_IF_OLDER_THAN_SECONDS = 120; + +const ANIMATIONS: ScenegraphLayerProps["_animations"] = { + '*': {speed: 1} +}; + +const INITIAL_VIEW_STATE: MapViewState = { + latitude: 39.1, + longitude: -94.57, + zoom: 3.8, + maxZoom: 16, + pitch: 0, + bearing: 0 +}; + +const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; + +// https://openskynetwork.github.io/opensky-api/rest.html#response +type Aircraft = [ + uniqueId: string, + callSign: string, + originCountry: string, + timePosition: number, + lastContact: number, + longitude: number | null, + latitude: number | null, + baroAltitude: number | null, + onGround: boolean, + velocity: number | null, + trueAttack: number | null, + verticalRate: number | null, + sensors: number[], + geoAltitude: number | null, + positionSource: number[], + category: number +]; + +const DATA_INDEX = { + UNIQUE_ID: 0, + CALL_SIGN: 1, + ORIGIN_COUNTRY: 2, + LAST_CONTACT: 4, + LONGITUDE: 5, + LATITUDE: 6, + BARO_ALTITUDE: 7, + VELOCITY: 9, + TRUE_TRACK: 10, + VERTICAL_RATE: 11, + GEO_ALTITUDE: 13, + CATEGORY: 17 +} as const; + +async function fetchData(): Promise { + const resp = await fetch(DATA_URL); + const {time, states} = await resp.json() as {time: number; states: Aircraft[]}; + // make lastContact timestamp relative to response time + for (const a of states) { + a[DATA_INDEX.LAST_CONTACT] -= time; + } + return states; +} + +function getTooltip({object}: PickingInfo) { + return ( + object && + `\ + Call Sign: ${object[DATA_INDEX.CALL_SIGN] || ''} + Country: ${object[DATA_INDEX.ORIGIN_COUNTRY] || ''} + Vertical Rate: ${object[DATA_INDEX.VERTICAL_RATE] || 0} m/s + Velocity: ${object[DATA_INDEX.VELOCITY] || 0} m/s + Direction: ${object[DATA_INDEX.TRUE_TRACK] || 0}` + ); +} + +export default function App({sizeScale = 25, onDataLoad, mapStyle = MAP_STYLE}: { + sizeScale?: number; + onDataLoad?: (count: number) => void; + mapStyle?: string; +}) { + const [data, setData] = useState(); + const [timer, setTimer] = useState<{id: number | null}>({id: null}); + + useEffect(() => { + timer.id++; + fetchData().then((newData) => { + if (timer.id === null) { + // Component has unmounted + return; + } + // In order to keep the animation smooth we need to always return the same + // object at a given index. This function will discard new objects + // and only update existing ones. + if (data) { + const dataById: Record = {}; + newData.forEach(entry => (dataById[entry[DATA_INDEX.UNIQUE_ID]] = entry)); + newData = data.map(entry => dataById[entry[DATA_INDEX.UNIQUE_ID]] || entry); + } + + setData(newData); + + if (onDataLoad) { + onDataLoad(newData.length); + } + }).finally(() => { + const timeoutId = window.setTimeout(() => setTimer({id: timeoutId}), REFRESH_TIME_SECONDS * 1000); + timer.id = timeoutId; + }); + + return () => { + clearTimeout(timer.id); + timer.id = null; + }; + }, [timer, data]); + + const layer = new ScenegraphLayer({ + id: 'scenegraph-layer', + data, + pickable: true, + sizeScale, + scenegraph: MODEL_URL, + _animations: ANIMATIONS, + sizeMinPixels: 0.1, + sizeMaxPixels: 1.5, + getPosition: d => [ + d[DATA_INDEX.LONGITUDE] ?? 0, + d[DATA_INDEX.LATITUDE] ?? 0, + d[DATA_INDEX.GEO_ALTITUDE] ?? 0 + ], + getOrientation: d => { + const verticalRate = d[DATA_INDEX.VERTICAL_RATE] ?? 0; + const velocity = d[DATA_INDEX.VELOCITY] ?? 0; + // -90 looking up, +90 looking down + const pitch = (-Math.atan2(verticalRate, velocity) * 180) / Math.PI; + const yaw = -d[DATA_INDEX.TRUE_TRACK] ?? 0; + return [pitch, yaw, 90]; + }, + getScale: d => { + const lastContact = d[DATA_INDEX.LAST_CONTACT]; + return lastContact < -DROP_IF_OLDER_THAN_SECONDS ? [0, 0, 0] : [1, 1, 1]; + }, + transitions: { + getPosition: REFRESH_TIME_SECONDS * 1000 + } + }); + + return ( + + + + ); +} + +export function renderToDOM(container: HTMLDivElement) { + createRoot(container).render(); +} diff --git a/examples/website/scenegraph/index.html b/examples/website/scenegraph/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/scenegraph/index.html +++ b/examples/website/scenegraph/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/scenegraph/package.json b/examples/website/scenegraph/package.json index 89603422736..c26a8a35452 100644 --- a/examples/website/scenegraph/package.json +++ b/examples/website/scenegraph/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/scenegraph/tsconfig.json b/examples/website/scenegraph/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/scenegraph/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/screen-grid/app.jsx b/examples/website/screen-grid/app.tsx similarity index 75% rename from examples/website/screen-grid/app.jsx rename to examples/website/screen-grid/app.tsx index 16ac0c28183..8b605ab85fb 100644 --- a/examples/website/screen-grid/app.jsx +++ b/examples/website/screen-grid/app.tsx @@ -4,11 +4,13 @@ import {Map} from 'react-map-gl/maplibre'; import DeckGL from '@deck.gl/react'; import {ScreenGridLayer} from '@deck.gl/aggregation-layers'; +import type {Color, MapViewState} from '@deck.gl/core'; + // Source data CSV const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/screen-grid/uber-pickup-locations.json'; // eslint-disable-line -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -73.75, latitude: 40.73, zoom: 9.6, @@ -19,7 +21,7 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; -const colorRange = [ +const colorRange: Color[] = [ [255, 255, 178, 25], [254, 217, 118, 85], [254, 178, 76, 127], @@ -28,16 +30,23 @@ const colorRange = [ [189, 0, 38, 255] ]; +type DataPoint = [longitude: number, latitude: number, count: number]; + export default function App({ data = DATA_URL, cellSize = 20, gpuAggregation = false, // TODO(v9): Re-enable GPU aggregation. aggregation = 'SUM', - disableGPUAggregation, mapStyle = MAP_STYLE +}: { + data?: string | DataPoint[]; + cellSize?: number; + gpuAggregation?: boolean; + aggregation?: 'SUM' | 'MEAN' | 'MIN' | 'MAX'; + mapStyle?: string; }) { const layers = [ - new ScreenGridLayer({ + new ScreenGridLayer({ id: 'grid', data, opacity: 0.8, @@ -50,20 +59,10 @@ export default function App({ }) ]; - const onInitialized = device => { - if (!device.features.has('webgl2')) { - console.warn('GPU aggregation is not supported'); // eslint-disable-line - if (disableGPUAggregation) { - disableGPUAggregation(); - } - } - }; - return ( @@ -71,6 +70,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/screen-grid/index.html b/examples/website/screen-grid/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/screen-grid/index.html +++ b/examples/website/screen-grid/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/screen-grid/package.json b/examples/website/screen-grid/package.json index c988dd3bf49..4dc08b09bf2 100644 --- a/examples/website/screen-grid/package.json +++ b/examples/website/screen-grid/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/screen-grid/tsconfig.json b/examples/website/screen-grid/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/screen-grid/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/terrain/README.md b/examples/website/terrain/README.md index ec36efb5720..ffb943881f3 100644 --- a/examples/website/terrain/README.md +++ b/examples/website/terrain/README.md @@ -11,7 +11,7 @@ To load the terrain tiles, you need a [Mapbox access token](https://docs.mapbox. export MapboxAccessToken= ``` -Or set `MAPBOX_TOKEN` directly in `app.jsx`. +Or set `MAPBOX_TOKEN` directly in `app.tsx`. ```bash # install dependencies diff --git a/examples/website/terrain/app.jsx b/examples/website/terrain/app.tsx similarity index 77% rename from examples/website/terrain/app.jsx rename to examples/website/terrain/app.tsx index 064e600a2cd..bfa2e811a50 100644 --- a/examples/website/terrain/app.jsx +++ b/examples/website/terrain/app.tsx @@ -2,12 +2,13 @@ import React from 'react'; import {createRoot} from 'react-dom/client'; import DeckGL from '@deck.gl/react'; -import {TerrainLayer} from '@deck.gl/geo-layers'; +import {TerrainLayer, TerrainLayerProps} from '@deck.gl/geo-layers'; +import type {MapViewState} from '@deck.gl/core'; // Set your mapbox token here const MAPBOX_TOKEN = process.env.MapboxAccessToken; // eslint-disable-line -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { latitude: 46.24, longitude: -122.18, zoom: 11.5, @@ -21,7 +22,7 @@ const SURFACE_IMAGE = `https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}@2x // https://docs.mapbox.com/help/troubleshooting/access-elevation-data/#mapbox-terrain-rgb // Note - the elevation rendered by this example is greatly exagerated! -const ELEVATION_DECODER = { +const ELEVATION_DECODER: TerrainLayerProps['elevationDecoder'] = { rScaler: 6553.6, gScaler: 25.6, bScaler: 0.1, @@ -32,6 +33,10 @@ export default function App({ texture = SURFACE_IMAGE, wireframe = false, initialViewState = INITIAL_VIEW_STATE +}: { + texture?: string; + wireframe?: boolean; + initialViewState?: MapViewState; }) { const layer = new TerrainLayer({ id: 'terrain', @@ -48,6 +53,6 @@ export default function App({ return ; } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/terrain/index.html b/examples/website/terrain/index.html index 21e34f8ec04..d91d4471749 100644 --- a/examples/website/terrain/index.html +++ b/examples/website/terrain/index.html @@ -12,7 +12,7 @@
diff --git a/examples/website/terrain/package.json b/examples/website/terrain/package.json index 53768c80249..061cdf70801 100644 --- a/examples/website/terrain/package.json +++ b/examples/website/terrain/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/examples/website/terrain/tsconfig.json b/examples/website/terrain/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/terrain/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/text/app.jsx b/examples/website/text/app.tsx similarity index 70% rename from examples/website/text/app.jsx rename to examples/website/text/app.tsx index deb8f7e5e8e..5be93024b7f 100644 --- a/examples/website/text/app.jsx +++ b/examples/website/text/app.tsx @@ -7,16 +7,18 @@ import DeckGL from '@deck.gl/react'; import {MapView} from '@deck.gl/core'; import {TextLayer} from '@deck.gl/layers'; import {CollisionFilterExtension} from '@deck.gl/extensions'; -import {scaleLinear} from 'd3-scale'; - +import {scaleLog} from 'd3-scale'; import {CSVLoader} from '@loaders.gl/csv'; import {load} from '@loaders.gl/core'; +import type {Color, MapViewState} from '@deck.gl/core'; +import type {CollisionFilterExtensionProps} from '@deck.gl/extensions'; + // Sample data const DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/text-layer/cities-1000.csv'; -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { latitude: 39.1, longitude: -94.57, zoom: 3.8, @@ -27,8 +29,15 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; -const colorScale = scaleLinear() - .domain([3, 4, 5, 6, 7]) +type City = { + name: string; + population: number; + longitude: number; + latitude: number; +}; + +const colorScale = scaleLog() + .domain([1e3, 1e4, 1e5, 1e6, 1e7]) .range([ [29, 145, 192], [65, 182, 196], @@ -37,8 +46,18 @@ const colorScale = scaleLinear() [237, 248, 177] ]); -export default function App({data, noOverlap = true, fontSize = 32, mapStyle = MAP_STYLE}) { - const [zoom, setZoom] = useState(INITIAL_VIEW_STATE.zoom); +export default function App({ + data, + noOverlap = true, + fontSize = 32, + mapStyle = MAP_STYLE +}: { + data?: City[]; + noOverlap?: boolean; + fontSize?: number; + mapStyle?: string; +}) { + const [zoom, setZoom] = useState(INITIAL_VIEW_STATE.zoom); const onViewStateChange = useCallback(({viewState}) => { setZoom(viewState.zoom); @@ -48,7 +67,7 @@ export default function App({data, noOverlap = true, fontSize = 32, mapStyle = M const sizeMaxPixels = (scale / 3) * fontSize; const sizeMinPixels = Math.min(scale / 1000, 0.5) * fontSize; - const textLayer = new TextLayer({ + const textLayer = new TextLayer>({ id: 'world-cities', data, characterSet: 'auto', @@ -59,7 +78,7 @@ export default function App({data, noOverlap = true, fontSize = 32, mapStyle = M // TextLayer options getText: d => d.name, getPosition: d => [d.longitude, d.latitude], - getColor: d => colorScale(Math.log10(d.population)), + getColor: d => colorScale(d.population), getSize: d => Math.pow(d.population, 0.25) / 40, sizeScale: fontSize, sizeMaxPixels, @@ -90,11 +109,10 @@ export default function App({data, noOverlap = true, fontSize = 32, mapStyle = M ); } -export function renderToDOM(container) { +export async function renderToDOM(container: HTMLDivElement) { const root = createRoot(container); root.render(); - load(DATA_URL, CSVLoader).then(data => { - root.render(); - }); + const cities = (await load(DATA_URL, CSVLoader)).data; + root.render(); } diff --git a/examples/website/text/index.html b/examples/website/text/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/text/index.html +++ b/examples/website/text/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/text/package.json b/examples/website/text/package.json index 3776842e313..8a5e39fbf95 100644 --- a/examples/website/text/package.json +++ b/examples/website/text/package.json @@ -10,7 +10,10 @@ }, "dependencies": { "@loaders.gl/csv": "^4.1.4", - "d3-scale": "^2.0.0", + "@types/d3-scale": "^4.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "d3-scale": "^4.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", "react": "^18.0.0", diff --git a/examples/website/text/tsconfig.json b/examples/website/text/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/text/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/trips/app.jsx b/examples/website/trips/app.tsx similarity index 69% rename from examples/website/trips/app.jsx rename to examples/website/trips/app.tsx index 931e4a71b85..92491032808 100644 --- a/examples/website/trips/app.jsx +++ b/examples/website/trips/app.tsx @@ -6,6 +6,9 @@ import {AmbientLight, PointLight, LightingEffect} from '@deck.gl/core'; import DeckGL from '@deck.gl/react'; import {PolygonLayer} from '@deck.gl/layers'; import {TripsLayer} from '@deck.gl/geo-layers'; +import {animate} from 'popmotion'; + +import type {Position, Color, Material, MapViewState} from '@deck.gl/core'; // Source data CSV const DATA_URL = { @@ -27,22 +30,28 @@ const pointLight = new PointLight({ const lightingEffect = new LightingEffect({ambientLight, pointLight}); -const material = { - ambient: 0.1, - diffuse: 0.6, - shininess: 32, - specularColor: [60, 64, 70] +type Theme = { + buildingColor: Color; + trailColor0: Color; + trailColor1: Color; + material: Material; + effects: [LightingEffect] }; -const DEFAULT_THEME = { +const DEFAULT_THEME: Theme = { buildingColor: [74, 80, 87], trailColor0: [253, 128, 93], trailColor1: [23, 184, 190], - material, + material: { + ambient: 0.1, + diffuse: 0.6, + shininess: 32, + specularColor: [60, 64, 70] + }, effects: [lightingEffect] }; -const INITIAL_VIEW_STATE = { +const INITIAL_VIEW_STATE: MapViewState = { longitude: -74, latitude: 40.72, zoom: 13, @@ -52,7 +61,7 @@ const INITIAL_VIEW_STATE = { const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json'; -const landCover = [ +const landCover: Position[][] = [ [ [-74.0, 40.7], [-74.02, 40.7], @@ -61,6 +70,17 @@ const landCover = [ ] ]; +type Building = { + polygon: Position[]; + height: number; +}; + +type Trip = { + vendor: number; + path: Position[]; + timestamps: number[]; +}; + export default function App({ buildings = DATA_URL.BUILDINGS, trips = DATA_URL.TRIPS, @@ -70,30 +90,39 @@ export default function App({ theme = DEFAULT_THEME, loopLength = 1800, // unit corresponds to the timestamp in source data animationSpeed = 1 +}: { + buildings?: string | Building[]; + trips?: string | Trip[]; + trailLength?: number; + loopLength?: number; + animationSpeed?: number; + initialViewState?: MapViewState; + mapStyle?: string; + theme?: Theme; }) { const [time, setTime] = useState(0); - const [animation] = useState({}); - - const animate = () => { - setTime(t => (t + animationSpeed) % loopLength); - animation.id = window.requestAnimationFrame(animate); - }; useEffect(() => { - animation.id = window.requestAnimationFrame(animate); - return () => window.cancelAnimationFrame(animation.id); - }, [animation]); + const animation = animate({ + from: 0, + to: loopLength, + duration: loopLength * 60 / animationSpeed, + repeat: Infinity, + onUpdate: setTime + }); + return () => animation.stop(); + }, [loopLength, animationSpeed]); const layers = [ // This is only needed when using shadow effects - new PolygonLayer({ + new PolygonLayer({ id: 'ground', data: landCover, getPolygon: f => f, stroked: false, getFillColor: [0, 0, 0, 0] }), - new TripsLayer({ + new TripsLayer({ id: 'trips', data: trips, getPath: d => d.path, @@ -107,7 +136,7 @@ export default function App({ shadowEnabled: false }), - new PolygonLayer({ + new PolygonLayer({ id: 'buildings', data: buildings, extruded: true, @@ -132,6 +161,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/trips/index.html b/examples/website/trips/index.html index 16f037f851e..8f3eea6882d 100644 --- a/examples/website/trips/index.html +++ b/examples/website/trips/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/trips/package.json b/examples/website/trips/package.json index f51a3df0947..68551d82da4 100644 --- a/examples/website/trips/package.json +++ b/examples/website/trips/package.json @@ -9,8 +9,11 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "maplibre-gl": "^3.0.0", + "popmotion": "^11.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", "react-map-gl": "^7.1.0" diff --git a/examples/website/trips/tsconfig.json b/examples/website/trips/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/trips/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/examples/website/wms/app.jsx b/examples/website/wms/app.tsx similarity index 67% rename from examples/website/wms/app.jsx rename to examples/website/wms/app.tsx index c3d7e6cddc8..4a1c3f29130 100644 --- a/examples/website/wms/app.jsx +++ b/examples/website/wms/app.tsx @@ -3,7 +3,11 @@ import {createRoot} from 'react-dom/client'; import DeckGL from '@deck.gl/react'; import {_WMSLayer as WMSLayer} from '@deck.gl/geo-layers'; -const INITIAL_VIEW_STATE = { +import type {MapViewState} from '@deck.gl/core'; +import type {BitmapLayerPickingInfo} from '@deck.gl/layers'; +import type {ImageSourceMetadata} from '@loaders.gl/loader-utils'; + +const INITIAL_VIEW_STATE: MapViewState = { longitude: -122.4, latitude: 37.74, zoom: 9, @@ -35,8 +39,18 @@ export default function App({ initialViewState = INITIAL_VIEW_STATE, onMetadataLoad = console.log, // eslint-disable-line onMetadataLoadError = console.error // eslint-disable-line +}: { + serviceUrl?: string; + layers?: string[]; + initialViewState?: MapViewState; + onMetadataLoad?: (metadata: ImageSourceMetadata) => void; + onMetadataLoadError?: (error: Error) => void; }) { - const [selection, setSelection] = useState(null); + const [selection, setSelection] = useState<{ + x: number; + y: number; + featureInfo: string; + } | null>(null); const layer = new WMSLayer({ data: serviceUrl, @@ -47,12 +61,13 @@ export default function App({ onMetadataLoad, onMetadataLoadError, - onClick: async ({bitmap}) => { + onClick: ({bitmap}: BitmapLayerPickingInfo) => { if (bitmap) { const x = bitmap.pixel[0]; const y = bitmap.pixel[1]; - const featureInfo = await layer.getFeatureInfoText(x, y); - setSelection({x, y, featureInfo}); + layer.getFeatureInfoText(x, y).then(featureInfo => { + setSelection({x, y, featureInfo}); + }); } } }); @@ -73,6 +88,6 @@ export default function App({ ); } -export function renderToDOM(container) { +export function renderToDOM(container: HTMLDivElement) { createRoot(container).render(); } diff --git a/examples/website/wms/examples.js b/examples/website/wms/examples.js deleted file mode 100644 index 2551c384e60..00000000000 --- a/examples/website/wms/examples.js +++ /dev/null @@ -1,35 +0,0 @@ -export const LOADERS_URI = 'https://raw.githubusercontent.com/visgl/loaders.gl/master'; - -export const INITIAL_CATEGORY_NAME = 'WMS'; -export const INITIAL_EXAMPLE_NAME = 'Terrestris(OpenStreetMap)'; - -export const INITIAL_MAP_STYLE = - 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json'; - -const VIEW_STATE = { - longitude: -122.4, - latitude: 37.74, - zoom: 9, - minZoom: 1, - maxZoom: 20, - pitch: 0, - bearing: 0 -}; - -export const EXAMPLES = { - WMS: { - 'Terrestris(OpenStreetMap)': { - service: `https://ows.terrestris.de/osm/service`, - serviceType: 'wms', - layers: ['OSM-WMS'], - viewState: {...VIEW_STATE} - }, - 'Canadian Weather': { - service: 'https://geo.weather.gc.ca/geomet', - serviceType: 'wms', - layers: ['GDPS.ETA_TT'], // 'RDPS.CONV_KINDEX.PT3H'], - viewState: {...VIEW_STATE, longitude: -100, latitude: 55, zoom: 3}, - opacity: 0.5 - } - } -}; diff --git a/examples/website/wms/index.html b/examples/website/wms/index.html index c499fc77d85..7c3a7f0f82b 100644 --- a/examples/website/wms/index.html +++ b/examples/website/wms/index.html @@ -13,7 +13,7 @@
diff --git a/examples/website/wms/package.json b/examples/website/wms/package.json index 32e0bf38cdc..6505f258f12 100644 --- a/examples/website/wms/package.json +++ b/examples/website/wms/package.json @@ -9,6 +9,8 @@ "build": "vite build" }, "dependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "deck.gl": "^9.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/examples/website/wms/tsconfig.json b/examples/website/wms/tsconfig.json new file mode 100644 index 00000000000..9b3c020493c --- /dev/null +++ b/examples/website/wms/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + } +} diff --git a/website/package.json b/website/package.json index d153ea354b6..007d91bae93 100644 --- a/website/package.json +++ b/website/package.json @@ -27,16 +27,17 @@ "d3-color": "^3.1.0", "d3-hierarchy": "^2.0.0", "d3-request": "^1.0.6", - "d3-scale": "^3.2.1", + "d3-scale": "^4.0.0", "expr-eval": "^2.0.2", "mapbox-gl": "^3.0.0", "maplibre-gl": "^3.0.0", + "popmotion": "^11.0.0", "react": "^18.0.0", "react-ace": "^6.1.4", "react-dom": "^18.0.0", "react-map-gl": "^7.1.0", - "react-virtualized-auto-sizer": "^1.0.2", - "styled-components": "^5.3.3" + "styled-components": "^5.3.3", + "supercluster": "^8.0.1" }, "devDependencies": { "@docusaurus/core": "^2.2.0", diff --git a/website/src/examples/brushing-extension.js b/website/src/examples/brushing-extension.js index db1630c9880..f2b3a01277f 100644 --- a/website/src/examples/brushing-extension.js +++ b/website/src/examples/brushing-extension.js @@ -1,14 +1,11 @@ import React, {Component} from 'react'; import {MAPBOX_STYLES, DATA_URI, GITHUB_TREE} from '../constants/defaults'; import {readableInteger} from '../utils/format-utils'; -import App, {inFlowColors, outFlowColors} from 'website-examples/brushing/app'; +import App, {inFlowColor, outFlowColor} from 'website-examples/brushing/app'; import {makeExample} from '../components'; -const colorRamp = inFlowColors - .slice() - .reverse() - .concat(outFlowColors) +const colorRamp = [inFlowColor, outFlowColor] .map(color => `rgb(${color.join(',')})`); class BrushingDemo extends Component { diff --git a/website/src/examples/line-layer.js b/website/src/examples/line-layer.js index b471de9c8f7..2e09cfd9603 100644 --- a/website/src/examples/line-layer.js +++ b/website/src/examples/line-layer.js @@ -60,7 +60,7 @@ class LineDemo extends Component { {...otherProps} flightPaths={data && data[0]} airports={data && data[1]} - getWidth={params.width.value} + lineWidth={params.width.value} /> ); } diff --git a/website/src/examples/point-cloud-layer.js b/website/src/examples/point-cloud-layer.js index ee7879d1bac..9db68bfe0c9 100644 --- a/website/src/examples/point-cloud-layer.js +++ b/website/src/examples/point-cloud-layer.js @@ -30,7 +30,9 @@ class PointCloudDemo extends Component { }; render() { - return ; + return
+ +
; } } diff --git a/website/yarn.lock b/website/yarn.lock index 3cae741b672..a5598c63085 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -5338,24 +5338,19 @@ d3-array@1: resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== -d3-array@^2.3.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.11.0.tgz#5ed6a2869bc7d471aec8df9ff6ed9fef798facc4" - integrity sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw== +"d3-array@2 - 3", "d3-array@2.10.0 - 3": + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== dependencies: - internmap "^1.0.0" + internmap "1 - 2" d3-collection@1: version "1.0.7" resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== -"d3-color@1 - 2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" - integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== - -d3-color@^3.1.0: +"d3-color@1 - 3", d3-color@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== @@ -5374,10 +5369,10 @@ d3-dsv@1: iconv-lite "0.4" rw "1" -"d3-format@1 - 2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767" - integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA== +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== d3-geo@1.7.1: version "1.7.1" @@ -5391,12 +5386,12 @@ d3-hierarchy@^2.0.0: resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz#dab88a58ca3e7a1bc6cab390e89667fcc6d20218" integrity sha512-SwIdqM3HxQX2214EG9GTjgmCc/mbSx4mQBn+DuEETubhOw6/U3fmnji4uCVrmzOydMHSO1nZle5gh6HB/wdOzw== -"d3-interpolate@1.2.0 - 2": - version "2.0.1" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" - integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== +"d3-interpolate@1.2.0 - 3": + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== dependencies: - d3-color "1 - 2" + d3-color "1 - 3" d3-request@^1.0.6: version "1.0.6" @@ -5408,28 +5403,30 @@ d3-request@^1.0.6: d3-dsv "1" xmlhttprequest "1" -d3-scale@^3.2.1: - version "3.2.3" - resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.2.3.tgz#be380f57f1f61d4ff2e6cbb65a40593a51649cfd" - integrity sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g== +d3-scale@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== dependencies: - d3-array "^2.3.0" - d3-format "1 - 2" - d3-interpolate "1.2.0 - 2" - d3-time "1 - 2" - d3-time-format "2 - 3" + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" -"d3-time-format@2 - 3": - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" - integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== dependencies: - d3-time "1 - 2" + d3-time "1 - 3" -"d3-time@1 - 2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.0.0.tgz#ad7c127d17c67bd57a4c61f3eaecb81108b1e0ab" - integrity sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q== +"d3-time@1 - 3", "d3-time@2.1.1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" d3-voronoi@1.1.2: version "1.1.2" @@ -6149,6 +6146,13 @@ fraction.js@^4.2.0: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== +framesync@6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.1.2.tgz#755eff2fb5b8f3b4d2b266dd18121b300aefea27" + integrity sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g== + dependencies: + tslib "2.4.0" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -6534,6 +6538,11 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -6793,10 +6802,10 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== -internmap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.0.tgz#3c6bf0944b0eae457698000412108752bbfddb56" - integrity sha512-SdoDWwNOTE2n4JWUsLn4KXZGuZPjPF9yyOGc8bnfWnBQh7BD/l80rzSznKc/r4Y0aQ7z3RTk9X+tV4tHBpu+dA== +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== interpret@^1.0.0: version "1.4.0" @@ -8155,6 +8164,16 @@ polygon-clipping@^0.15.3: dependencies: splaytree "^3.1.0" +popmotion@^11.0.0: + version "11.0.5" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.5.tgz#8e3e014421a0ffa30ecd722564fd2558954e1f7d" + integrity sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA== + dependencies: + framesync "6.1.2" + hey-listen "^1.0.8" + style-value-types "5.1.2" + tslib "2.4.0" + popper.js@1.16.1-lts: version "1.16.1-lts" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1-lts.tgz#cf6847b807da3799d80ee3d6d2f90df8a3f50b05" @@ -8827,11 +8846,6 @@ react-transition-group@^4.4.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react-virtualized-auto-sizer@^1.0.2: - version "1.0.24" - resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz#3ebdc92f4b05ad65693b3cc8e7d8dd54924c0227" - integrity sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg== - react@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -9715,6 +9729,14 @@ style-to-object@0.3.0, style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" +style-value-types@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.1.2.tgz#6be66b237bd546048a764883528072ed95713b62" + integrity sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q== + dependencies: + hey-listen "^1.0.8" + tslib "2.4.0" + styled-components@^5.3.3: version "5.3.6" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.6.tgz#27753c8c27c650bee9358e343fc927966bfd00d1" @@ -9934,6 +9956,11 @@ ts-node@~10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"