diff --git a/examples/common/package.json b/examples/common/package.json deleted file mode 100644 index c4dc645..0000000 --- a/examples/common/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@flowmap.gl/examples-common", - "version": "8.0.0-alpha.25", - "private": true, - "main": "src/index.js", - "types": "src/index.d.ts", - "module": "src/index.js", - "license": "MIT", - "dependencies": { - "@flowmap.gl/data": "^8.0.0-alpha.25", - "@flowmap.gl/layers": "^8.0.0-alpha.25", - "d3-fetch": "^3.0.1", - "h3-js": "^3.7.2", - "lil-gui": "^0.16.1" - }, - "peerDependencies": { - "react": "^17.0.2" - } -} diff --git a/examples/common/src/fetchData.js b/examples/common/src/fetchData.js deleted file mode 100644 index 4f278f3..0000000 --- a/examples/common/src/fetchData.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import {csv} from 'd3-fetch'; -import {getClusterLevelsH3} from './h3-clustering'; - -const base = 'https://gist.githubusercontent.com/ilyabo/'; -const path = - // Migrations in Switzerland - // `${base}/a7b9701424257146b571149d92a14926/raw/2e9e1e9bcf64cf0090781b451037229ccb78e1b1`; - // BIXI rides - `${base}/68d3dba61d86164b940ffe60e9d36931/raw/a72938b5d51b6df9fa7bba9aa1fb7df00cd0f06a`; - -let cachedData; - -export default async function fetchData(clusterMethod = 'HCA') { - if (!cachedData) { - cachedData = await Promise.all([ - csv(`${path}/locations.csv`, (row, i) => ({ - id: row.id, - name: row.name, - lat: Number(row.lat), - lon: Number(row.lon), - })), - csv(`${path}/flows.csv`, (row) => ({ - origin: row.origin, - dest: row.dest, - count: Number(row.count), - })), - ]).then(([locations, flows]) => ({locations, flows})); - } - return { - ...cachedData, - ...(clusterMethod === 'H3' - ? {clusterLevels: getClusterLevelsH3(cachedData.locations)} - : null), - }; -} diff --git a/examples/common/src/h3-clustering.js b/examples/common/src/h3-clustering.js deleted file mode 100644 index f05a121..0000000 --- a/examples/common/src/h3-clustering.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import {geoToH3, h3ToGeo} from 'h3-js'; - -export function getClusterLevelsH3(locations, minZoom = 1, maxZoom = 20) { - let nodes = locations.map((d) => ({ - id: d.id, - zoom: maxZoom, - lat: +d.lat, - lon: +d.lon, - })); - - const result = []; - let rawZoom = null; - for (let zoom = maxZoom - 1; zoom >= minZoom; zoom--) { - const h3Zoom = zoom - 4; - const nodesByH3 = nodes.reduce((acc, d) => { - const h3Id = geoToH3(+d.lat, +d.lon, h3Zoom); - if (!acc[h3Id]) { - acc[h3Id] = []; - } - acc[h3Id].push(d); - return acc; - }, {}); - - const keys = Object.keys(nodesByH3); - if (keys.length < locations.length) { - if (rawZoom === null) { - rawZoom = zoom + 1; - } - nodes = keys.map((id) => { - if (nodesByH3[id].length === 1) { - const node = nodesByH3[id][0]; - return { - id: `{[${node.id}:${zoom}]}`, - zoom, - lat: node.lat, - lon: node.lon, - children: [node.id], - }; - } - return { - id: `{[${id}:${zoom}]}`, - zoom, - lat: h3ToGeo(id, true)[0], - lon: h3ToGeo(id, true)[1], - children: nodesByH3[id].map((d) => d.id), - }; - }); - - result.unshift({ - zoom, - nodes, - }); - } - - if (keys.length <= 1) { - break; - } - } - - result.push({ - zoom: rawZoom ?? maxZoom, - nodes: locations, - }); - - return result; -} diff --git a/examples/common/src/index.d.ts b/examples/common/src/index.d.ts deleted file mode 100644 index c5a61ab..0000000 --- a/examples/common/src/index.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import type {GUI} from 'lil-gui'; -import {ClusterLevels} from '@flowmap.gl/data'; -export type LocationDatum = { - id: string; - name: string; - lon: number; - lat: number; -}; -export type FlowDatum = { - origin: string; - dest: string; - count: number; -}; -export type LoadedData = { - locations: LocationDatum[]; - flows: FlowDatum[]; -}; - -export function getClusterLevelsH3( - locations: LocationDatum[], - minZoom?: number, - maxZoom?: number, -): ClusterLevels; -export function fetchData(clusteringMethod?: string): Promise; - -export const UI_INITIAL: Record; -export const initLilGui: (gui: GUI) => void; - -export function useUI>( - initialState: T, - initUi: (gui: GUI) => void, -); diff --git a/examples/common/src/index.js b/examples/common/src/index.js deleted file mode 100644 index c6596a7..0000000 --- a/examples/common/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -export {default as fetchData} from './fetchData'; -export {default as useUI} from './useUI'; -export * from './h3-clustering'; -export * from './ui-config'; diff --git a/examples/common/src/ui-config.js b/examples/common/src/ui-config.js deleted file mode 100644 index cad4461..0000000 --- a/examples/common/src/ui-config.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import {FlowmapLayer} from '@flowmap.gl/layers'; -import {COLOR_SCHEMES} from '@flowmap.gl/data'; - -export const UI_INITIAL = { - darkMode: true, - colorScheme: 'Teal', - highlightColor: '#ff9b29', - opacity: 1.0, - fadeEnabled: FlowmapLayer.defaultProps.fadeEnabled, - fadeOpacityEnabled: FlowmapLayer.defaultProps.fadeOpacityEnabled, - fadeAmount: FlowmapLayer.defaultProps.fadeAmount, - clusteringEnabled: FlowmapLayer.defaultProps.clusteringEnabled, - clusteringAuto: FlowmapLayer.defaultProps.clusteringAuto, - clusteringLevel: 5, - clusteringMethod: 'HCA', - animationEnabled: FlowmapLayer.defaultProps.animationEnabled, - adaptiveScalesEnabled: FlowmapLayer.defaultProps.adaptiveScalesEnabled, - locationTotalsEnabled: FlowmapLayer.defaultProps.locationTotalsEnabled, - locationLabelsEnabled: FlowmapLayer.defaultProps.locationLabelsEnabled, - maxTopFlowsDisplayNum: FlowmapLayer.defaultProps.maxTopFlowsDisplayNum, -}; - -export const initLilGui = (gui) => { - gui.add(UI_INITIAL, 'darkMode'); - gui.add(UI_INITIAL, 'colorScheme', Object.keys(COLOR_SCHEMES)); - gui.addColor(UI_INITIAL, 'highlightColor'); - gui.add(UI_INITIAL, 'animationEnabled'); - gui.add(UI_INITIAL, 'adaptiveScalesEnabled'); - gui.add(UI_INITIAL, 'locationTotalsEnabled'); - gui.add(UI_INITIAL, 'locationLabelsEnabled'); - - gui - .add(UI_INITIAL, 'maxTopFlowsDisplayNum') - .min(0) - .max(10000) - .step(10) - .enable(FlowmapLayer.defaultProps.maxTopFlowsDisplayNum); - - gui.add(UI_INITIAL, 'opacity', 0.0, 1.0); - - const fading = gui.addFolder('Fade'); - const fadeEnabled = fading.add(UI_INITIAL, 'fadeEnabled'); - const fadeOpacityEnabled = fading - .add(UI_INITIAL, 'fadeOpacityEnabled') - .enable(FlowmapLayer.defaultProps.fadeEnabled); - const fadeAmount = fading - .add(UI_INITIAL, 'fadeAmount') - .min(0) - .max(100) - .enable(FlowmapLayer.defaultProps.fadeEnabled); - fadeEnabled.onChange((v) => { - fadeAmount.enable(v); - fadeOpacityEnabled.enable(v); - }); - const clustering = gui.addFolder('Clustering'); - const clusteringEnabled = clustering.add(UI_INITIAL, 'clusteringEnabled'); - const clusteringMethod = clustering - .add(UI_INITIAL, 'clusteringMethod', ['HCA', 'H3']) - .enable(FlowmapLayer.defaultProps.clusteringEnabled); - - const clusteringAuto = clustering.add(UI_INITIAL, 'clusteringAuto'); - const clusteringLevel = clustering - .add(UI_INITIAL, 'clusteringLevel') - .min(0) - .max(20) - .step(1) - .enable(!FlowmapLayer.defaultProps.clusteringAuto); - clusteringEnabled.onChange((v) => { - clusteringAuto.enable(v); - clusteringMethod.enable(v); - clusteringLevel.enable(v && !clusteringEnabled.object.clusteringAuto); - }); - clusteringAuto - .enable(FlowmapLayer.defaultProps.clusteringEnabled) - .onChange((v) => { - clusteringLevel.enable(!v); - }); -}; diff --git a/examples/common/src/useUI.js b/examples/common/src/useUI.js deleted file mode 100644 index 26ec2ac..0000000 --- a/examples/common/src/useUI.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import {useEffect, useRef, useState} from 'react'; -import GUI from 'lil-gui'; - -export default function useUI(initialState, initUi) { - const [state, setState] = useState(initialState); - const lilGuiRef = useRef(); - useEffect(() => { - const gui = new GUI(); - initUi(gui); - - const controllers = gui.controllersRecursive(); - for (const key in initialState) { - if (initialState[key]) { - if (controllers.find((c) => c._name === key)) continue; - const args = [initialState, key]; - // @ts-ignore - gui.add.apply(gui, args); - } - } - - gui.onChange((event) => { - setState((state) => ({ - ...state, - [event.property]: event.value, - })); - }); - - lilGuiRef.current = gui; - return () => { - gui.destroy(); - }; - }, [initialState, initUi]); - - return state; -} diff --git a/examples/react-app/.env.example b/examples/react-app/.env.example deleted file mode 100644 index 37231e1..0000000 --- a/examples/react-app/.env.example +++ /dev/null @@ -1 +0,0 @@ -REACT_APP_MAPBOX_ACCESS_TOKEN=... diff --git a/examples/react-app/.gitignore b/examples/react-app/.gitignore deleted file mode 100644 index 4d29575..0000000 --- a/examples/react-app/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/examples/react-app/README.md b/examples/react-app/README.md deleted file mode 100644 index 58dcbe7..0000000 --- a/examples/react-app/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Flowmap.gl example with Create React App - -This example shows how Flowmap.gl can be used in a React web app. - - -## Usage - -To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). -Rename `.env.example` into `.env` and add your Mapbox token there. - - -```bash -npm i -npm run start -``` - -To build a production version: - -```bash -npm run build -``` diff --git a/examples/react-app/package.json b/examples/react-app/package.json deleted file mode 100644 index 983f9ea..0000000 --- a/examples/react-app/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@flowmap.gl/react-app", - "version": "8.0.0-alpha.25", - "homepage": "https://flowmapblue.github.io/flowmap.gl/", - "private": true, - "dependencies": { - "@deck.gl/core": "^8.6.4", - "@deck.gl/layers": "^8.6.4", - "@deck.gl/react": "^8.6.4", - "@flowmap.gl/data": "^8.0.0-alpha.25", - "@flowmap.gl/layers": "^8.0.0-alpha.25", - "@luma.gl/constants": "^8.1.0", - "@luma.gl/core": "^8.1.0", - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^13.2.0", - "@testing-library/user-event": "^14.2.0", - "@types/jest": "^27.5.1", - "@types/node": "^17.0.35", - "@types/react": "^18.0.9", - "@types/react-dom": "^18.0.4", - "d3-fetch": "^3.0.1", - "lil-gui": "^0.16.1", - "mapbox-gl": "^2.6.1", - "react": "^18.1.0", - "react-dom": "^18.1.0", - "react-map-gl": "^7.0.13", - "react-scripts": "^5.0.0-next.58", - "typescript": "^4.1.2", - "web-vitals": "^2.1.4" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - "defaults", - "not ie 11" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "@types/d3-fetch": "^3.0.1", - "eslint-config-react-app": "^7.0.0-next.102" - } -} diff --git a/examples/react-app/public/index.html b/examples/react-app/public/index.html deleted file mode 100644 index 055d38c..0000000 --- a/examples/react-app/public/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - Flowmap.gl example - - - -
- - diff --git a/examples/react-app/src/App.tsx b/examples/react-app/src/App.tsx deleted file mode 100644 index ee0fd24..0000000 --- a/examples/react-app/src/App.tsx +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as React from 'react'; -import {ReactNode, useEffect, useState} from 'react'; -import DeckGL from '@deck.gl/react'; -import { - FlowmapLayer, - FlowmapLayerPickingInfo, - PickingType, -} from '@flowmap.gl/layers'; -import {FlowmapData, getViewStateForLocations} from '@flowmap.gl/data'; -import {Map as ReactMapGl, ViewState as ViewportProps} from 'react-map-gl'; -import { - fetchData, - FlowDatum, - initLilGui, - LocationDatum, - UI_INITIAL, - useUI, -} from '@flowmap.gl/examples-common'; -import 'mapbox-gl/dist/mapbox-gl.css'; - -const MAPBOX_ACCESS_TOKEN = process.env.REACT_APP_MAPBOX_ACCESS_TOKEN; -const MAPBOX_STYLE_LIGHT = 'mapbox://styles/mapbox/streets-v11'; -const MAPBOX_STYLE_DARK = MAPBOX_STYLE_LIGHT; - -type TooltipState = { - position: {left: number; top: number}; - content: ReactNode; -}; - -function App() { - const config = useUI(UI_INITIAL, initLilGui); - const [viewState, setViewState] = useState(); - const [data, setData] = useState>(); - const [tooltip, setTooltip] = useState(); - useEffect(() => { - (async () => { - setData(await fetchData(config.clusteringMethod)); - })(); - }, [config.clusteringMethod]); - - useEffect(() => { - if (!viewState && data?.locations) { - const [width, height] = [globalThis.innerWidth, globalThis.innerHeight]; - const viewState = getViewStateForLocations( - data.locations, - (loc: LocationDatum) => [loc.lon, loc.lat], - [width, height], - ); - setViewState({ - ...viewState, - latitude: viewState.latitude - 0.02, - zoom: viewState.zoom + 1, - // @ts-ignore - width, - height, - }); - } - }, [data]); - const handleViewStateChange = ({viewState}: any) => { - setViewState(viewState); - setTooltip(undefined); - }; - const layers = []; - if (data) { - layers.push( - new FlowmapLayer({ - id: 'my-flowmap-layer', - data, - opacity: config.opacity, - pickable: true, - darkMode: config.darkMode, - colorScheme: config.colorScheme, - fadeAmount: config.fadeAmount, - fadeEnabled: config.fadeEnabled, - fadeOpacityEnabled: config.fadeOpacityEnabled, - locationTotalsEnabled: config.locationTotalsEnabled, - locationLabelsEnabled: config.locationLabelsEnabled, - animationEnabled: config.animationEnabled, - clusteringEnabled: config.clusteringEnabled, - clusteringAuto: config.clusteringAuto, - clusteringLevel: config.clusteringLevel, - adaptiveScalesEnabled: config.adaptiveScalesEnabled, - highlightColor: config.highlightColor, - maxTopFlowsDisplayNum: config.maxTopFlowsDisplayNum, - getLocationId: (loc) => loc.id, - getLocationLat: (loc) => loc.lat, - getLocationLon: (loc) => loc.lon, - getFlowOriginId: (flow) => flow.origin, - getLocationName: (loc) => loc.name, - getFlowDestId: (flow) => flow.dest, - getFlowMagnitude: (flow) => flow.count, - onHover: (info) => setTooltip(getTooltipState(info)), - onClick: (info) => - console.log('clicked', info.object?.type, info.object, info), - }), - ); - } - if (!viewState) { - return null; - } - return ( -
- - - - {tooltip && ( -
- {tooltip.content} -
- )} -
- ); -} - -function getTooltipState( - info: FlowmapLayerPickingInfo | undefined, -): TooltipState | undefined { - if (!info) return undefined; - const {x, y, object} = info; - const position = {left: x, top: y}; - switch (object?.type) { - case PickingType.LOCATION: - return { - position, - content: ( - <> -
{object.name}
-
Incoming trips: {object.totals.incomingCount}
-
Outgoing trips: {object.totals.outgoingCount}
-
Internal or round trips: {object.totals.internalCount}
- - ), - }; - case PickingType.FLOW: - return { - position, - content: ( - <> -
- {object.origin.id} → {object.dest.id} -
-
{object.count}
- - ), - }; - } - return undefined; -} - -export default App; diff --git a/examples/react-app/src/index.css b/examples/react-app/src/index.css deleted file mode 100644 index 560aa22..0000000 --- a/examples/react-app/src/index.css +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} - -.flowmap-container { - width: 100vw; - height: 100vh; -} -.light .mapboxgl-map { - filter: grayscale(0.85); - opacity: 0.5; -} -.dark { - background: #000; -} -.dark .mapboxgl-map { - filter: grayscale(0.1) invert(1) hue-rotate(-180deg) saturate(0.5) - contrast(0.9); - opacity: 0.3; -} - -.tooltip { - position: absolute; - font-size: 10px; - border-radius: 5px; - background-color: rgba(150, 150, 150, 0.75); - padding: 1em; - color: white; - pointer-events: none; -} diff --git a/examples/react-app/src/index.tsx b/examples/react-app/src/index.tsx deleted file mode 100644 index 9cccc05..0000000 --- a/examples/react-app/src/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import './index.css'; -import App from './App'; -import {createRoot} from 'react-dom/client'; - -const container = document.getElementById('root'); -const root = createRoot(container!); -root.render( - - - , -); diff --git a/examples/react-app/src/react-app-env.d.ts b/examples/react-app/src/react-app-env.d.ts deleted file mode 100644 index 5da4f7c..0000000 --- a/examples/react-app/src/react-app-env.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -/// -declare module '@deck.gl/react'; diff --git a/examples/react-app/tsconfig.json b/examples/react-app/tsconfig.json deleted file mode 100644 index a273b0c..0000000 --- a/examples/react-app/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": [ - "src" - ] -} diff --git a/examples/react-worker-app/.env.example b/examples/react-worker-app/.env.example deleted file mode 100644 index 37231e1..0000000 --- a/examples/react-worker-app/.env.example +++ /dev/null @@ -1 +0,0 @@ -REACT_APP_MAPBOX_ACCESS_TOKEN=... diff --git a/examples/react-worker-app/.gitignore b/examples/react-worker-app/.gitignore deleted file mode 100644 index 4d29575..0000000 --- a/examples/react-worker-app/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/examples/react-worker-app/README.md b/examples/react-worker-app/README.md deleted file mode 100644 index 390e0af..0000000 --- a/examples/react-worker-app/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Flowmap.gl example using React and WorkerFlowmapDataProvider - -This example is an example of using Flowmap.gl's `WorkerFlowmapDataProvider` in a React web app. - - -## Usage - -To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). -Rename `.env.example` into `.env` and add your Mapbox token there. - - -```bash -npm i -npm run start -``` - -To build a production version: - -```bash -npm run build -``` diff --git a/examples/react-worker-app/package.json b/examples/react-worker-app/package.json deleted file mode 100644 index 1c91cf6..0000000 --- a/examples/react-worker-app/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "@flowmap.gl/react-worker-app", - "version": "8.0.0-alpha.25", - "homepage": "https://flowmapblue.github.io/flowmap.gl/", - "private": true, - "dependencies": { - "@deck.gl/core": "^8.6.4", - "@deck.gl/layers": "^8.6.4", - "@deck.gl/react": "^8.6.4", - "@flowmap.gl/data": "^8.0.0-alpha.25", - "@flowmap.gl/layers": "^8.0.0-alpha.25", - "@loaders.gl/core": "^3.1.4", - "@loaders.gl/csv": "^3.1.4", - "@luma.gl/constants": "^8.1.0", - "@luma.gl/core": "^8.1.0", - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^13.2.0", - "@testing-library/user-event": "^14.2.0", - "@types/jest": "^27.5.1", - "@types/node": "^17.0.35", - "@types/react": "^18.0.9", - "@types/react-dom": "^18.0.4", - "comlink": "^4.3.1", - "d3-fetch": "^3.0.1", - "lil-gui": "^0.16.1", - "react": "^18.1.0", - "react-dom": "^18.1.0", - "react-map-gl": "^7.0.13", - "react-scripts": "^5.0.0-next.58", - "typescript": "^4.1.2", - "web-vitals": "^2.1.4" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - "defaults", - "not ie 11" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "@types/d3-fetch": "^3.0.1", - "eslint-config-react-app": "^7.0.0-next.102" - } -} diff --git a/examples/react-worker-app/public/index.html b/examples/react-worker-app/public/index.html deleted file mode 100644 index 055d38c..0000000 --- a/examples/react-worker-app/public/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - Flowmap.gl example - - - -
- - diff --git a/examples/react-worker-app/src/App.tsx b/examples/react-worker-app/src/App.tsx deleted file mode 100644 index 640934c..0000000 --- a/examples/react-worker-app/src/App.tsx +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as React from 'react'; -import {ReactNode, useEffect, useState} from 'react'; -import DeckGL from '@deck.gl/react'; -import { - FlowmapLayer, - FlowmapLayerPickingInfo, - PickingType, -} from '@flowmap.gl/layers'; -import {ViewState} from '@flowmap.gl/data'; -import { - FlowDatum, - LocationDatum, - createWorkerDataProvider, - WorkerFlowmapDataProvider, -} from './worker'; -import {Map as ReactMapGl} from 'react-map-gl'; -import {initLilGui, UI_INITIAL, useUI} from '@flowmap.gl/examples-common'; -import 'mapbox-gl/dist/mapbox-gl.css'; - -const MAPBOX_ACCESS_TOKEN = process.env.REACT_APP_MAPBOX_ACCESS_TOKEN; -const MAPBOX_STYLE_LIGHT = 'mapbox://styles/mapbox/streets-v11'; -const MAPBOX_STYLE_DARK = MAPBOX_STYLE_LIGHT; - -type TooltipState = { - position: {left: number; top: number}; - content: ReactNode; -}; - -const DATA_BASE_URL = 'https://gist.githubusercontent.com/ilyabo/'; -const DATA_PATH = `${DATA_BASE_URL}/68d3dba61d86164b940ffe60e9d36931/raw/a72938b5d51b6df9fa7bba9aa1fb7df00cd0f06a`; - -async function createDataProvider() { - return await createWorkerDataProvider({ - flows: { - url: `${DATA_PATH}/flows.csv`, - columns: { - originId: 'origin', - destId: 'dest', - count: 'count', - }, - }, - locations: { - url: `${DATA_PATH}/locations.csv`, - columns: { - id: 'id', - name: 'name', - lat: 'lat', - lon: 'lon', - }, - }, - }); -} - -function App() { - const config = useUI(UI_INITIAL, initLilGui); - const [viewState, setViewState] = useState(); - const [data, setData] = useState<{dataProvider: WorkerFlowmapDataProvider}>(); - const [tooltip, setTooltip] = useState(); - useEffect(() => { - (async () => { - const dataProvider = await createDataProvider(); - setData({dataProvider}); - const [width, height] = [globalThis.innerWidth, globalThis.innerHeight]; - const viewState = await dataProvider.getViewportForLocations([ - width, - height, - ]); - if (viewState) { - // @ts-ignore - setViewState(viewState); - } - })(); - }, []); - const handleViewStateChange = ({viewState}: any) => { - setViewState(viewState); - setTooltip(undefined); - }; - const layers = []; - if (data) { - layers.push( - new FlowmapLayer({ - id: 'my-flowmap-layer', - dataProvider: data.dataProvider, - opacity: config.opacity, - pickable: true, - darkMode: config.darkMode, - colorScheme: config.colorScheme, - fadeAmount: config.fadeAmount, - fadeEnabled: config.fadeEnabled, - fadeOpacityEnabled: config.fadeOpacityEnabled, - locationTotalsEnabled: config.locationTotalsEnabled, - animationEnabled: config.animationEnabled, - clusteringEnabled: config.clusteringEnabled, - clusteringAuto: config.clusteringAuto, - clusteringLevel: config.clusteringLevel, - adaptiveScalesEnabled: config.adaptiveScalesEnabled, - highlightColor: config.highlightColor, - maxTopFlowsDisplayNum: config.maxTopFlowsDisplayNum, - onHover: (info) => setTooltip(getTooltipState(info)), - onClick: (info) => - console.log('clicked', info.object?.type, info.object), - }), - ); - } - if (!viewState) { - return null; - } - return ( -
- - - - {tooltip && ( -
- {tooltip.content} -
- )} -
- ); -} - -function getTooltipState( - info: FlowmapLayerPickingInfo | undefined, -): TooltipState | undefined { - if (!info) return undefined; - const {x, y, object} = info; - const position = {left: x, top: y}; - switch (object?.type) { - case PickingType.LOCATION: - return { - position, - content: ( - <> -
{object.name}
-
Incoming trips: {object.totals.incomingCount}
-
Outgoing trips: {object.totals.outgoingCount}
-
Internal or round trips: {object.totals.internalCount}
- - ), - }; - case PickingType.FLOW: - return { - position, - content: ( - <> -
{`${object.origin.id} → ${object.dest.id}`}
-
{object.count}
- - ), - }; - } - return undefined; -} - -export default App; diff --git a/examples/react-worker-app/src/index.css b/examples/react-worker-app/src/index.css deleted file mode 100644 index 560aa22..0000000 --- a/examples/react-worker-app/src/index.css +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} - -.flowmap-container { - width: 100vw; - height: 100vh; -} -.light .mapboxgl-map { - filter: grayscale(0.85); - opacity: 0.5; -} -.dark { - background: #000; -} -.dark .mapboxgl-map { - filter: grayscale(0.1) invert(1) hue-rotate(-180deg) saturate(0.5) - contrast(0.9); - opacity: 0.3; -} - -.tooltip { - position: absolute; - font-size: 10px; - border-radius: 5px; - background-color: rgba(150, 150, 150, 0.75); - padding: 1em; - color: white; - pointer-events: none; -} diff --git a/examples/react-worker-app/src/index.tsx b/examples/react-worker-app/src/index.tsx deleted file mode 100644 index 2d751a0..0000000 --- a/examples/react-worker-app/src/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import './index.css'; -import App from './App'; -// import {createWorkerDataProvider, LocationFilterMode} from '@flowmap.gl/data'; -// import {FlowmapLayer} from '@flowmap.gl/layers'; -import {createRoot} from 'react-dom/client'; - -const container = document.getElementById('root'); -const root = createRoot(container!); -root.render( - - - , -); - -// (async () => { -// const base = 'https://gist.githubusercontent.com/ilyabo/'; -// const path = -// // Migrations in Switzerland -// // `${base}/a7b9701424257146b571149d92a14926/raw/2e9e1e9bcf64cf0090781b451037229ccb78e1b1`; -// // BIXI rides -// `${base}/68d3dba61d86164b940ffe60e9d36931/raw/a72938b5d51b6df9fa7bba9aa1fb7df00cd0f06a`; -// const dataProvider = await createWorkerDataProvider({ -// flows: { -// url: `${path}/flows.csv`, -// columns: { -// originId: 'origin', -// destId: 'dest', -// count: 'count', -// }, -// }, -// locations: { -// url: `${path}/locations.csv`, -// columns: { -// id: 'id', -// name: 'name', -// lat: 'lat', -// lon: 'lon', -// }, -// }, -// }); -// -// const viewport = await dataProvider.getViewportForLocations([ -// window.innerWidth, -// window.innerHeight, -// ]); -// -// if (viewport) { -// await dataProvider.setFlowmapState({ -// viewport, -// filter: { -// selectedLocations: undefined, -// locationFilterMode: LocationFilterMode.ALL, -// selectedTimeRange: undefined, -// }, -// settings: { -// ...FlowmapLayer.defaultProps, -// }, -// }); -// console.log(await dataProvider.getLayersData()); -// } -// })(); diff --git a/examples/react-worker-app/src/react-app-env.d.ts b/examples/react-worker-app/src/react-app-env.d.ts deleted file mode 100644 index 5da4f7c..0000000 --- a/examples/react-worker-app/src/react-app-env.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -/// -declare module '@deck.gl/react'; diff --git a/examples/react-worker-app/src/worker/WorkerFlowmapDataProvider.ts b/examples/react-worker-app/src/worker/WorkerFlowmapDataProvider.ts deleted file mode 100644 index 042f2bb..0000000 --- a/examples/react-worker-app/src/worker/WorkerFlowmapDataProvider.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import { - Cluster, - ClusterNode, - FlowmapData, - FlowmapDataAccessors, - FlowmapDataProvider, - LayersData, - LocationTotals, - ViewportProps, - FlowmapState, - AggregateFlow, - LocalFlowmapDataProvider, -} from '@flowmap.gl/data'; -import {load} from '@loaders.gl/core'; -import {CSVLoader} from '@loaders.gl/csv'; - -const LOADERS = [CSVLoader]; - -export type WorkerDataProviderProps = { - flows: { - url: string; - columns: { - originId: string; - destId: string; - count: string; - }; - }; - locations: { - url: string; - columns: { - id: string; - name: string; - lat: string; - lon: string; - }; - }; -}; - -export type LocationDatum = Record; -export type FlowDatum = Record; - -export default class WorkerFlowmapDataProvider - implements FlowmapDataProvider -{ - private props: WorkerDataProviderProps; - private localProvider: LocalFlowmapDataProvider; - private flowmapState: FlowmapState | undefined; - - constructor(props: WorkerDataProviderProps) { - this.props = props; - this.localProvider = new LocalFlowmapDataProvider({ - getFlowOriginId: (flow) => flow[props.flows.columns.originId] as string, - getFlowDestId: (flow) => flow[props.flows.columns.destId] as string, - getFlowMagnitude: (flow) => flow[props.flows.columns.count] as number, - // getFlowTime: (flow) => flow.time, - getLocationLat: (location) => - location[props.locations.columns.lat] as number, - getLocationLon: (location) => - location[props.locations.columns.lon] as number, - getLocationName: (location) => - location[props.locations.columns.name] as string, - getLocationId: (location) => - location[props.locations.columns.id] as string, - }); - this.flowmapState = undefined; - } - - async loadData() { - const [locations, flows] = await Promise.all([ - load(this.props.locations.url, LOADERS), - load(this.props.flows.url, LOADERS), - ]); - this.localProvider.setFlowmapData({locations, flows}); - } - - setAccessors(accessors: FlowmapDataAccessors) { - throw new Error('Not supported'); - } - - async setFlowmapData( - flowmapData: FlowmapData, - ): Promise { - throw new Error('Not supported'); - } - - async setFlowmapState(flowmapState: FlowmapState): Promise { - await this.localProvider.setFlowmapState(flowmapState); - } - - async getFlowByIndex( - idx: number, - ): Promise { - return this.localProvider.getFlowByIndex(idx); - } - - async getLocationByIndex( - idx: number, - ): Promise { - return this.localProvider.getLocationByIndex(idx); - } - - async getLayersData(): Promise { - return await this.localProvider.getLayersData(); - } - - async getLocationById( - id: string | number, - ): Promise { - return this.localProvider.getLocationById(id); - } - - async getTotalsForLocation( - id: string | number, - ): Promise { - return this.localProvider.getTotalsForLocation(id); - } - - getViewportForLocations( - dims: [number, number], - ): Promise { - return this.localProvider.getViewportForLocations(dims); - } - - async updateLayersData( - setLayersData: (layersData: LayersData | undefined) => void, - ) { - setLayersData(await this.getLayersData()); - } -} diff --git a/examples/react-worker-app/src/worker/WorkerFlowmapDataProviderWorker.ts b/examples/react-worker-app/src/worker/WorkerFlowmapDataProviderWorker.ts deleted file mode 100644 index 4c4709e..0000000 --- a/examples/react-worker-app/src/worker/WorkerFlowmapDataProviderWorker.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import {expose} from 'comlink'; -import WorkerFlowmapDataProvider from './WorkerFlowmapDataProvider'; - -expose(WorkerFlowmapDataProvider); diff --git a/examples/react-worker-app/src/worker/createWorkerDataProvider.ts b/examples/react-worker-app/src/worker/createWorkerDataProvider.ts deleted file mode 100644 index 447a211..0000000 --- a/examples/react-worker-app/src/worker/createWorkerDataProvider.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as Comlink from 'comlink'; -import WorkerFlowmapDataProvider, { - WorkerDataProviderProps, -} from './WorkerFlowmapDataProvider'; - -export default async function createWorkerDataProvider( - props: WorkerDataProviderProps, -): Promise { - const worker = new Worker( - new URL('./WorkerFlowmapDataProviderWorker', import.meta.url), - ); - const WorkerFlowmapDataProvider = - Comlink.wrap(worker); - // @ts-ignore - const provider = await new WorkerFlowmapDataProvider(props); - await provider.loadData(); - return provider; -} diff --git a/examples/react-worker-app/src/worker/index.ts b/examples/react-worker-app/src/worker/index.ts deleted file mode 100644 index b12204d..0000000 --- a/examples/react-worker-app/src/worker/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -export {default as createWorkerDataProvider} from './createWorkerDataProvider'; -export {default as WorkerFlowmapDataProvider} from './WorkerFlowmapDataProvider'; -export * from './WorkerFlowmapDataProvider'; diff --git a/examples/react-worker-app/tsconfig.json b/examples/react-worker-app/tsconfig.json deleted file mode 100644 index a273b0c..0000000 --- a/examples/react-worker-app/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": [ - "src" - ] -} diff --git a/examples/svelte-app/.env.example b/examples/svelte-app/.env.example deleted file mode 100644 index 1d4efc1..0000000 --- a/examples/svelte-app/.env.example +++ /dev/null @@ -1 +0,0 @@ -MAPBOX_ACCESS_TOKEN=… diff --git a/examples/svelte-app/.gitignore b/examples/svelte-app/.gitignore deleted file mode 100644 index 5349ef8..0000000 --- a/examples/svelte-app/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/node_modules/ -/public/build/ - -.DS_Store -.env \ No newline at end of file diff --git a/examples/svelte-app/README.md b/examples/svelte-app/README.md deleted file mode 100644 index 45dd857..0000000 --- a/examples/svelte-app/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Flowmap.gl example with Svelte JS - -This example shows how Flowmap.gl can be used in a Svelte app. - - -## Usage - -To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). -Rename `.env.example` into `.env` and add your Mapbox token there. - - -```bash -npm i -npm run dev -``` - -To build a production version: - -```bash -npm run build -``` - diff --git a/examples/svelte-app/package.json b/examples/svelte-app/package.json deleted file mode 100644 index a5ebb3c..0000000 --- a/examples/svelte-app/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "@flowmap.gl/svelte-app", - "version": "8.0.0-alpha.25", - "private": true, - "scripts": { - "build": "rollup -c", - "dev": "rollup -c -w", - "start": "sirv public --no-clear" - }, - "devDependencies": { - "@deck.gl/core": "^8.6.4", - "@deck.gl/layers": "^8.6.4", - "@deck.gl/react": "^8.6.4", - "@flowmap.gl/data": "^8.0.0-alpha.25", - "@flowmap.gl/layers": "^8.0.0-alpha.25", - "@luma.gl/constants": "^8.1.0", - "@luma.gl/core": "^8.1.0", - "@rollup/plugin-commonjs": "^22.0.0", - "@rollup/plugin-node-resolve": "^13.3.0", - "@rollup/plugin-replace": "^4.0.0", - "dotenv": "^16.0.1", - "mapbox-gl": "^2.6.1", - "rollup": "^2.3.4", - "rollup-plugin-css-only": "^3.1.0", - "rollup-plugin-livereload": "^2.0.0", - "rollup-plugin-svelte": "^7.0.0", - "rollup-plugin-terser": "^7.0.0", - "svelte": "^3.0.0" - }, - "dependencies": { - "sirv-cli": "^2.0.2" - } -} diff --git a/examples/svelte-app/public/favicon.png b/examples/svelte-app/public/favicon.png deleted file mode 100644 index 7e6f5eb..0000000 Binary files a/examples/svelte-app/public/favicon.png and /dev/null differ diff --git a/examples/svelte-app/public/global.css b/examples/svelte-app/public/global.css deleted file mode 100644 index 4652ef1..0000000 --- a/examples/svelte-app/public/global.css +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -html, body { - position: relative; - width: 100%; - height: 100%; -} - -body { - background: #000; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.flowmap-container { - width: 100vw; - height: 100vh; -} -.light .mapboxgl-map { - filter: grayscale(0.85); - opacity: 0.5; -} -.dark { - background: #000; -} -.dark .mapboxgl-map { - filter: grayscale(0.1) invert(1) hue-rotate(-180deg) saturate(0.5) - contrast(0.9); - opacity: 0.3; -} diff --git a/examples/svelte-app/public/index.html b/examples/svelte-app/public/index.html deleted file mode 100644 index 9546a71..0000000 --- a/examples/svelte-app/public/index.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - Svelte app - - - - - - - - - - - - diff --git a/examples/svelte-app/rollup.config.js b/examples/svelte-app/rollup.config.js deleted file mode 100644 index 3226010..0000000 --- a/examples/svelte-app/rollup.config.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import svelte from 'rollup-plugin-svelte'; -import commonjs from '@rollup/plugin-commonjs'; -import resolve from '@rollup/plugin-node-resolve'; -import livereload from 'rollup-plugin-livereload'; -import {terser} from 'rollup-plugin-terser'; -import css from 'rollup-plugin-css-only'; -import replace from '@rollup/plugin-replace'; -import dotenv from 'dotenv'; - -dotenv.config(); - -const production = !process.env.ROLLUP_WATCH; - -function serve() { - let server; - - function toExit() { - if (server) server.kill(0); - } - - return { - writeBundle() { - if (server) return; - server = require('child_process').spawn( - 'npm', - ['run', 'start', '--', '--dev'], - { - stdio: ['ignore', 'inherit', 'inherit'], - shell: true, - }, - ); - - process.on('SIGTERM', toExit); - process.on('exit', toExit); - }, - }; -} - -export default { - input: 'src/main.js', - output: { - sourcemap: true, - format: 'iife', - name: 'app', - file: 'public/build/bundle.js', - }, - plugins: [ - replace({ - MAPBOX_ACCESS_TOKEN: JSON.stringify(process.env.MAPBOX_ACCESS_TOKEN), - }), - svelte({ - compilerOptions: { - // enable run-time checks when not in production - dev: !production, - }, - }), - // we'll extract any component CSS out into - // a separate file - better for performance - css({output: 'bundle.css'}), - - // If you have external dependencies installed from - // npm, you'll most likely need these plugins. In - // some cases you'll need additional configuration - - // consult the documentation for details: - // https://github.com/rollup/plugins/tree/master/packages/commonjs - resolve({ - browser: true, - dedupe: ['svelte'], - }), - commonjs(), - - // In dev mode, call `npm run start` once - // the bundle has been generated - !production && serve(), - - // Watch the `public` directory and refresh the - // browser on changes when not in production - !production && livereload('public'), - - // If we're building for production (npm run build - // instead of npm run dev), minify - production && terser(), - ], - watch: { - clearScreen: false, - }, -}; diff --git a/examples/svelte-app/scripts/setupTypeScript.js b/examples/svelte-app/scripts/setupTypeScript.js deleted file mode 100644 index 1592831..0000000 --- a/examples/svelte-app/scripts/setupTypeScript.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -// @ts-check - -/** This script modifies the project to support TS code in .svelte files like: - - - - As well as validating the code for CI. - */ - -/** To work on this script: - rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template -*/ - -const fs = require('fs'); -const path = require('path'); -const {argv} = require('process'); - -const projectRoot = argv[2] || path.join(__dirname, '..'); - -// Add deps to pkg.json -const packageJSON = JSON.parse( - fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'), -); -packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { - 'svelte-check': '^2.0.0', - 'svelte-preprocess': '^4.0.0', - '@rollup/plugin-typescript': '^8.0.0', - typescript: '^4.0.0', - tslib: '^2.0.0', - '@tsconfig/svelte': '^2.0.0', -}); - -// Add script for checking -packageJSON.scripts = Object.assign(packageJSON.scripts, { - check: 'svelte-check --tsconfig ./tsconfig.json', -}); - -// Write the package JSON -fs.writeFileSync( - path.join(projectRoot, 'package.json'), - JSON.stringify(packageJSON, null, ' '), -); - -// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too -const beforeMainJSPath = path.join(projectRoot, 'src', 'main.js'); -const afterMainTSPath = path.join(projectRoot, 'src', 'main.ts'); -fs.renameSync(beforeMainJSPath, afterMainTSPath); - -// Switch the app.svelte file to use TS -const appSveltePath = path.join(projectRoot, 'src', 'App.svelte'); -let appFile = fs.readFileSync(appSveltePath, 'utf8'); -appFile = appFile.replace(' - - - - - \ No newline at end of file diff --git a/examples/svelte-app/src/Map.svelte b/examples/svelte-app/src/Map.svelte deleted file mode 100644 index 62f1e0d..0000000 --- a/examples/svelte-app/src/Map.svelte +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - -
-
- {#if map} - - {/if} -
- -
- - diff --git a/examples/svelte-app/src/main.js b/examples/svelte-app/src/main.js deleted file mode 100644 index 1213e11..0000000 --- a/examples/svelte-app/src/main.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import App from './App.svelte'; - -const app = new App({ - target: document.body, - props: { - name: 'world', - }, -}); - -export default app; diff --git a/examples/svelte-app/src/mapbox.js b/examples/svelte-app/src/mapbox.js deleted file mode 100644 index 1c28dff..0000000 --- a/examples/svelte-app/src/mapbox.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import mapbox from 'mapbox-gl'; - -// https://docs.mapbox.com/help/glossary/access-token/ -mapbox.accessToken = MAPBOX_ACCESS_TOKEN; - -const key = {}; - -export {mapbox, key}; diff --git a/examples/vanilla-html/index.html b/examples/vanilla-html/index.html deleted file mode 100644 index 6f91b59..0000000 --- a/examples/vanilla-html/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/examples/webpack-app/.babelrc b/examples/webpack-app/.babelrc deleted file mode 100644 index e393947..0000000 --- a/examples/webpack-app/.babelrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "presets": ["@babel/preset-env"], - "plugins": [ - "@babel/plugin-transform-runtime" - ] -} \ No newline at end of file diff --git a/examples/webpack-app/.env.example b/examples/webpack-app/.env.example deleted file mode 100644 index e80db06..0000000 --- a/examples/webpack-app/.env.example +++ /dev/null @@ -1 +0,0 @@ -MAPBOX_ACCESS_TOKEN=... diff --git a/examples/webpack-app/.gitignore b/examples/webpack-app/.gitignore deleted file mode 100644 index deed335..0000000 --- a/examples/webpack-app/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ -dist/ -.env diff --git a/examples/webpack-app/README.md b/examples/webpack-app/README.md deleted file mode 100644 index 2fa2f76..0000000 --- a/examples/webpack-app/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Flowmap.gl example - -This example shows how Flowmap.gl can be used in a JS app built with Webpack. - - -## Usage - -To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). -Rename `.env.example` into `.env` and add your Mapbox token there. - - -```bash -npm i -npm run start -``` - -To build a production version: - -```bash -npm run build -``` diff --git a/examples/webpack-app/index.html b/examples/webpack-app/index.html deleted file mode 100644 index 18eea88..0000000 --- a/examples/webpack-app/index.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - Flowmap.gl pure js example - - - -
-
- -
- - - diff --git a/examples/webpack-app/package.json b/examples/webpack-app/package.json deleted file mode 100644 index d4fb511..0000000 --- a/examples/webpack-app/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "@flowmap.gl/webpack-app", - "version": "8.0.0-alpha.25", - "private": true, - "scripts": { - "start": "webpack serve", - "build": "webpack --progress -p" - }, - "dependencies": { - "@deck.gl/core": "^8.6.5", - "@deck.gl/layers": "^8.6.5", - "@flowmap.gl/data": "^8.0.0-alpha.25", - "@flowmap.gl/layers": "^8.0.0-alpha.25", - "@luma.gl/constants": "^8.1.0", - "@luma.gl/core": "^8.1.0", - "d3-fetch": "^3.0.1", - "lil-gui": "^0.16.1", - "mapbox-gl": "^2.6.1" - }, - "devDependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/runtime": "^7.16.5", - "babel-loader": "8.2.5", - "dotenv-webpack": "^7.0.3", - "html-webpack-plugin": "^5.5.0", - "webpack": "^5.65.0", - "webpack-cli": "^4.9.1", - "webpack-dev-server": "^4.7.2" - } -} diff --git a/examples/webpack-app/src/app.js b/examples/webpack-app/src/app.js deleted file mode 100644 index b7b27ce..0000000 --- a/examples/webpack-app/src/app.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -import {Deck} from '@deck.gl/core'; -import mapboxgl from 'mapbox-gl'; -import {FlowmapLayer} from '@flowmap.gl/layers'; -import {GUI} from 'lil-gui'; -import {fetchData, initLilGui, UI_INITIAL} from '@flowmap.gl/examples-common'; -import {getViewStateForLocations} from '@flowmap.gl/data'; - -// eslint-disable-next-line no-undef -const MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_ACCESS_TOKEN; -const MAPBOX_STYLE_DARK = 'mapbox://styles/mapbox/dark-v10'; - -const config = {...UI_INITIAL}; - -fetchData().then((data) => { - const gui = new GUI(); - initLilGui(gui); - - const {locations, flows} = data; - // eslint-disable-next-line no-undef - const [width, height] = [globalThis.innerWidth, globalThis.innerHeight]; - const INITIAL_VIEW_STATE = getViewStateForLocations( - locations, - (loc) => [loc.lon, loc.lat], - [width, height], - {pad: 0.3}, - ); - - const map = new mapboxgl.Map({ - container: 'map', - accessToken: MAPBOX_ACCESS_TOKEN, - style: MAPBOX_STYLE_DARK, - // Note: deck.gl will be in charge of interaction and event handling - interactive: false, - center: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude], - zoom: INITIAL_VIEW_STATE.zoom, - bearing: INITIAL_VIEW_STATE.bearing, - pitch: INITIAL_VIEW_STATE.pitch, - }); - - const deck = new Deck({ - canvas: 'deck-canvas', - width: '100%', - height: '100%', - initialViewState: INITIAL_VIEW_STATE, - controller: true, - map: true, - onViewStateChange: ({viewState}) => { - map.jumpTo({ - center: [viewState.longitude, viewState.latitude], - zoom: viewState.zoom, - bearing: viewState.bearing, - pitch: viewState.pitch, - }); - }, - layers: [], - }); - - updateDeck(); - gui.onChange(({property, value}) => { - config[property] = value; - updateDeck(); - }); - - function updateDeck() { - deck.setProps({ - layers: [ - new FlowmapLayer({ - id: 'my-flowmap-layer', - data, - pickable: true, - opacity: config.opacity, - darkMode: config.darkMode, - colorScheme: config.colorScheme, - fadeAmount: config.fadeAmount, - fadeEnabled: config.fadeEnabled, - fadeOpacityEnabled: config.fadeOpacityEnabled, - locationTotalsEnabled: config.locationTotalsEnabled, - animationEnabled: config.animationEnabled, - clusteringEnabled: config.clusteringEnabled, - clusteringAuto: config.clusteringAuto, - clusteringLevel: config.clusteringLevel, - adaptiveScalesEnabled: config.adaptiveScalesEnabled, - highlightColor: config.highlightColor, - getLocationId: (loc) => loc.id, - getLocationLat: (loc) => loc.lat, - getLocationLon: (loc) => loc.lon, - getFlowOriginId: (flow) => flow.origin, - getFlowDestId: (flow) => flow.dest, - getFlowMagnitude: (flow) => flow.count, - getLocationName: (loc) => loc.name, - // onHover: (info) => setTooltip(getTooltipState(info)), - // onClick: (info) => console.log('clicked', info.type, info.object), - }), - ], - }); - } -}); diff --git a/examples/webpack-app/webpack.config.js b/examples/webpack-app/webpack.config.js deleted file mode 100644 index 3918ea6..0000000 --- a/examples/webpack-app/webpack.config.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) Flowmap.gl contributors - * Copyright (c) 2018-2020 Teralytics - * SPDX-License-Identifier: Apache-2.0 - */ - -// eslint-disable-next-line @typescript-eslint/no-var-requires,no-undef -const webpack = require('webpack'); -// eslint-disable-next-line @typescript-eslint/no-var-requires,no-undef -const HtmlWebpackPlugin = require('html-webpack-plugin'); -// eslint-disable-next-line @typescript-eslint/no-var-requires,no-undef -const Dotenv = require('dotenv-webpack'); - -// eslint-disable-next-line no-undef -module.exports = { - mode: 'development', - - entry: { - app: './src/app.js', - }, - - devtool: 'inline-source-map', - - devServer: { - static: './dist', - }, - - // module: { - // rules: [ - // { - // // Compile ES2015 using babel - // test: /\.js$/, - // exclude: [/node_modules/], - // use: [ - // { - // loader: 'babel-loader', - // // options: { - // // presets: ['@babel/env'], - // // }, - // }, - // ], - // }, - // ], - // }, - - plugins: [ - new HtmlWebpackPlugin({template: './index.html'}), - new Dotenv({ - path: './.env', // Path to .env file (this is the default) - safe: true, // load .env.example (defaults to "false" which does not use dotenv-safe) - }), - ], -}; diff --git a/packages/data/src/FlowmapAggregateAccessors.ts b/packages/data/src/FlowmapAggregateAccessors.ts index eeb884c..b41d3cb 100644 --- a/packages/data/src/FlowmapAggregateAccessors.ts +++ b/packages/data/src/FlowmapAggregateAccessors.ts @@ -13,6 +13,11 @@ import { isLocationClusterNode, } from './types'; +export type aggFunctionVars = { + aggvalue: number | undefined; + aggweight: number | undefined; +}; + export default class FlowmapAggregateAccessors { private accessors: FlowmapDataAccessors; constructor(accessors: FlowmapDataAccessors) { @@ -65,6 +70,20 @@ export default class FlowmapAggregateAccessors { return isAggregateFlow(f) ? f.count : this.accessors.getFlowMagnitude(f); }; + getFlowAggWeight = (f: F | AggregateFlow) => { + return !this.accessors.getFlowAggWeight + ? undefined + : isAggregateFlow(f) + ? f.count + : this.accessors.getFlowAggWeight(f); + }; + + getFlowAggFunc = (f: aggFunctionVars[] | undefined = []) => { + return !this.accessors.getFlowAggFunc + ? undefined + : this.accessors.getFlowAggFunc(f); + }; + // Note: Aggregate flows have no time getFlowTime = (f: F) => { const {getFlowTime} = this.accessors; diff --git a/packages/data/src/FlowmapSelectors.ts b/packages/data/src/FlowmapSelectors.ts index 6e6f2b1..6472fcc 100644 --- a/packages/data/src/FlowmapSelectors.ts +++ b/packages/data/src/FlowmapSelectors.ts @@ -60,6 +60,8 @@ import { LayersData, LocationFilterMode, LocationTotals, + FlowAggregatorFunc, + aggFunctionVars, } from './types'; const MAX_CLUSTER_ZOOM_LEVEL = 20; @@ -700,20 +702,108 @@ export default class FlowmapSelectors { if (d.internalCount != null) rv.internalCount += d.internalCount; return rv; }; - for (const f of flows) { - if ( - this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode) - ) { - const originId = this.accessors.getFlowOriginId(f); - const destId = this.accessors.getFlowDestId(f); - const count = this.accessors.getFlowMagnitude(f); - if (originId === destId) { - totals.set(originId, add(originId, {internalCount: count})); - } else { - totals.set(originId, add(originId, {outgoingCount: count})); - totals.set(destId, add(destId, {incomingCount: count})); + + const aggFuncAdd = ( + id: string | number, + d: Partial, + ): LocationTotals => { + const rv = totals.get(id) ?? { + incomingCount: 0, + outgoingCount: 0, + internalCount: 0, + }; + if (d.incomingCount != null) rv.incomingCount = d.incomingCount; + if (d.outgoingCount != null) rv.outgoingCount = d.outgoingCount; + if (d.internalCount != null) rv.internalCount = d.internalCount; + return rv; + }; + + if (this.accessors.getFlowAggFunc() === undefined) { + for (const f of flows) { + if ( + this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode) + ) { + const originId = this.accessors.getFlowOriginId(f); + const destId = this.accessors.getFlowDestId(f); + const count = this.accessors.getFlowMagnitude(f); + if (originId === destId) { + totals.set(originId, add(originId, {internalCount: count})); + } else { + totals.set(originId, add(originId, {outgoingCount: count})); + totals.set(destId, add(destId, {incomingCount: count})); + } } } + } else { + const flowDestValues = new Map(); + const flowOriginValues = new Map(); + const flowInternalValues = new Map(); + for (const f of flows) { + if ( + this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode) + ) { + const originId = this.accessors.getFlowOriginId(f).toString(); + const destId = this.accessors.getFlowDestId(f).toString(); + const count = this.accessors.getFlowMagnitude(f); + const aggweightmap = + this.accessors.getFlowAggWeight === undefined + ? this.accessors.getFlowMagnitude(f) + : this.accessors.getFlowAggWeight(f); + if (originId === destId) { + const interval = flowInternalValues.get(originId); + if (!interval) { + flowInternalValues.set(originId, [ + {aggvalue: count, aggweight: aggweightmap}, + ]); + } else { + interval.push({aggvalue: count, aggweight: aggweightmap}); + } + } else { + const destval = flowDestValues.get(destId); + if (!destval) { + flowDestValues.set(destId, [ + {aggvalue: count, aggweight: aggweightmap}, + ]); + } else { + destval.push({aggvalue: count, aggweight: aggweightmap}); + } + + const originval = flowOriginValues.get(originId); + if (!originval) { + flowOriginValues.set(originId, [ + {aggvalue: count, aggweight: aggweightmap}, + ]); + } else { + originval.push({aggvalue: count, aggweight: aggweightmap}); + } + } + } + } + + for (const [originId, values] of flowOriginValues.entries()) { + totals.set( + originId, + aggFuncAdd(originId, { + outgoingCount: this.accessors.getFlowAggFunc(values), + }), + ); + } + for (const [destId, values] of flowDestValues.entries()) { + totals.set( + destId, + aggFuncAdd(destId, { + incomingCount: this.accessors.getFlowAggFunc(values), + }), + ); + } + for (const [internalIds, values] of flowInternalValues.entries()) { + totals.set( + internalIds, + aggFuncAdd(internalIds, { + internalCount: this.accessors.getFlowAggFunc(values), + }), + ); + } } return totals; }, diff --git a/packages/data/src/cluster/ClusterIndex.ts b/packages/data/src/cluster/ClusterIndex.ts index dafc6c7..09f0b23 100644 --- a/packages/data/src/cluster/ClusterIndex.ts +++ b/packages/data/src/cluster/ClusterIndex.ts @@ -12,6 +12,7 @@ import { FlowAccessors, FlowCountsMapReduce, isCluster, + aggFunctionVars, } from './../types'; import {ascending, bisectLeft, extent} from 'd3-array'; @@ -48,9 +49,15 @@ export interface ClusterIndex { aggregateFlows: ( flows: F[], zoom: number, - {getFlowOriginId, getFlowDestId, getFlowMagnitude}: FlowAccessors, + { + getFlowOriginId, + getFlowDestId, + getFlowMagnitude, + getFlowAggFunc, + getFlowAggWeight, + }: FlowAccessors, options?: { - flowCountsMapReduce?: FlowCountsMapReduce; + flowCountsMapReduce?: FlowCountsMapReduce; }, ) => (F | AggregateFlow)[]; } @@ -165,7 +172,13 @@ export function buildIndex(clusterLevels: ClusterLevels): ClusterIndex { aggregateFlows: ( flows, zoom, - {getFlowOriginId, getFlowDestId, getFlowMagnitude}, + { + getFlowOriginId, + getFlowDestId, + getFlowMagnitude, + getFlowAggFunc, + getFlowAggWeight, + }, options = {}, ) => { if (zoom > maxZoom) { @@ -173,12 +186,16 @@ export function buildIndex(clusterLevels: ClusterLevels): ClusterIndex { } const result: (F | AggregateFlow)[] = []; const aggFlowsByKey = new Map(); + const aggFlowCountsByKey = new Map(); const makeKey = (origin: string | number, dest: string | number) => `${origin}:${dest}`; const { flowCountsMapReduce = { map: getFlowMagnitude, - reduce: (acc: any, count: number) => (acc || 0) + count, + aggweightmap: !getFlowAggWeight ? getFlowMagnitude : getFlowAggWeight, + reduce: !getFlowAggFunc + ? (acc: any, count: number) => (acc || 0) + count + : getFlowAggFunc, }, } = options; for (const flow of flows) { @@ -200,14 +217,46 @@ export function buildIndex(clusterLevels: ClusterLevels): ClusterIndex { }; result.push(aggregateFlow); aggFlowsByKey.set(key, aggregateFlow); + aggFlowCountsByKey.set(key, [ + { + aggvalue: flowCountsMapReduce.map(flow), + aggweight: flowCountsMapReduce.aggweightmap(flow), + }, + ]); } else { - aggregateFlow.count = flowCountsMapReduce.reduce( - aggregateFlow.count, - flowCountsMapReduce.map(flow), - ); + if (!getFlowAggFunc) { + aggregateFlow.count = flowCountsMapReduce.reduce( + aggregateFlow.count, + flowCountsMapReduce.map(flow), + ); + } else { + const aggFlowsCounts = aggFlowCountsByKey.get(key); + if (!aggFlowsCounts) { + aggFlowCountsByKey.set(key, [ + { + aggvalue: flowCountsMapReduce.map(flow), + aggweight: flowCountsMapReduce.aggweightmap(flow), + }, + ]); + } else { + aggFlowsCounts.push({ + aggvalue: flowCountsMapReduce.map(flow), + aggweight: flowCountsMapReduce.aggweightmap(flow), + }); + } + } } } } + if (getFlowAggFunc !== undefined) { + for (const [key, aggregateFlow] of aggFlowsByKey.entries()) { + aggregateFlow.count = flowCountsMapReduce.reduce( + aggFlowCountsByKey.get(key), + 0, + ); + } + } + return result; }, }; @@ -215,24 +264,69 @@ export function buildIndex(clusterLevels: ClusterLevels): ClusterIndex { export function makeLocationWeightGetter( flows: F[], - {getFlowOriginId, getFlowDestId, getFlowMagnitude}: FlowAccessors, + { + getFlowOriginId, + getFlowDestId, + getFlowMagnitude, + getFlowAggFunc, + getFlowAggWeight, + }: FlowAccessors, ): LocationWeightGetter { const locationTotals = { incoming: new Map(), outgoing: new Map(), }; - for (const flow of flows) { - const origin = getFlowOriginId(flow); - const dest = getFlowDestId(flow); - const count = getFlowMagnitude(flow); - locationTotals.incoming.set( - dest, - (locationTotals.incoming.get(dest) || 0) + count, - ); - locationTotals.outgoing.set( - origin, - (locationTotals.outgoing.get(origin) || 0) + count, - ); + const flowAggFunc = !getFlowAggFunc + ? (acc: any, count: number) => (acc || 0) + count + : getFlowAggFunc; + if (!getFlowAggFunc) { + for (const flow of flows) { + const origin = getFlowOriginId(flow); + const dest = getFlowDestId(flow); + const count = getFlowMagnitude(flow); + locationTotals.incoming.set( + dest, + flowAggFunc(locationTotals.incoming.get(dest) || 0, count), + ); + locationTotals.outgoing.set( + origin, + flowAggFunc(locationTotals.outgoing.get(origin) || 0, count), + ); + } + } else { + const flowDestValues = new Map(); + const flowOriginValues = new Map(); + for (const flow of flows) { + const origin = getFlowOriginId(flow).toString(); + const dest = getFlowDestId(flow).toString(); + const count = getFlowMagnitude(flow); + const aggweightmap = + getFlowAggWeight === undefined + ? getFlowMagnitude(flow) + : getFlowAggWeight(flow); + + const destFlowVal = flowDestValues.get(dest); + if (!destFlowVal) { + flowDestValues.set(dest, [{aggvalue: count, aggweight: aggweightmap}]); + } else { + destFlowVal.push({aggvalue: count, aggweight: aggweightmap}); + } + + const originFlowVal = flowOriginValues.get(origin); + if (!originFlowVal) { + flowOriginValues.set(origin, [ + {aggvalue: count, aggweight: aggweightmap}, + ]); + } else { + originFlowVal.push({aggvalue: count, aggweight: aggweightmap}); + } + } + for (const [dest, values] of flowDestValues.entries()) { + locationTotals.incoming.set(dest, flowAggFunc(values, 0)); + } + for (const [origin, values] of flowOriginValues.entries()) { + locationTotals.outgoing.set(origin, flowAggFunc(values, 0)); + } } return (id: string | number) => Math.max( diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 6c5bbb7..69e4b5c 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -3,6 +3,10 @@ * Copyright (c) 2018-2020 Teralytics * SPDX-License-Identifier: Apache-2.0 */ +export type aggFunctionVars = { + aggvalue: number | undefined; + aggweight: number | undefined; +}; export type FlowmapData = { locations: Iterable | undefined; @@ -22,12 +26,16 @@ export interface ViewState { export type FlowAccessor = (flow: F) => T; // objectInfo?: AccessorObjectInfo, export type LocationAccessor = (location: L) => T; +export type FlowAggregatorFunc = (values: V) => T; + export interface FlowAccessors { getFlowOriginId: FlowAccessor; getFlowDestId: FlowAccessor; getFlowMagnitude: FlowAccessor; getFlowTime?: FlowAccessor; // TODO: use number instead of Date // getFlowColor?: FlowAccessor; + getFlowAggFunc: FlowAggregatorFunc; + getFlowAggWeight: FlowAccessor; } export interface LocationAccessors { @@ -129,9 +137,10 @@ export function isAggregateFlow( ); } -export interface FlowCountsMapReduce { +export interface FlowCountsMapReduce { map: (flow: F) => T; - reduce: (accumulated: T, val: T) => T; + aggweightmap: (flow: H) => T; + reduce: (values: T) => T; } export enum LocationFilterMode {