Repository: teralytics/flowmap.gl Branch: master Commit: 88c0215a7a38 Files: 71 Total size: 429.7 KB Directory structure: gitextract_r_op4bg4/ ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── README.md ├── examples/ │ ├── .storybook/ │ │ ├── config.js │ │ ├── styles.css │ │ └── webpack.config.js │ ├── package.json │ ├── public/ │ │ └── data/ │ │ ├── flows-2015.json │ │ ├── flows-2016.json │ │ ├── flows-diff-2015-2016.json │ │ ├── locations.json │ │ └── source.url │ ├── src/ │ │ ├── components/ │ │ │ ├── ClusteringExample.tsx │ │ │ └── Example.tsx │ │ ├── index.tsx │ │ ├── stories/ │ │ │ └── index.tsx │ │ └── utils/ │ │ ├── pipe.ts │ │ ├── withFetch.tsx │ │ ├── withSheetsFetch.tsx │ │ └── withStats.tsx │ └── tsconfig.json ├── lerna.json ├── package.json ├── packages/ │ ├── cluster/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ClusterIndex.ts │ │ │ ├── cluster.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.esm.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── core/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── AnimatedFlowLinesLayer/ │ │ │ │ ├── AnimatedFlowLinesLayer.ts │ │ │ │ ├── AnimatedFlowLinesLayerFragment.glsl.ts │ │ │ │ ├── AnimatedFlowLinesLayerVertex.glsl.ts │ │ │ │ └── index.ts │ │ │ ├── FlowCirclesLayer/ │ │ │ │ ├── FlowCirclesLayer.ts │ │ │ │ ├── FlowCirclesLayerFragment.glsl.ts │ │ │ │ ├── FlowCirclesLayerVertex.glsl.ts │ │ │ │ └── index.ts │ │ │ ├── FlowLinesLayer/ │ │ │ │ ├── FlowLinesLayer.ts │ │ │ │ ├── FlowLinesLayerFragment.glsl.ts │ │ │ │ ├── FlowLinesLayerVertex.glsl.ts │ │ │ │ └── index.ts │ │ │ ├── FlowMapLayer.ts │ │ │ ├── LayerProps.ts │ │ │ ├── Selectors.ts │ │ │ ├── colors.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.esm.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── react/ │ ├── package.json │ ├── src/ │ │ ├── FlowMap.tsx │ │ ├── index.ts │ │ ├── legend/ │ │ │ ├── DiffColorsLegend.tsx │ │ │ ├── Disc.tsx │ │ │ ├── LegendBox.tsx │ │ │ └── LocationTotalsLegend.tsx │ │ └── viewport.ts │ ├── tsconfig.build.esm.json │ ├── tsconfig.build.json │ └── tsconfig.json ├── tslint.json └── typings/ ├── deck.gl.d.ts ├── kdbush.d.ts ├── luma.gl.d.ts └── react-map-gl.d.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules dist-* dist .idea .DS_Store .env npm-debug.log storybook-static/ lerna-debug.log ================================================ FILE: CHANGELOG.md ================================================ # Change log ## [7.3.4] - 2021-11-10 ### Fixed - Build error #124 ## [7.3.3] - 2020-12-22 ### Added - Upgrade deps ## [7.3.2] - 2020-09-14 ### Added - Upgrade deps ## [7.3.1] - 2020-08-03 ### Added - Added locationTotalsExtent ## [7.3.0] - 2020-08-02 ### Added - Added flowMagnitudeExtent - Added animationTailLength ## [7.2.4] - 2020-08-01 ### Fixed - Upgrade deps ## [7.2.2] - 2020-05-07 ### Fixed - Upgraded all deps to latest to prevent vulnerabilities warnings ## [7.2.1] - 2020-03-17 ### Fixed - Upgraded all deps to latest to prevent vulnerabilities warnings ## [7.2.0] - 2020-03-07 ### Fixed - Added missing DOUBLE type for the instanceSourcePositions and instanceTargetPositions attributes in FlowLineLayer: this led to incorrect projection when zooming in beyond level 12 - Upgraded deck.gl to 8.0.17 - Upgraded all deps to latest ## [7.1.0] - 2020-02-17 ### Fixed - Flow lines didn't render correctly with non-zero bearing/pitch ### Added - Support for Layer props (pickable, opacity etc) - Using getSubLayerPros for updateTriggers ## [7.0.0] - 2020-01-27 ### Upgraded - Upgrading all dependencies to latest - Making shaders work with deck.gl v8 ### Fixed - selectedLocationIds didn't accept `undefined` ## [6.1.1] - 2019-11-19 ### Added - Added maxLocationCircleSize - Added getAnimatedFlowLineStaggering ### Fixed - ClusterIndex.makeLocationWeightGetter didn't correctly calculate outgoing totals ## [6.1.0] - 2019-11-05 ### Added - Yarn for building - Upgraded deck.gl - Improved highlighted location area coloring; drawing location areas outline above arrows - Added staggering to animated flow lines - Added highlightedLocationAreaId for pre-hovering - Added maxFlowThickness - Added minPickableFlowThickness ## [6.0.0] - 2019-08-20 ### Added - Automated clustering - Upgraded to deck.gl@7 - Color schemes support - Improved rendering ## [5.2.0] - 2019-03-11 ### Added - Flow color overriding via `getFlowColor` ### Fixed - Disabling depth test to prevent z-fighting causing rendering issues with non-zero pitch/bearing - Specified selected color wasn't used for location areas ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. ------ BOILERPLATE NOTICE TO ADD AS A HEADER TO SOURCE CODE FILES ------- Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ------ END OF THE BOILERPLATE NOTICE --------------------- ======================================================================== FLOWMAP.GL COMPONENTS: The flowmap.gl project bundles or references the following components with separate copyright notices and license terms in example(s) and/or as dev- and/or peer-dependencies. Your use of the these components is subject to the terms and conditions of the following licenses. ======================================================================== MIT licenses ======================================================================== The following components are provided under the MIT License: reselect - http://www.npmjs.com/package/reselect @storybook/react - http://www.npmjs.com/package/@storybook/react @types/d3-array - http://www.npmjs.com/package/@types/d3-array @types/d3-collection - http://www.npmjs.com/package/@types/d3-collection @types/d3-color - http://www.npmjs.com/package/@types/d3-color @types/d3-interpolate - http://www.npmjs.com/package/@types/d3-interpolate @types/d3-scale - http://www.npmjs.com/package/@types/d3-scale @types/geojson - http://www.npmjs.com/package/@types/geojson @types/gl-matrix - http://www.npmjs.com/package/@types/gl-matrix @types/hammerjs - http://www.npmjs.com/package/@types/hammerjs @types/node - http://www.npmjs.com/package/@types/node @types/react - http://www.npmjs.com/package/@types/react @types/storybook__react - http://www.npmjs.com/package/@types/storybook__react @types/webpack - http://www.npmjs.com/package/@types/webpack awesome-typescript-loader - http://www.npmjs.com/package/awesome-typescript-loader babel-core - http://www.npmjs.com/package/babel-core deck.gl - http://www.npmjs.com/package/deck.gl file-loader - http://www.npmjs.com/package/file-loader husky - http://www.npmjs.com/package/husky lint-staged - http://www.npmjs.com/package/lint-staged luma.gl - http://www.npmjs.com/package/luma.gl npm-run-all - http://www.npmjs.com/package/npm-run-all prettier - http://www.npmjs.com/package/prettier react - http://www.npmjs.com/package/react react-dom - http://www.npmjs.com/package/react-dom react-map-gl - http://www.npmjs.com/package/react-map-gl tslint-config-prettier - http://www.npmjs.com/package/tslint-config-prettier tslint-loader - http://www.npmjs.com/package/tslint-loader tslint-plugin-prettier - http://www.npmjs.com/package/tslint-plugin-prettier webpack - http://www.npmjs.com/package/webpack react - http://www.npmjs.com/package/react react-dom - http://www.npmjs.com/package/react-dom deck.gl - http://www.npmjs.com/package/deck.gl luma.gl - http://www.npmjs.com/package/luma.gl ======================================================================== BSD 3-Clause licenses ======================================================================== The following components are provided under the BSD 3-Clause license: d3-array - http://www.npmjs.com/package/d3-array d3-collection - http://www.npmjs.com/package/d3-collection d3-color - http://www.npmjs.com/package/d3-color d3-interpolate - http://www.npmjs.com/package/d3-interpolate d3-scale - http://www.npmjs.com/package/d3-scale @mapbox/geo-viewport - http://www.npmjs.com/package/@mapbox/geo-viewport ======================================================================== Apache 2.0 licenses ======================================================================== The following components are provided under the Apache 2.0 license: tslint - http://www.npmjs.com/package/tslint tslint-react - http://www.npmjs.com/package/tslint-react typescript - http://www.npmjs.com/package/typescript ================================================ FILE: NOTICE ================================================ flowmap.gl Copyright 2018 Teralytics This product includes software developed at The Apache Software Foundation (http://www.apache.org/). Portions of this software were developed at Teralytics (http://www.teralytics.net) ================================================ FILE: README.md ================================================ # flowmap.gl ## IMPORTANT: This repository is in maintenance-only mode. ## > [The next version of the library is being developed here](https://github.com/FlowmapBlue/flowmap.gl) < [Flow map](https://en.wikipedia.org/wiki/Flow_map) drawing layer for [deck.gl](http://uber.github.io/deck.gl). Can be used for visualizing movement of people (e.g. migration) or objects between geographic locations. The layer is rendered in WebGL and can handle large numbers of flows with a good rendering performance. Try [flowmap.blue](https://flowmap.blue/) for an easy way of publishing a flow map backed by a Google Sheets spreadsheet (no programming skills required). Check out the [live examples Storybook](https://teralytics.github.io/flowmap.gl/index.html) or the minimal example apps: [with React](https://github.com/ilyabo/flowmap.gl-example), [without React](https://github.com/ilyabo/flowmap.gl-purejs-example/). ## Features Given an array of locations and an array of flows between these locations the layer will do the following: - Represent the flows as lines of varying thickness depending on the flow magnitudes - The flow lines are sorted so that the larger flows are drawn above - GeoJSON geometries of the location areas are rendered as polygons - Total incoming and outgoing flows for the locations are calculated and represented as circles of varying sizes. ### Location totals Both the incoming and outgoing totals for the locations are represented. A darker outline means that there are more incoming flows, a lighter outline means that there are more outgoing flows. For instance, below we compare between the evening and the morning commuting behaviors of a large city: ### Difference mode The layer can be used to show the [difference between two moments in time](https://teralytics.github.io/flowmap.gl/index.html?path=/story/basic--difference-mode). ## Usage First install the required dependencies: ``` npm install @flowmap.gl/core deck.gl react-map-gl ``` Then, you can either use as a deck.gl layer or flowmap.gl as a React component: ### Usage as a deck.gl layer With this approach you can use flowmap.gl together with other deck.gl layers. ```jsx harmony import { StaticMap } from 'react-map-gl'; import { DeckGL } from 'deck.gl'; import FlowMapLayer from '@flowmap.gl/core'; import * as ReactDOM from 'react-dom'; ReactDOM.render( flow.count || 0, getFlowOriginId: (flow) => flow.origin, getFlowDestId: (flow) => flow.dest, getLocationId: (loc) => loc.id, getLocationCentroid: (location) => [location.lon, location.lat], }) ]} > , document.body ) ``` ### Usage as a React component Install this additional dependency: ``` npm install @flowmap.gl/react ``` ```jsx harmony import FlowMap, { getViewStateForLocations } from '@flowmap.gl/react' const MapVis = ({ width, height }) =>
l.properties.centroid, [ width, height ] )} mapboxAccessToken={mapboxAccessToken} flows={flows} locations={locations} getLocationId={l => l.id} getLocationCentroid={l => l.properties.centroid} getFlowOriginId={f => f.origin} getFlowDestId={f => f.dest} getFlowMagnitude={f => f.count} pickable={true} />
``` The full list of supported props: ```typescript interface Props { id: string; locations: Locations; flows: Flow[]; diffMode?: boolean; animate?: boolean; animationCurrentTime?: number; animationTailLength?: number; colors?: Colors | DiffColors; getLocationId?: LocationAccessor; getLocationCentroid?: LocationAccessor<[number, number]>; getLocationTotalIn?: LocationAccessor; getLocationTotalOut?: LocationAccessor; getLocationTotalWithin?: LocationAccessor; getFlowOriginId?: FlowAccessor; getFlowDestId?: FlowAccessor; getFlowMagnitude?: FlowAccessor; getAnimatedFlowLineStaggering?: FlowAccessor; getFlowColor?: FlowAccessor; maxFlowThickness?: number; flowMagnitudeExtent?: [number, number]; locationTotalsExtent?: [number, number]; maxLocationCircleSize?: number; minPickableFlowThickness?: number; showTotals?: boolean; showLocationAreas?: boolean; showOnlyTopFlows?: number; selectedLocationIds?: string[]; highlightedLocationId?: string; highlightedLocationAreaId?: string; highlightedFlow?: Flow; outlineThickness?: number; pickable?: boolean; onClick?: PickingHandler; onHover?: PickingHandler; } ``` ## Development Create an `.env` file in the project root containing one line: MapboxAccessToken= Then, run: yarn install yarn start open http://localhost:6006 If you want to make changes to the `core` and `react` packages and have them automatically recompiled, run the following: yarn core-dev yarn react-dev ## Acknowledgements Many thanks to [Philippe Voinov](https://github.com/tehwalris) for his help with the first version of the FlowLinesLayer. ## License flowmap.gl Copyright 2018 Teralytics This product includes software developed at The Apache Software Foundation (http://www.apache.org/). Portions of this software were developed at Teralytics (http://www.teralytics.net) ================================================ FILE: examples/.storybook/config.js ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import './styles.css'; import { configure } from '@storybook/react'; const req = require.context('../src/stories', true, /\.tsx?$/); function loadStories() { req.keys().forEach(req); } configure(loadStories, module); ================================================ FILE: examples/.storybook/styles.css ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ body { font-family: sans-serif; } #deckgl-overlay { /* This is temporary until mixBlendMode style prop works in as before v8 */ mix-blend-mode: inherit; } ================================================ FILE: examples/.storybook/webpack.config.js ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ const Dotenv = require('dotenv-webpack'); module.exports = async ({ config, mode }) => { config.plugins.push( new Dotenv({ path: '../.env', }) ); config.module.rules = [ { test: /\.tsx?$/, loader: 'awesome-typescript-loader', exclude: /node_modules/, }, { test: /\.(png|jpg|gif)$/, loader: 'url-loader', exclude: /node_modules/, options: { limit: 8192, } }, { test: /\.css$/, use: ['style-loader', 'css-loader'], } ]; config.resolve.extensions = ['.ts', '.tsx', '.js', '.jsx', '.json']; return config; }; ================================================ FILE: examples/package.json ================================================ { "name": "flowmap.gl-examples", "description": "flowmap.gl usage examples", "author": "Teralytics AG", "license": "Apache-2.0", "version": "7.3.4", "private": true, "keywords": [ "flows", "flow map", "webgl", "visualization", "overlay", "layer" ], "repository": { "type": "git", "url": "https://github.com/teralytics/flowmap.gl.git" }, "main": "dist/index.js", "module": "dist-esm/index.js", "types": "dist/index.d.ts", "files": [ "src", "dist", "dist-esm" ], "scripts": { "start": "start-storybook -p 6006 -s ./public --ci ", "storybook:build": "build-storybook -s ./public", "storybook:deploy": "yarn storybook:build && gh-pages -d storybook-static", "typecheck": "tsc --noEmit" }, "dependencies": { "@deck.gl/core": "^8.2.8", "@deck.gl/layers": "^8.2.8", "@deck.gl/react": "^8.2.8", "@flowmap.gl/cluster": "^7.3.4", "@flowmap.gl/core": "^7.3.4", "@flowmap.gl/react": "^7.3.4", "@types/d3-scale-chromatic": "^1.5.0", "d3-color": "^2.0.0", "d3-dsv": "^2.0.0", "d3-geo": "^2.0.1", "d3-scale-chromatic": "^2.0.0", "react": "^16.13.1", "react-dom": "^16.13.1", "react-map-gl": "^5.2.8", "stats.js": "^0.17.0", "tslint": "^6.1.2", "tslint-plugin-prettier": "^2.3.0" }, "devDependencies": { "@babel/core": "^7.11.6", "@storybook/react": "^6.0.21", "@types/d3-color": "^1.2.2", "@types/d3-dsv": "^1.0.36", "@types/d3-geo": "^1.11.1", "@types/geojson": "^7946.0.7", "@types/node": "^14.10.1", "@types/react": "^16.9.49", "@types/stats.js": "^0.17.0", "@types/storybook__react": "^5.2.1", "awesome-typescript-loader": "^5.2.1", "babel-loader": "^8.1.0", "dotenv-webpack": "^2.0.0", "file-loader": "^6.1.0", "gh-pages": "^3.1.0", "npm-run-all": "^4.1.5", "style-loader": "^1.2.1", "typescript": "^4.0.2", "webpack": "^4.43.0" } } ================================================ FILE: examples/public/data/flows-2015.json ================================================ [{"year":"2015","origin":"ZH","dest":"ZH","count":63760},{"year":"2015","origin":"ZH","dest":"BE","count":1685},{"year":"2015","origin":"ZH","dest":"LU","count":1104},{"year":"2015","origin":"ZH","dest":"UR","count":55},{"year":"2015","origin":"ZH","dest":"SZ","count":1821},{"year":"2015","origin":"ZH","dest":"OW","count":93},{"year":"2015","origin":"ZH","dest":"NW","count":89},{"year":"2015","origin":"ZH","dest":"GL","count":286},{"year":"2015","origin":"ZH","dest":"ZG","count":1256},{"year":"2015","origin":"ZH","dest":"FR","count":160},{"year":"2015","origin":"ZH","dest":"SO","count":582},{"year":"2015","origin":"ZH","dest":"BS","count":503},{"year":"2015","origin":"ZH","dest":"BL","count":398},{"year":"2015","origin":"ZH","dest":"SH","count":1019},{"year":"2015","origin":"ZH","dest":"AR","count":146},{"year":"2015","origin":"ZH","dest":"AI","count":45},{"year":"2015","origin":"ZH","dest":"SG","count":2492},{"year":"2015","origin":"ZH","dest":"GR","count":876},{"year":"2015","origin":"ZH","dest":"AG","count":6041},{"year":"2015","origin":"ZH","dest":"TG","count":2663},{"year":"2015","origin":"ZH","dest":"TI","count":415},{"year":"2015","origin":"ZH","dest":"VD","count":446},{"year":"2015","origin":"ZH","dest":"VS","count":211},{"year":"2015","origin":"ZH","dest":"NE","count":64},{"year":"2015","origin":"ZH","dest":"GE","count":198},{"year":"2015","origin":"ZH","dest":"JU","count":14},{"year":"2015","origin":"BE","dest":"ZH","count":2186},{"year":"2015","origin":"BE","dest":"BE","count":50330},{"year":"2015","origin":"BE","dest":"LU","count":885},{"year":"2015","origin":"BE","dest":"UR","count":32},{"year":"2015","origin":"BE","dest":"SZ","count":175},{"year":"2015","origin":"BE","dest":"OW","count":59},{"year":"2015","origin":"BE","dest":"NW","count":76},{"year":"2015","origin":"BE","dest":"GL","count":29},{"year":"2015","origin":"BE","dest":"ZG","count":165},{"year":"2015","origin":"BE","dest":"FR","count":1391},{"year":"2015","origin":"BE","dest":"SO","count":2299},{"year":"2015","origin":"BE","dest":"BS","count":328},{"year":"2015","origin":"BE","dest":"BL","count":353},{"year":"2015","origin":"BE","dest":"SH","count":64},{"year":"2015","origin":"BE","dest":"AR","count":57},{"year":"2015","origin":"BE","dest":"AI","count":9},{"year":"2015","origin":"BE","dest":"SG","count":349},{"year":"2015","origin":"BE","dest":"GR","count":284},{"year":"2015","origin":"BE","dest":"AG","count":1070},{"year":"2015","origin":"BE","dest":"TG","count":183},{"year":"2015","origin":"BE","dest":"TI","count":127},{"year":"2015","origin":"BE","dest":"VD","count":646},{"year":"2015","origin":"BE","dest":"VS","count":495},{"year":"2015","origin":"BE","dest":"NE","count":558},{"year":"2015","origin":"BE","dest":"GE","count":137},{"year":"2015","origin":"BE","dest":"JU","count":327},{"year":"2015","origin":"LU","dest":"ZH","count":1510},{"year":"2015","origin":"LU","dest":"BE","count":928},{"year":"2015","origin":"LU","dest":"LU","count":16318},{"year":"2015","origin":"LU","dest":"UR","count":94},{"year":"2015","origin":"LU","dest":"SZ","count":522},{"year":"2015","origin":"LU","dest":"OW","count":314},{"year":"2015","origin":"LU","dest":"NW","count":557},{"year":"2015","origin":"LU","dest":"GL","count":19},{"year":"2015","origin":"LU","dest":"ZG","count":733},{"year":"2015","origin":"LU","dest":"FR","count":38},{"year":"2015","origin":"LU","dest":"SO","count":299},{"year":"2015","origin":"LU","dest":"BS","count":133},{"year":"2015","origin":"LU","dest":"BL","count":132},{"year":"2015","origin":"LU","dest":"SH","count":32},{"year":"2015","origin":"LU","dest":"AR","count":20},{"year":"2015","origin":"LU","dest":"AI","count":1},{"year":"2015","origin":"LU","dest":"SG","count":181},{"year":"2015","origin":"LU","dest":"GR","count":164},{"year":"2015","origin":"LU","dest":"AG","count":1393},{"year":"2015","origin":"LU","dest":"TG","count":80},{"year":"2015","origin":"LU","dest":"TI","count":74},{"year":"2015","origin":"LU","dest":"VD","count":52},{"year":"2015","origin":"LU","dest":"VS","count":193},{"year":"2015","origin":"LU","dest":"NE","count":9},{"year":"2015","origin":"LU","dest":"GE","count":16},{"year":"2015","origin":"LU","dest":"JU","count":6},{"year":"2015","origin":"UR","dest":"ZH","count":106},{"year":"2015","origin":"UR","dest":"BE","count":30},{"year":"2015","origin":"UR","dest":"LU","count":149},{"year":"2015","origin":"UR","dest":"UR","count":1127},{"year":"2015","origin":"UR","dest":"SZ","count":85},{"year":"2015","origin":"UR","dest":"OW","count":19},{"year":"2015","origin":"UR","dest":"NW","count":36},{"year":"2015","origin":"UR","dest":"GL","count":6},{"year":"2015","origin":"UR","dest":"ZG","count":34},{"year":"2015","origin":"UR","dest":"FR","count":2},{"year":"2015","origin":"UR","dest":"SO","count":24},{"year":"2015","origin":"UR","dest":"BS","count":6},{"year":"2015","origin":"UR","dest":"BL","count":4},{"year":"2015","origin":"UR","dest":"SH","count":3},{"year":"2015","origin":"UR","dest":"AR","count":2},{"year":"2015","origin":"UR","dest":"AI","count":0},{"year":"2015","origin":"UR","dest":"SG","count":15},{"year":"2015","origin":"UR","dest":"GR","count":41},{"year":"2015","origin":"UR","dest":"AG","count":36},{"year":"2015","origin":"UR","dest":"TG","count":10},{"year":"2015","origin":"UR","dest":"TI","count":4},{"year":"2015","origin":"UR","dest":"VD","count":0},{"year":"2015","origin":"UR","dest":"VS","count":12},{"year":"2015","origin":"UR","dest":"NE","count":2},{"year":"2015","origin":"UR","dest":"GE","count":2},{"year":"2015","origin":"UR","dest":"JU","count":0},{"year":"2015","origin":"SZ","dest":"ZH","count":1540},{"year":"2015","origin":"SZ","dest":"BE","count":157},{"year":"2015","origin":"SZ","dest":"LU","count":521},{"year":"2015","origin":"SZ","dest":"UR","count":80},{"year":"2015","origin":"SZ","dest":"SZ","count":4326},{"year":"2015","origin":"SZ","dest":"OW","count":47},{"year":"2015","origin":"SZ","dest":"NW","count":53},{"year":"2015","origin":"SZ","dest":"GL","count":158},{"year":"2015","origin":"SZ","dest":"ZG","count":396},{"year":"2015","origin":"SZ","dest":"FR","count":13},{"year":"2015","origin":"SZ","dest":"SO","count":46},{"year":"2015","origin":"SZ","dest":"BS","count":45},{"year":"2015","origin":"SZ","dest":"BL","count":33},{"year":"2015","origin":"SZ","dest":"SH","count":14},{"year":"2015","origin":"SZ","dest":"AR","count":15},{"year":"2015","origin":"SZ","dest":"AI","count":5},{"year":"2015","origin":"SZ","dest":"SG","count":526},{"year":"2015","origin":"SZ","dest":"GR","count":117},{"year":"2015","origin":"SZ","dest":"AG","count":293},{"year":"2015","origin":"SZ","dest":"TG","count":87},{"year":"2015","origin":"SZ","dest":"TI","count":47},{"year":"2015","origin":"SZ","dest":"VD","count":44},{"year":"2015","origin":"SZ","dest":"VS","count":32},{"year":"2015","origin":"SZ","dest":"NE","count":4},{"year":"2015","origin":"SZ","dest":"GE","count":20},{"year":"2015","origin":"SZ","dest":"JU","count":5},{"year":"2015","origin":"OW","dest":"ZH","count":99},{"year":"2015","origin":"OW","dest":"BE","count":86},{"year":"2015","origin":"OW","dest":"LU","count":392},{"year":"2015","origin":"OW","dest":"UR","count":20},{"year":"2015","origin":"OW","dest":"SZ","count":30},{"year":"2015","origin":"OW","dest":"OW","count":899},{"year":"2015","origin":"OW","dest":"NW","count":178},{"year":"2015","origin":"OW","dest":"GL","count":1},{"year":"2015","origin":"OW","dest":"ZG","count":18},{"year":"2015","origin":"OW","dest":"FR","count":8},{"year":"2015","origin":"OW","dest":"SO","count":16},{"year":"2015","origin":"OW","dest":"BS","count":8},{"year":"2015","origin":"OW","dest":"BL","count":17},{"year":"2015","origin":"OW","dest":"SH","count":1},{"year":"2015","origin":"OW","dest":"AR","count":7},{"year":"2015","origin":"OW","dest":"AI","count":1},{"year":"2015","origin":"OW","dest":"SG","count":17},{"year":"2015","origin":"OW","dest":"GR","count":23},{"year":"2015","origin":"OW","dest":"AG","count":55},{"year":"2015","origin":"OW","dest":"TG","count":21},{"year":"2015","origin":"OW","dest":"TI","count":7},{"year":"2015","origin":"OW","dest":"VD","count":8},{"year":"2015","origin":"OW","dest":"VS","count":10},{"year":"2015","origin":"OW","dest":"NE","count":0},{"year":"2015","origin":"OW","dest":"GE","count":1},{"year":"2015","origin":"OW","dest":"JU","count":3},{"year":"2015","origin":"NW","dest":"ZH","count":118},{"year":"2015","origin":"NW","dest":"BE","count":57},{"year":"2015","origin":"NW","dest":"LU","count":542},{"year":"2015","origin":"NW","dest":"UR","count":44},{"year":"2015","origin":"NW","dest":"SZ","count":42},{"year":"2015","origin":"NW","dest":"OW","count":207},{"year":"2015","origin":"NW","dest":"NW","count":1284},{"year":"2015","origin":"NW","dest":"GL","count":0},{"year":"2015","origin":"NW","dest":"ZG","count":47},{"year":"2015","origin":"NW","dest":"FR","count":4},{"year":"2015","origin":"NW","dest":"SO","count":17},{"year":"2015","origin":"NW","dest":"BS","count":8},{"year":"2015","origin":"NW","dest":"BL","count":9},{"year":"2015","origin":"NW","dest":"SH","count":1},{"year":"2015","origin":"NW","dest":"AR","count":2},{"year":"2015","origin":"NW","dest":"AI","count":0},{"year":"2015","origin":"NW","dest":"SG","count":17},{"year":"2015","origin":"NW","dest":"GR","count":22},{"year":"2015","origin":"NW","dest":"AG","count":60},{"year":"2015","origin":"NW","dest":"TG","count":6},{"year":"2015","origin":"NW","dest":"TI","count":6},{"year":"2015","origin":"NW","dest":"VD","count":6},{"year":"2015","origin":"NW","dest":"VS","count":12},{"year":"2015","origin":"NW","dest":"NE","count":1},{"year":"2015","origin":"NW","dest":"GE","count":4},{"year":"2015","origin":"NW","dest":"JU","count":1},{"year":"2015","origin":"GL","dest":"ZH","count":280},{"year":"2015","origin":"GL","dest":"BE","count":44},{"year":"2015","origin":"GL","dest":"LU","count":37},{"year":"2015","origin":"GL","dest":"UR","count":4},{"year":"2015","origin":"GL","dest":"SZ","count":122},{"year":"2015","origin":"GL","dest":"OW","count":8},{"year":"2015","origin":"GL","dest":"NW","count":0},{"year":"2015","origin":"GL","dest":"GL","count":917},{"year":"2015","origin":"GL","dest":"ZG","count":20},{"year":"2015","origin":"GL","dest":"FR","count":9},{"year":"2015","origin":"GL","dest":"SO","count":19},{"year":"2015","origin":"GL","dest":"BS","count":4},{"year":"2015","origin":"GL","dest":"BL","count":12},{"year":"2015","origin":"GL","dest":"SH","count":8},{"year":"2015","origin":"GL","dest":"AR","count":10},{"year":"2015","origin":"GL","dest":"AI","count":1},{"year":"2015","origin":"GL","dest":"SG","count":303},{"year":"2015","origin":"GL","dest":"GR","count":64},{"year":"2015","origin":"GL","dest":"AG","count":53},{"year":"2015","origin":"GL","dest":"TG","count":24},{"year":"2015","origin":"GL","dest":"TI","count":10},{"year":"2015","origin":"GL","dest":"VD","count":3},{"year":"2015","origin":"GL","dest":"VS","count":16},{"year":"2015","origin":"GL","dest":"NE","count":2},{"year":"2015","origin":"GL","dest":"GE","count":1},{"year":"2015","origin":"GL","dest":"JU","count":5},{"year":"2015","origin":"ZG","dest":"ZH","count":1075},{"year":"2015","origin":"ZG","dest":"BE","count":163},{"year":"2015","origin":"ZG","dest":"LU","count":761},{"year":"2015","origin":"ZG","dest":"UR","count":26},{"year":"2015","origin":"ZG","dest":"SZ","count":383},{"year":"2015","origin":"ZG","dest":"OW","count":30},{"year":"2015","origin":"ZG","dest":"NW","count":41},{"year":"2015","origin":"ZG","dest":"GL","count":5},{"year":"2015","origin":"ZG","dest":"ZG","count":4160},{"year":"2015","origin":"ZG","dest":"FR","count":12},{"year":"2015","origin":"ZG","dest":"SO","count":51},{"year":"2015","origin":"ZG","dest":"BS","count":31},{"year":"2015","origin":"ZG","dest":"BL","count":46},{"year":"2015","origin":"ZG","dest":"SH","count":21},{"year":"2015","origin":"ZG","dest":"AR","count":18},{"year":"2015","origin":"ZG","dest":"AI","count":4},{"year":"2015","origin":"ZG","dest":"SG","count":73},{"year":"2015","origin":"ZG","dest":"GR","count":41},{"year":"2015","origin":"ZG","dest":"AG","count":472},{"year":"2015","origin":"ZG","dest":"TG","count":47},{"year":"2015","origin":"ZG","dest":"TI","count":35},{"year":"2015","origin":"ZG","dest":"VD","count":63},{"year":"2015","origin":"ZG","dest":"VS","count":28},{"year":"2015","origin":"ZG","dest":"NE","count":10},{"year":"2015","origin":"ZG","dest":"GE","count":20},{"year":"2015","origin":"ZG","dest":"JU","count":1},{"year":"2015","origin":"FR","dest":"ZH","count":333},{"year":"2015","origin":"FR","dest":"BE","count":1517},{"year":"2015","origin":"FR","dest":"LU","count":81},{"year":"2015","origin":"FR","dest":"UR","count":3},{"year":"2015","origin":"FR","dest":"SZ","count":18},{"year":"2015","origin":"FR","dest":"OW","count":1},{"year":"2015","origin":"FR","dest":"NW","count":7},{"year":"2015","origin":"FR","dest":"GL","count":2},{"year":"2015","origin":"FR","dest":"ZG","count":31},{"year":"2015","origin":"FR","dest":"FR","count":14758},{"year":"2015","origin":"FR","dest":"SO","count":105},{"year":"2015","origin":"FR","dest":"BS","count":67},{"year":"2015","origin":"FR","dest":"BL","count":46},{"year":"2015","origin":"FR","dest":"SH","count":7},{"year":"2015","origin":"FR","dest":"AR","count":4},{"year":"2015","origin":"FR","dest":"AI","count":0},{"year":"2015","origin":"FR","dest":"SG","count":39},{"year":"2015","origin":"FR","dest":"GR","count":18},{"year":"2015","origin":"FR","dest":"AG","count":99},{"year":"2015","origin":"FR","dest":"TG","count":18},{"year":"2015","origin":"FR","dest":"TI","count":47},{"year":"2015","origin":"FR","dest":"VD","count":2165},{"year":"2015","origin":"FR","dest":"VS","count":351},{"year":"2015","origin":"FR","dest":"NE","count":229},{"year":"2015","origin":"FR","dest":"GE","count":194},{"year":"2015","origin":"FR","dest":"JU","count":54},{"year":"2015","origin":"SO","dest":"ZH","count":632},{"year":"2015","origin":"SO","dest":"BE","count":2207},{"year":"2015","origin":"SO","dest":"LU","count":365},{"year":"2015","origin":"SO","dest":"UR","count":2},{"year":"2015","origin":"SO","dest":"SZ","count":58},{"year":"2015","origin":"SO","dest":"OW","count":17},{"year":"2015","origin":"SO","dest":"NW","count":26},{"year":"2015","origin":"SO","dest":"GL","count":5},{"year":"2015","origin":"SO","dest":"ZG","count":54},{"year":"2015","origin":"SO","dest":"FR","count":70},{"year":"2015","origin":"SO","dest":"SO","count":10266},{"year":"2015","origin":"SO","dest":"BS","count":381},{"year":"2015","origin":"SO","dest":"BL","count":988},{"year":"2015","origin":"SO","dest":"SH","count":12},{"year":"2015","origin":"SO","dest":"AR","count":22},{"year":"2015","origin":"SO","dest":"AI","count":0},{"year":"2015","origin":"SO","dest":"SG","count":87},{"year":"2015","origin":"SO","dest":"GR","count":52},{"year":"2015","origin":"SO","dest":"AG","count":1436},{"year":"2015","origin":"SO","dest":"TG","count":85},{"year":"2015","origin":"SO","dest":"TI","count":56},{"year":"2015","origin":"SO","dest":"VD","count":39},{"year":"2015","origin":"SO","dest":"VS","count":69},{"year":"2015","origin":"SO","dest":"NE","count":16},{"year":"2015","origin":"SO","dest":"GE","count":9},{"year":"2015","origin":"SO","dest":"JU","count":19},{"year":"2015","origin":"BS","dest":"ZH","count":686},{"year":"2015","origin":"BS","dest":"BE","count":447},{"year":"2015","origin":"BS","dest":"LU","count":128},{"year":"2015","origin":"BS","dest":"UR","count":7},{"year":"2015","origin":"BS","dest":"SZ","count":34},{"year":"2015","origin":"BS","dest":"OW","count":8},{"year":"2015","origin":"BS","dest":"NW","count":19},{"year":"2015","origin":"BS","dest":"GL","count":5},{"year":"2015","origin":"BS","dest":"ZG","count":61},{"year":"2015","origin":"BS","dest":"FR","count":35},{"year":"2015","origin":"BS","dest":"SO","count":476},{"year":"2015","origin":"BS","dest":"BS","count":1267},{"year":"2015","origin":"BS","dest":"BL","count":3599},{"year":"2015","origin":"BS","dest":"SH","count":25},{"year":"2015","origin":"BS","dest":"AR","count":12},{"year":"2015","origin":"BS","dest":"AI","count":3},{"year":"2015","origin":"BS","dest":"SG","count":83},{"year":"2015","origin":"BS","dest":"GR","count":67},{"year":"2015","origin":"BS","dest":"AG","count":616},{"year":"2015","origin":"BS","dest":"TG","count":33},{"year":"2015","origin":"BS","dest":"TI","count":51},{"year":"2015","origin":"BS","dest":"VD","count":99},{"year":"2015","origin":"BS","dest":"VS","count":35},{"year":"2015","origin":"BS","dest":"NE","count":31},{"year":"2015","origin":"BS","dest":"GE","count":61},{"year":"2015","origin":"BS","dest":"JU","count":38},{"year":"2015","origin":"BL","dest":"ZH","count":498},{"year":"2015","origin":"BL","dest":"BE","count":436},{"year":"2015","origin":"BL","dest":"LU","count":150},{"year":"2015","origin":"BL","dest":"UR","count":7},{"year":"2015","origin":"BL","dest":"SZ","count":47},{"year":"2015","origin":"BL","dest":"OW","count":14},{"year":"2015","origin":"BL","dest":"NW","count":10},{"year":"2015","origin":"BL","dest":"GL","count":9},{"year":"2015","origin":"BL","dest":"ZG","count":60},{"year":"2015","origin":"BL","dest":"FR","count":27},{"year":"2015","origin":"BL","dest":"SO","count":1158},{"year":"2015","origin":"BL","dest":"BS","count":2527},{"year":"2015","origin":"BL","dest":"BL","count":9953},{"year":"2015","origin":"BL","dest":"SH","count":22},{"year":"2015","origin":"BL","dest":"AR","count":5},{"year":"2015","origin":"BL","dest":"AI","count":4},{"year":"2015","origin":"BL","dest":"SG","count":72},{"year":"2015","origin":"BL","dest":"GR","count":65},{"year":"2015","origin":"BL","dest":"AG","count":1172},{"year":"2015","origin":"BL","dest":"TG","count":44},{"year":"2015","origin":"BL","dest":"TI","count":45},{"year":"2015","origin":"BL","dest":"VD","count":47},{"year":"2015","origin":"BL","dest":"VS","count":29},{"year":"2015","origin":"BL","dest":"NE","count":11},{"year":"2015","origin":"BL","dest":"GE","count":11},{"year":"2015","origin":"BL","dest":"JU","count":46},{"year":"2015","origin":"SH","dest":"ZH","count":1000},{"year":"2015","origin":"SH","dest":"BE","count":58},{"year":"2015","origin":"SH","dest":"LU","count":36},{"year":"2015","origin":"SH","dest":"UR","count":2},{"year":"2015","origin":"SH","dest":"SZ","count":12},{"year":"2015","origin":"SH","dest":"OW","count":3},{"year":"2015","origin":"SH","dest":"NW","count":3},{"year":"2015","origin":"SH","dest":"GL","count":10},{"year":"2015","origin":"SH","dest":"ZG","count":18},{"year":"2015","origin":"SH","dest":"FR","count":6},{"year":"2015","origin":"SH","dest":"SO","count":18},{"year":"2015","origin":"SH","dest":"BS","count":26},{"year":"2015","origin":"SH","dest":"BL","count":19},{"year":"2015","origin":"SH","dest":"SH","count":2286},{"year":"2015","origin":"SH","dest":"AR","count":10},{"year":"2015","origin":"SH","dest":"AI","count":4},{"year":"2015","origin":"SH","dest":"SG","count":122},{"year":"2015","origin":"SH","dest":"GR","count":41},{"year":"2015","origin":"SH","dest":"AG","count":102},{"year":"2015","origin":"SH","dest":"TG","count":335},{"year":"2015","origin":"SH","dest":"TI","count":11},{"year":"2015","origin":"SH","dest":"VD","count":15},{"year":"2015","origin":"SH","dest":"VS","count":8},{"year":"2015","origin":"SH","dest":"NE","count":1},{"year":"2015","origin":"SH","dest":"GE","count":6},{"year":"2015","origin":"SH","dest":"JU","count":3},{"year":"2015","origin":"AR","dest":"ZH","count":209},{"year":"2015","origin":"AR","dest":"BE","count":58},{"year":"2015","origin":"AR","dest":"LU","count":21},{"year":"2015","origin":"AR","dest":"UR","count":0},{"year":"2015","origin":"AR","dest":"SZ","count":12},{"year":"2015","origin":"AR","dest":"OW","count":0},{"year":"2015","origin":"AR","dest":"NW","count":2},{"year":"2015","origin":"AR","dest":"GL","count":10},{"year":"2015","origin":"AR","dest":"ZG","count":6},{"year":"2015","origin":"AR","dest":"FR","count":6},{"year":"2015","origin":"AR","dest":"SO","count":7},{"year":"2015","origin":"AR","dest":"BS","count":11},{"year":"2015","origin":"AR","dest":"BL","count":13},{"year":"2015","origin":"AR","dest":"SH","count":6},{"year":"2015","origin":"AR","dest":"AR","count":948},{"year":"2015","origin":"AR","dest":"AI","count":138},{"year":"2015","origin":"AR","dest":"SG","count":1231},{"year":"2015","origin":"AR","dest":"GR","count":39},{"year":"2015","origin":"AR","dest":"AG","count":60},{"year":"2015","origin":"AR","dest":"TG","count":210},{"year":"2015","origin":"AR","dest":"TI","count":16},{"year":"2015","origin":"AR","dest":"VD","count":7},{"year":"2015","origin":"AR","dest":"VS","count":8},{"year":"2015","origin":"AR","dest":"NE","count":0},{"year":"2015","origin":"AR","dest":"GE","count":3},{"year":"2015","origin":"AR","dest":"JU","count":1},{"year":"2015","origin":"AI","dest":"ZH","count":50},{"year":"2015","origin":"AI","dest":"BE","count":12},{"year":"2015","origin":"AI","dest":"LU","count":6},{"year":"2015","origin":"AI","dest":"UR","count":1},{"year":"2015","origin":"AI","dest":"SZ","count":0},{"year":"2015","origin":"AI","dest":"OW","count":2},{"year":"2015","origin":"AI","dest":"NW","count":0},{"year":"2015","origin":"AI","dest":"GL","count":1},{"year":"2015","origin":"AI","dest":"ZG","count":2},{"year":"2015","origin":"AI","dest":"FR","count":0},{"year":"2015","origin":"AI","dest":"SO","count":0},{"year":"2015","origin":"AI","dest":"BS","count":0},{"year":"2015","origin":"AI","dest":"BL","count":5},{"year":"2015","origin":"AI","dest":"SH","count":1},{"year":"2015","origin":"AI","dest":"AR","count":113},{"year":"2015","origin":"AI","dest":"AI","count":358},{"year":"2015","origin":"AI","dest":"SG","count":166},{"year":"2015","origin":"AI","dest":"GR","count":8},{"year":"2015","origin":"AI","dest":"AG","count":11},{"year":"2015","origin":"AI","dest":"TG","count":25},{"year":"2015","origin":"AI","dest":"TI","count":3},{"year":"2015","origin":"AI","dest":"VD","count":2},{"year":"2015","origin":"AI","dest":"VS","count":5},{"year":"2015","origin":"AI","dest":"NE","count":2},{"year":"2015","origin":"AI","dest":"GE","count":0},{"year":"2015","origin":"AI","dest":"JU","count":0},{"year":"2015","origin":"SG","dest":"ZH","count":3226},{"year":"2015","origin":"SG","dest":"BE","count":426},{"year":"2015","origin":"SG","dest":"LU","count":284},{"year":"2015","origin":"SG","dest":"UR","count":30},{"year":"2015","origin":"SG","dest":"SZ","count":498},{"year":"2015","origin":"SG","dest":"OW","count":16},{"year":"2015","origin":"SG","dest":"NW","count":23},{"year":"2015","origin":"SG","dest":"GL","count":256},{"year":"2015","origin":"SG","dest":"ZG","count":117},{"year":"2015","origin":"SG","dest":"FR","count":39},{"year":"2015","origin":"SG","dest":"SO","count":132},{"year":"2015","origin":"SG","dest":"BS","count":113},{"year":"2015","origin":"SG","dest":"BL","count":104},{"year":"2015","origin":"SG","dest":"SH","count":81},{"year":"2015","origin":"SG","dest":"AR","count":1421},{"year":"2015","origin":"SG","dest":"AI","count":156},{"year":"2015","origin":"SG","dest":"SG","count":16989},{"year":"2015","origin":"SG","dest":"GR","count":741},{"year":"2015","origin":"SG","dest":"AG","count":465},{"year":"2015","origin":"SG","dest":"TG","count":2574},{"year":"2015","origin":"SG","dest":"TI","count":87},{"year":"2015","origin":"SG","dest":"VD","count":70},{"year":"2015","origin":"SG","dest":"VS","count":65},{"year":"2015","origin":"SG","dest":"NE","count":14},{"year":"2015","origin":"SG","dest":"GE","count":30},{"year":"2015","origin":"SG","dest":"JU","count":10},{"year":"2015","origin":"GR","dest":"ZH","count":1355},{"year":"2015","origin":"GR","dest":"BE","count":423},{"year":"2015","origin":"GR","dest":"LU","count":227},{"year":"2015","origin":"GR","dest":"UR","count":31},{"year":"2015","origin":"GR","dest":"SZ","count":109},{"year":"2015","origin":"GR","dest":"OW","count":24},{"year":"2015","origin":"GR","dest":"NW","count":32},{"year":"2015","origin":"GR","dest":"GL","count":52},{"year":"2015","origin":"GR","dest":"ZG","count":69},{"year":"2015","origin":"GR","dest":"FR","count":33},{"year":"2015","origin":"GR","dest":"SO","count":66},{"year":"2015","origin":"GR","dest":"BS","count":58},{"year":"2015","origin":"GR","dest":"BL","count":70},{"year":"2015","origin":"GR","dest":"SH","count":25},{"year":"2015","origin":"GR","dest":"AR","count":36},{"year":"2015","origin":"GR","dest":"AI","count":16},{"year":"2015","origin":"GR","dest":"SG","count":788},{"year":"2015","origin":"GR","dest":"GR","count":7254},{"year":"2015","origin":"GR","dest":"AG","count":234},{"year":"2015","origin":"GR","dest":"TG","count":139},{"year":"2015","origin":"GR","dest":"TI","count":299},{"year":"2015","origin":"GR","dest":"VD","count":38},{"year":"2015","origin":"GR","dest":"VS","count":44},{"year":"2015","origin":"GR","dest":"NE","count":3},{"year":"2015","origin":"GR","dest":"GE","count":20},{"year":"2015","origin":"GR","dest":"JU","count":1},{"year":"2015","origin":"AG","dest":"ZH","count":4711},{"year":"2015","origin":"AG","dest":"BE","count":1204},{"year":"2015","origin":"AG","dest":"LU","count":1479},{"year":"2015","origin":"AG","dest":"UR","count":16},{"year":"2015","origin":"AG","dest":"SZ","count":281},{"year":"2015","origin":"AG","dest":"OW","count":64},{"year":"2015","origin":"AG","dest":"NW","count":54},{"year":"2015","origin":"AG","dest":"GL","count":43},{"year":"2015","origin":"AG","dest":"ZG","count":421},{"year":"2015","origin":"AG","dest":"FR","count":73},{"year":"2015","origin":"AG","dest":"SO","count":1614},{"year":"2015","origin":"AG","dest":"BS","count":527},{"year":"2015","origin":"AG","dest":"BL","count":1041},{"year":"2015","origin":"AG","dest":"SH","count":114},{"year":"2015","origin":"AG","dest":"AR","count":40},{"year":"2015","origin":"AG","dest":"AI","count":10},{"year":"2015","origin":"AG","dest":"SG","count":345},{"year":"2015","origin":"AG","dest":"GR","count":231},{"year":"2015","origin":"AG","dest":"AG","count":30488},{"year":"2015","origin":"AG","dest":"TG","count":281},{"year":"2015","origin":"AG","dest":"TI","count":93},{"year":"2015","origin":"AG","dest":"VD","count":115},{"year":"2015","origin":"AG","dest":"VS","count":79},{"year":"2015","origin":"AG","dest":"NE","count":27},{"year":"2015","origin":"AG","dest":"GE","count":29},{"year":"2015","origin":"AG","dest":"JU","count":20},{"year":"2015","origin":"TG","dest":"ZH","count":2101},{"year":"2015","origin":"TG","dest":"BE","count":210},{"year":"2015","origin":"TG","dest":"LU","count":113},{"year":"2015","origin":"TG","dest":"UR","count":12},{"year":"2015","origin":"TG","dest":"SZ","count":78},{"year":"2015","origin":"TG","dest":"OW","count":10},{"year":"2015","origin":"TG","dest":"NW","count":6},{"year":"2015","origin":"TG","dest":"GL","count":14},{"year":"2015","origin":"TG","dest":"ZG","count":57},{"year":"2015","origin":"TG","dest":"FR","count":51},{"year":"2015","origin":"TG","dest":"SO","count":98},{"year":"2015","origin":"TG","dest":"BS","count":63},{"year":"2015","origin":"TG","dest":"BL","count":67},{"year":"2015","origin":"TG","dest":"SH","count":279},{"year":"2015","origin":"TG","dest":"AR","count":188},{"year":"2015","origin":"TG","dest":"AI","count":32},{"year":"2015","origin":"TG","dest":"SG","count":2146},{"year":"2015","origin":"TG","dest":"GR","count":133},{"year":"2015","origin":"TG","dest":"AG","count":264},{"year":"2015","origin":"TG","dest":"TG","count":10156},{"year":"2015","origin":"TG","dest":"TI","count":38},{"year":"2015","origin":"TG","dest":"VD","count":24},{"year":"2015","origin":"TG","dest":"VS","count":23},{"year":"2015","origin":"TG","dest":"NE","count":8},{"year":"2015","origin":"TG","dest":"GE","count":6},{"year":"2015","origin":"TG","dest":"JU","count":2},{"year":"2015","origin":"TI","dest":"ZH","count":794},{"year":"2015","origin":"TI","dest":"BE","count":220},{"year":"2015","origin":"TI","dest":"LU","count":119},{"year":"2015","origin":"TI","dest":"UR","count":18},{"year":"2015","origin":"TI","dest":"SZ","count":55},{"year":"2015","origin":"TI","dest":"OW","count":2},{"year":"2015","origin":"TI","dest":"NW","count":7},{"year":"2015","origin":"TI","dest":"GL","count":12},{"year":"2015","origin":"TI","dest":"ZG","count":59},{"year":"2015","origin":"TI","dest":"FR","count":99},{"year":"2015","origin":"TI","dest":"SO","count":51},{"year":"2015","origin":"TI","dest":"BS","count":61},{"year":"2015","origin":"TI","dest":"BL","count":37},{"year":"2015","origin":"TI","dest":"SH","count":16},{"year":"2015","origin":"TI","dest":"AR","count":5},{"year":"2015","origin":"TI","dest":"AI","count":5},{"year":"2015","origin":"TI","dest":"SG","count":110},{"year":"2015","origin":"TI","dest":"GR","count":393},{"year":"2015","origin":"TI","dest":"AG","count":164},{"year":"2015","origin":"TI","dest":"TG","count":50},{"year":"2015","origin":"TI","dest":"TI","count":16069},{"year":"2015","origin":"TI","dest":"VD","count":271},{"year":"2015","origin":"TI","dest":"VS","count":100},{"year":"2015","origin":"TI","dest":"NE","count":36},{"year":"2015","origin":"TI","dest":"GE","count":109},{"year":"2015","origin":"TI","dest":"JU","count":9},{"year":"2015","origin":"VD","dest":"ZH","count":845},{"year":"2015","origin":"VD","dest":"BE","count":712},{"year":"2015","origin":"VD","dest":"LU","count":80},{"year":"2015","origin":"VD","dest":"UR","count":3},{"year":"2015","origin":"VD","dest":"SZ","count":64},{"year":"2015","origin":"VD","dest":"OW","count":11},{"year":"2015","origin":"VD","dest":"NW","count":11},{"year":"2015","origin":"VD","dest":"GL","count":5},{"year":"2015","origin":"VD","dest":"ZG","count":152},{"year":"2015","origin":"VD","dest":"FR","count":3205},{"year":"2015","origin":"VD","dest":"SO","count":79},{"year":"2015","origin":"VD","dest":"BS","count":107},{"year":"2015","origin":"VD","dest":"BL","count":43},{"year":"2015","origin":"VD","dest":"SH","count":15},{"year":"2015","origin":"VD","dest":"AR","count":4},{"year":"2015","origin":"VD","dest":"AI","count":7},{"year":"2015","origin":"VD","dest":"SG","count":53},{"year":"2015","origin":"VD","dest":"GR","count":33},{"year":"2015","origin":"VD","dest":"AG","count":129},{"year":"2015","origin":"VD","dest":"TG","count":18},{"year":"2015","origin":"VD","dest":"TI","count":158},{"year":"2015","origin":"VD","dest":"VD","count":45028},{"year":"2015","origin":"VD","dest":"VS","count":2454},{"year":"2015","origin":"VD","dest":"NE","count":858},{"year":"2015","origin":"VD","dest":"GE","count":1787},{"year":"2015","origin":"VD","dest":"JU","count":145},{"year":"2015","origin":"VS","dest":"ZH","count":337},{"year":"2015","origin":"VS","dest":"BE","count":616},{"year":"2015","origin":"VS","dest":"LU","count":153},{"year":"2015","origin":"VS","dest":"UR","count":18},{"year":"2015","origin":"VS","dest":"SZ","count":45},{"year":"2015","origin":"VS","dest":"OW","count":12},{"year":"2015","origin":"VS","dest":"NW","count":12},{"year":"2015","origin":"VS","dest":"GL","count":7},{"year":"2015","origin":"VS","dest":"ZG","count":23},{"year":"2015","origin":"VS","dest":"FR","count":390},{"year":"2015","origin":"VS","dest":"SO","count":79},{"year":"2015","origin":"VS","dest":"BS","count":43},{"year":"2015","origin":"VS","dest":"BL","count":53},{"year":"2015","origin":"VS","dest":"SH","count":11},{"year":"2015","origin":"VS","dest":"AR","count":6},{"year":"2015","origin":"VS","dest":"AI","count":6},{"year":"2015","origin":"VS","dest":"SG","count":53},{"year":"2015","origin":"VS","dest":"GR","count":52},{"year":"2015","origin":"VS","dest":"AG","count":108},{"year":"2015","origin":"VS","dest":"TG","count":31},{"year":"2015","origin":"VS","dest":"TI","count":56},{"year":"2015","origin":"VS","dest":"VD","count":1640},{"year":"2015","origin":"VS","dest":"VS","count":14500},{"year":"2015","origin":"VS","dest":"NE","count":170},{"year":"2015","origin":"VS","dest":"GE","count":271},{"year":"2015","origin":"VS","dest":"JU","count":43},{"year":"2015","origin":"NE","dest":"ZH","count":114},{"year":"2015","origin":"NE","dest":"BE","count":918},{"year":"2015","origin":"NE","dest":"LU","count":14},{"year":"2015","origin":"NE","dest":"UR","count":0},{"year":"2015","origin":"NE","dest":"SZ","count":5},{"year":"2015","origin":"NE","dest":"OW","count":0},{"year":"2015","origin":"NE","dest":"NW","count":1},{"year":"2015","origin":"NE","dest":"GL","count":0},{"year":"2015","origin":"NE","dest":"ZG","count":20},{"year":"2015","origin":"NE","dest":"FR","count":383},{"year":"2015","origin":"NE","dest":"SO","count":28},{"year":"2015","origin":"NE","dest":"BS","count":22},{"year":"2015","origin":"NE","dest":"BL","count":7},{"year":"2015","origin":"NE","dest":"SH","count":7},{"year":"2015","origin":"NE","dest":"AR","count":1},{"year":"2015","origin":"NE","dest":"AI","count":0},{"year":"2015","origin":"NE","dest":"SG","count":11},{"year":"2015","origin":"NE","dest":"GR","count":7},{"year":"2015","origin":"NE","dest":"AG","count":23},{"year":"2015","origin":"NE","dest":"TG","count":3},{"year":"2015","origin":"NE","dest":"TI","count":42},{"year":"2015","origin":"NE","dest":"VD","count":1095},{"year":"2015","origin":"NE","dest":"VS","count":199},{"year":"2015","origin":"NE","dest":"NE","count":7369},{"year":"2015","origin":"NE","dest":"GE","count":173},{"year":"2015","origin":"NE","dest":"JU","count":170},{"year":"2015","origin":"GE","dest":"ZH","count":413},{"year":"2015","origin":"GE","dest":"BE","count":206},{"year":"2015","origin":"GE","dest":"LU","count":20},{"year":"2015","origin":"GE","dest":"UR","count":2},{"year":"2015","origin":"GE","dest":"SZ","count":29},{"year":"2015","origin":"GE","dest":"OW","count":1},{"year":"2015","origin":"GE","dest":"NW","count":3},{"year":"2015","origin":"GE","dest":"GL","count":0},{"year":"2015","origin":"GE","dest":"ZG","count":66},{"year":"2015","origin":"GE","dest":"FR","count":238},{"year":"2015","origin":"GE","dest":"SO","count":10},{"year":"2015","origin":"GE","dest":"BS","count":80},{"year":"2015","origin":"GE","dest":"BL","count":7},{"year":"2015","origin":"GE","dest":"SH","count":5},{"year":"2015","origin":"GE","dest":"AR","count":1},{"year":"2015","origin":"GE","dest":"AI","count":0},{"year":"2015","origin":"GE","dest":"SG","count":29},{"year":"2015","origin":"GE","dest":"GR","count":19},{"year":"2015","origin":"GE","dest":"AG","count":41},{"year":"2015","origin":"GE","dest":"TG","count":6},{"year":"2015","origin":"GE","dest":"TI","count":88},{"year":"2015","origin":"GE","dest":"VD","count":2809},{"year":"2015","origin":"GE","dest":"VS","count":433},{"year":"2015","origin":"GE","dest":"NE","count":172},{"year":"2015","origin":"GE","dest":"GE","count":26862},{"year":"2015","origin":"GE","dest":"JU","count":30},{"year":"2015","origin":"JU","dest":"ZH","count":34},{"year":"2015","origin":"JU","dest":"BE","count":318},{"year":"2015","origin":"JU","dest":"LU","count":7},{"year":"2015","origin":"JU","dest":"UR","count":0},{"year":"2015","origin":"JU","dest":"SZ","count":2},{"year":"2015","origin":"JU","dest":"OW","count":0},{"year":"2015","origin":"JU","dest":"NW","count":0},{"year":"2015","origin":"JU","dest":"GL","count":0},{"year":"2015","origin":"JU","dest":"ZG","count":6},{"year":"2015","origin":"JU","dest":"FR","count":79},{"year":"2015","origin":"JU","dest":"SO","count":30},{"year":"2015","origin":"JU","dest":"BS","count":22},{"year":"2015","origin":"JU","dest":"BL","count":24},{"year":"2015","origin":"JU","dest":"SH","count":2},{"year":"2015","origin":"JU","dest":"AR","count":0},{"year":"2015","origin":"JU","dest":"AI","count":0},{"year":"2015","origin":"JU","dest":"SG","count":4},{"year":"2015","origin":"JU","dest":"GR","count":2},{"year":"2015","origin":"JU","dest":"AG","count":13},{"year":"2015","origin":"JU","dest":"TG","count":1},{"year":"2015","origin":"JU","dest":"TI","count":11},{"year":"2015","origin":"JU","dest":"VD","count":205},{"year":"2015","origin":"JU","dest":"VS","count":89},{"year":"2015","origin":"JU","dest":"NE","count":195},{"year":"2015","origin":"JU","dest":"GE","count":45},{"year":"2015","origin":"JU","dest":"JU","count":2982}] ================================================ FILE: examples/public/data/flows-2016.json ================================================ [{"year":"2016","origin":"ZH","dest":"ZH","count":66855},{"year":"2016","origin":"ZH","dest":"BE","count":1673},{"year":"2016","origin":"ZH","dest":"LU","count":1017},{"year":"2016","origin":"ZH","dest":"UR","count":84},{"year":"2016","origin":"ZH","dest":"SZ","count":1704},{"year":"2016","origin":"ZH","dest":"OW","count":70},{"year":"2016","origin":"ZH","dest":"NW","count":94},{"year":"2016","origin":"ZH","dest":"GL","count":250},{"year":"2016","origin":"ZH","dest":"ZG","count":1246},{"year":"2016","origin":"ZH","dest":"FR","count":173},{"year":"2016","origin":"ZH","dest":"SO","count":547},{"year":"2016","origin":"ZH","dest":"BS","count":540},{"year":"2016","origin":"ZH","dest":"BL","count":409},{"year":"2016","origin":"ZH","dest":"SH","count":996},{"year":"2016","origin":"ZH","dest":"AR","count":167},{"year":"2016","origin":"ZH","dest":"AI","count":50},{"year":"2016","origin":"ZH","dest":"SG","count":2444},{"year":"2016","origin":"ZH","dest":"GR","count":987},{"year":"2016","origin":"ZH","dest":"AG","count":6143},{"year":"2016","origin":"ZH","dest":"TG","count":2475},{"year":"2016","origin":"ZH","dest":"TI","count":445},{"year":"2016","origin":"ZH","dest":"VD","count":511},{"year":"2016","origin":"ZH","dest":"VS","count":179},{"year":"2016","origin":"ZH","dest":"NE","count":58},{"year":"2016","origin":"ZH","dest":"GE","count":226},{"year":"2016","origin":"ZH","dest":"JU","count":27},{"year":"2016","origin":"BE","dest":"ZH","count":2097},{"year":"2016","origin":"BE","dest":"BE","count":52112},{"year":"2016","origin":"BE","dest":"LU","count":815},{"year":"2016","origin":"BE","dest":"UR","count":33},{"year":"2016","origin":"BE","dest":"SZ","count":129},{"year":"2016","origin":"BE","dest":"OW","count":85},{"year":"2016","origin":"BE","dest":"NW","count":40},{"year":"2016","origin":"BE","dest":"GL","count":27},{"year":"2016","origin":"BE","dest":"ZG","count":177},{"year":"2016","origin":"BE","dest":"FR","count":1353},{"year":"2016","origin":"BE","dest":"SO","count":2405},{"year":"2016","origin":"BE","dest":"BS","count":324},{"year":"2016","origin":"BE","dest":"BL","count":347},{"year":"2016","origin":"BE","dest":"SH","count":52},{"year":"2016","origin":"BE","dest":"AR","count":23},{"year":"2016","origin":"BE","dest":"AI","count":7},{"year":"2016","origin":"BE","dest":"SG","count":314},{"year":"2016","origin":"BE","dest":"GR","count":290},{"year":"2016","origin":"BE","dest":"AG","count":1124},{"year":"2016","origin":"BE","dest":"TG","count":198},{"year":"2016","origin":"BE","dest":"TI","count":148},{"year":"2016","origin":"BE","dest":"VD","count":658},{"year":"2016","origin":"BE","dest":"VS","count":422},{"year":"2016","origin":"BE","dest":"NE","count":561},{"year":"2016","origin":"BE","dest":"GE","count":156},{"year":"2016","origin":"BE","dest":"JU","count":447},{"year":"2016","origin":"LU","dest":"ZH","count":1530},{"year":"2016","origin":"LU","dest":"BE","count":899},{"year":"2016","origin":"LU","dest":"LU","count":17371},{"year":"2016","origin":"LU","dest":"UR","count":122},{"year":"2016","origin":"LU","dest":"SZ","count":452},{"year":"2016","origin":"LU","dest":"OW","count":282},{"year":"2016","origin":"LU","dest":"NW","count":506},{"year":"2016","origin":"LU","dest":"GL","count":30},{"year":"2016","origin":"LU","dest":"ZG","count":679},{"year":"2016","origin":"LU","dest":"FR","count":50},{"year":"2016","origin":"LU","dest":"SO","count":331},{"year":"2016","origin":"LU","dest":"BS","count":143},{"year":"2016","origin":"LU","dest":"BL","count":112},{"year":"2016","origin":"LU","dest":"SH","count":41},{"year":"2016","origin":"LU","dest":"AR","count":17},{"year":"2016","origin":"LU","dest":"AI","count":2},{"year":"2016","origin":"LU","dest":"SG","count":204},{"year":"2016","origin":"LU","dest":"GR","count":148},{"year":"2016","origin":"LU","dest":"AG","count":1512},{"year":"2016","origin":"LU","dest":"TG","count":90},{"year":"2016","origin":"LU","dest":"TI","count":88},{"year":"2016","origin":"LU","dest":"VD","count":44},{"year":"2016","origin":"LU","dest":"VS","count":156},{"year":"2016","origin":"LU","dest":"NE","count":7},{"year":"2016","origin":"LU","dest":"GE","count":24},{"year":"2016","origin":"LU","dest":"JU","count":9},{"year":"2016","origin":"UR","dest":"ZH","count":110},{"year":"2016","origin":"UR","dest":"BE","count":40},{"year":"2016","origin":"UR","dest":"LU","count":108},{"year":"2016","origin":"UR","dest":"UR","count":1346},{"year":"2016","origin":"UR","dest":"SZ","count":94},{"year":"2016","origin":"UR","dest":"OW","count":30},{"year":"2016","origin":"UR","dest":"NW","count":29},{"year":"2016","origin":"UR","dest":"GL","count":9},{"year":"2016","origin":"UR","dest":"ZG","count":29},{"year":"2016","origin":"UR","dest":"FR","count":4},{"year":"2016","origin":"UR","dest":"SO","count":11},{"year":"2016","origin":"UR","dest":"BS","count":7},{"year":"2016","origin":"UR","dest":"BL","count":14},{"year":"2016","origin":"UR","dest":"SH","count":1},{"year":"2016","origin":"UR","dest":"AR","count":0},{"year":"2016","origin":"UR","dest":"AI","count":1},{"year":"2016","origin":"UR","dest":"SG","count":26},{"year":"2016","origin":"UR","dest":"GR","count":34},{"year":"2016","origin":"UR","dest":"AG","count":31},{"year":"2016","origin":"UR","dest":"TG","count":10},{"year":"2016","origin":"UR","dest":"TI","count":6},{"year":"2016","origin":"UR","dest":"VD","count":1},{"year":"2016","origin":"UR","dest":"VS","count":16},{"year":"2016","origin":"UR","dest":"NE","count":1},{"year":"2016","origin":"UR","dest":"GE","count":1},{"year":"2016","origin":"UR","dest":"JU","count":0},{"year":"2016","origin":"SZ","dest":"ZH","count":1428},{"year":"2016","origin":"SZ","dest":"BE","count":123},{"year":"2016","origin":"SZ","dest":"LU","count":507},{"year":"2016","origin":"SZ","dest":"UR","count":102},{"year":"2016","origin":"SZ","dest":"SZ","count":4210},{"year":"2016","origin":"SZ","dest":"OW","count":29},{"year":"2016","origin":"SZ","dest":"NW","count":40},{"year":"2016","origin":"SZ","dest":"GL","count":129},{"year":"2016","origin":"SZ","dest":"ZG","count":381},{"year":"2016","origin":"SZ","dest":"FR","count":7},{"year":"2016","origin":"SZ","dest":"SO","count":56},{"year":"2016","origin":"SZ","dest":"BS","count":33},{"year":"2016","origin":"SZ","dest":"BL","count":21},{"year":"2016","origin":"SZ","dest":"SH","count":18},{"year":"2016","origin":"SZ","dest":"AR","count":14},{"year":"2016","origin":"SZ","dest":"AI","count":9},{"year":"2016","origin":"SZ","dest":"SG","count":497},{"year":"2016","origin":"SZ","dest":"GR","count":129},{"year":"2016","origin":"SZ","dest":"AG","count":330},{"year":"2016","origin":"SZ","dest":"TG","count":94},{"year":"2016","origin":"SZ","dest":"TI","count":43},{"year":"2016","origin":"SZ","dest":"VD","count":53},{"year":"2016","origin":"SZ","dest":"VS","count":23},{"year":"2016","origin":"SZ","dest":"NE","count":10},{"year":"2016","origin":"SZ","dest":"GE","count":13},{"year":"2016","origin":"SZ","dest":"JU","count":3},{"year":"2016","origin":"OW","dest":"ZH","count":107},{"year":"2016","origin":"OW","dest":"BE","count":101},{"year":"2016","origin":"OW","dest":"LU","count":344},{"year":"2016","origin":"OW","dest":"UR","count":31},{"year":"2016","origin":"OW","dest":"SZ","count":26},{"year":"2016","origin":"OW","dest":"OW","count":992},{"year":"2016","origin":"OW","dest":"NW","count":163},{"year":"2016","origin":"OW","dest":"GL","count":2},{"year":"2016","origin":"OW","dest":"ZG","count":30},{"year":"2016","origin":"OW","dest":"FR","count":6},{"year":"2016","origin":"OW","dest":"SO","count":19},{"year":"2016","origin":"OW","dest":"BS","count":7},{"year":"2016","origin":"OW","dest":"BL","count":16},{"year":"2016","origin":"OW","dest":"SH","count":1},{"year":"2016","origin":"OW","dest":"AR","count":6},{"year":"2016","origin":"OW","dest":"AI","count":1},{"year":"2016","origin":"OW","dest":"SG","count":11},{"year":"2016","origin":"OW","dest":"GR","count":18},{"year":"2016","origin":"OW","dest":"AG","count":49},{"year":"2016","origin":"OW","dest":"TG","count":5},{"year":"2016","origin":"OW","dest":"TI","count":9},{"year":"2016","origin":"OW","dest":"VD","count":3},{"year":"2016","origin":"OW","dest":"VS","count":4},{"year":"2016","origin":"OW","dest":"NE","count":0},{"year":"2016","origin":"OW","dest":"GE","count":3},{"year":"2016","origin":"OW","dest":"JU","count":0},{"year":"2016","origin":"NW","dest":"ZH","count":132},{"year":"2016","origin":"NW","dest":"BE","count":57},{"year":"2016","origin":"NW","dest":"LU","count":519},{"year":"2016","origin":"NW","dest":"UR","count":58},{"year":"2016","origin":"NW","dest":"SZ","count":45},{"year":"2016","origin":"NW","dest":"OW","count":187},{"year":"2016","origin":"NW","dest":"NW","count":1259},{"year":"2016","origin":"NW","dest":"GL","count":8},{"year":"2016","origin":"NW","dest":"ZG","count":61},{"year":"2016","origin":"NW","dest":"FR","count":10},{"year":"2016","origin":"NW","dest":"SO","count":14},{"year":"2016","origin":"NW","dest":"BS","count":13},{"year":"2016","origin":"NW","dest":"BL","count":10},{"year":"2016","origin":"NW","dest":"SH","count":4},{"year":"2016","origin":"NW","dest":"AR","count":0},{"year":"2016","origin":"NW","dest":"AI","count":0},{"year":"2016","origin":"NW","dest":"SG","count":18},{"year":"2016","origin":"NW","dest":"GR","count":23},{"year":"2016","origin":"NW","dest":"AG","count":70},{"year":"2016","origin":"NW","dest":"TG","count":13},{"year":"2016","origin":"NW","dest":"TI","count":6},{"year":"2016","origin":"NW","dest":"VD","count":7},{"year":"2016","origin":"NW","dest":"VS","count":8},{"year":"2016","origin":"NW","dest":"NE","count":0},{"year":"2016","origin":"NW","dest":"GE","count":1},{"year":"2016","origin":"NW","dest":"JU","count":2},{"year":"2016","origin":"GL","dest":"ZH","count":268},{"year":"2016","origin":"GL","dest":"BE","count":38},{"year":"2016","origin":"GL","dest":"LU","count":25},{"year":"2016","origin":"GL","dest":"UR","count":1},{"year":"2016","origin":"GL","dest":"SZ","count":141},{"year":"2016","origin":"GL","dest":"OW","count":3},{"year":"2016","origin":"GL","dest":"NW","count":1},{"year":"2016","origin":"GL","dest":"GL","count":962},{"year":"2016","origin":"GL","dest":"ZG","count":6},{"year":"2016","origin":"GL","dest":"FR","count":8},{"year":"2016","origin":"GL","dest":"SO","count":29},{"year":"2016","origin":"GL","dest":"BS","count":4},{"year":"2016","origin":"GL","dest":"BL","count":13},{"year":"2016","origin":"GL","dest":"SH","count":9},{"year":"2016","origin":"GL","dest":"AR","count":0},{"year":"2016","origin":"GL","dest":"AI","count":3},{"year":"2016","origin":"GL","dest":"SG","count":301},{"year":"2016","origin":"GL","dest":"GR","count":60},{"year":"2016","origin":"GL","dest":"AG","count":84},{"year":"2016","origin":"GL","dest":"TG","count":30},{"year":"2016","origin":"GL","dest":"TI","count":11},{"year":"2016","origin":"GL","dest":"VD","count":4},{"year":"2016","origin":"GL","dest":"VS","count":8},{"year":"2016","origin":"GL","dest":"NE","count":0},{"year":"2016","origin":"GL","dest":"GE","count":0},{"year":"2016","origin":"GL","dest":"JU","count":0},{"year":"2016","origin":"ZG","dest":"ZH","count":1218},{"year":"2016","origin":"ZG","dest":"BE","count":150},{"year":"2016","origin":"ZG","dest":"LU","count":746},{"year":"2016","origin":"ZG","dest":"UR","count":29},{"year":"2016","origin":"ZG","dest":"SZ","count":392},{"year":"2016","origin":"ZG","dest":"OW","count":31},{"year":"2016","origin":"ZG","dest":"NW","count":23},{"year":"2016","origin":"ZG","dest":"GL","count":10},{"year":"2016","origin":"ZG","dest":"ZG","count":4168},{"year":"2016","origin":"ZG","dest":"FR","count":18},{"year":"2016","origin":"ZG","dest":"SO","count":65},{"year":"2016","origin":"ZG","dest":"BS","count":51},{"year":"2016","origin":"ZG","dest":"BL","count":52},{"year":"2016","origin":"ZG","dest":"SH","count":16},{"year":"2016","origin":"ZG","dest":"AR","count":12},{"year":"2016","origin":"ZG","dest":"AI","count":2},{"year":"2016","origin":"ZG","dest":"SG","count":67},{"year":"2016","origin":"ZG","dest":"GR","count":41},{"year":"2016","origin":"ZG","dest":"AG","count":537},{"year":"2016","origin":"ZG","dest":"TG","count":31},{"year":"2016","origin":"ZG","dest":"TI","count":64},{"year":"2016","origin":"ZG","dest":"VD","count":67},{"year":"2016","origin":"ZG","dest":"VS","count":24},{"year":"2016","origin":"ZG","dest":"NE","count":3},{"year":"2016","origin":"ZG","dest":"GE","count":18},{"year":"2016","origin":"ZG","dest":"JU","count":5},{"year":"2016","origin":"FR","dest":"ZH","count":267},{"year":"2016","origin":"FR","dest":"BE","count":1522},{"year":"2016","origin":"FR","dest":"LU","count":55},{"year":"2016","origin":"FR","dest":"UR","count":1},{"year":"2016","origin":"FR","dest":"SZ","count":11},{"year":"2016","origin":"FR","dest":"OW","count":2},{"year":"2016","origin":"FR","dest":"NW","count":4},{"year":"2016","origin":"FR","dest":"GL","count":1},{"year":"2016","origin":"FR","dest":"ZG","count":30},{"year":"2016","origin":"FR","dest":"FR","count":15341},{"year":"2016","origin":"FR","dest":"SO","count":69},{"year":"2016","origin":"FR","dest":"BS","count":47},{"year":"2016","origin":"FR","dest":"BL","count":18},{"year":"2016","origin":"FR","dest":"SH","count":3},{"year":"2016","origin":"FR","dest":"AR","count":2},{"year":"2016","origin":"FR","dest":"AI","count":0},{"year":"2016","origin":"FR","dest":"SG","count":34},{"year":"2016","origin":"FR","dest":"GR","count":18},{"year":"2016","origin":"FR","dest":"AG","count":101},{"year":"2016","origin":"FR","dest":"TG","count":18},{"year":"2016","origin":"FR","dest":"TI","count":45},{"year":"2016","origin":"FR","dest":"VD","count":2178},{"year":"2016","origin":"FR","dest":"VS","count":391},{"year":"2016","origin":"FR","dest":"NE","count":231},{"year":"2016","origin":"FR","dest":"GE","count":164},{"year":"2016","origin":"FR","dest":"JU","count":52},{"year":"2016","origin":"SO","dest":"ZH","count":636},{"year":"2016","origin":"SO","dest":"BE","count":2197},{"year":"2016","origin":"SO","dest":"LU","count":321},{"year":"2016","origin":"SO","dest":"UR","count":9},{"year":"2016","origin":"SO","dest":"SZ","count":71},{"year":"2016","origin":"SO","dest":"OW","count":15},{"year":"2016","origin":"SO","dest":"NW","count":16},{"year":"2016","origin":"SO","dest":"GL","count":7},{"year":"2016","origin":"SO","dest":"ZG","count":54},{"year":"2016","origin":"SO","dest":"FR","count":78},{"year":"2016","origin":"SO","dest":"SO","count":10309},{"year":"2016","origin":"SO","dest":"BS","count":438},{"year":"2016","origin":"SO","dest":"BL","count":992},{"year":"2016","origin":"SO","dest":"SH","count":24},{"year":"2016","origin":"SO","dest":"AR","count":8},{"year":"2016","origin":"SO","dest":"AI","count":1},{"year":"2016","origin":"SO","dest":"SG","count":77},{"year":"2016","origin":"SO","dest":"GR","count":68},{"year":"2016","origin":"SO","dest":"AG","count":1470},{"year":"2016","origin":"SO","dest":"TG","count":65},{"year":"2016","origin":"SO","dest":"TI","count":35},{"year":"2016","origin":"SO","dest":"VD","count":50},{"year":"2016","origin":"SO","dest":"VS","count":63},{"year":"2016","origin":"SO","dest":"NE","count":17},{"year":"2016","origin":"SO","dest":"GE","count":13},{"year":"2016","origin":"SO","dest":"JU","count":28},{"year":"2016","origin":"BS","dest":"ZH","count":808},{"year":"2016","origin":"BS","dest":"BE","count":416},{"year":"2016","origin":"BS","dest":"LU","count":146},{"year":"2016","origin":"BS","dest":"UR","count":1},{"year":"2016","origin":"BS","dest":"SZ","count":44},{"year":"2016","origin":"BS","dest":"OW","count":10},{"year":"2016","origin":"BS","dest":"NW","count":11},{"year":"2016","origin":"BS","dest":"GL","count":6},{"year":"2016","origin":"BS","dest":"ZG","count":73},{"year":"2016","origin":"BS","dest":"FR","count":44},{"year":"2016","origin":"BS","dest":"SO","count":393},{"year":"2016","origin":"BS","dest":"BS","count":1280},{"year":"2016","origin":"BS","dest":"BL","count":3926},{"year":"2016","origin":"BS","dest":"SH","count":27},{"year":"2016","origin":"BS","dest":"AR","count":11},{"year":"2016","origin":"BS","dest":"AI","count":3},{"year":"2016","origin":"BS","dest":"SG","count":59},{"year":"2016","origin":"BS","dest":"GR","count":72},{"year":"2016","origin":"BS","dest":"AG","count":625},{"year":"2016","origin":"BS","dest":"TG","count":29},{"year":"2016","origin":"BS","dest":"TI","count":66},{"year":"2016","origin":"BS","dest":"VD","count":86},{"year":"2016","origin":"BS","dest":"VS","count":37},{"year":"2016","origin":"BS","dest":"NE","count":39},{"year":"2016","origin":"BS","dest":"GE","count":52},{"year":"2016","origin":"BS","dest":"JU","count":46},{"year":"2016","origin":"BL","dest":"ZH","count":573},{"year":"2016","origin":"BL","dest":"BE","count":443},{"year":"2016","origin":"BL","dest":"LU","count":187},{"year":"2016","origin":"BL","dest":"UR","count":2},{"year":"2016","origin":"BL","dest":"SZ","count":50},{"year":"2016","origin":"BL","dest":"OW","count":20},{"year":"2016","origin":"BL","dest":"NW","count":23},{"year":"2016","origin":"BL","dest":"GL","count":4},{"year":"2016","origin":"BL","dest":"ZG","count":57},{"year":"2016","origin":"BL","dest":"FR","count":31},{"year":"2016","origin":"BL","dest":"SO","count":1200},{"year":"2016","origin":"BL","dest":"BS","count":2447},{"year":"2016","origin":"BL","dest":"BL","count":10269},{"year":"2016","origin":"BL","dest":"SH","count":16},{"year":"2016","origin":"BL","dest":"AR","count":11},{"year":"2016","origin":"BL","dest":"AI","count":1},{"year":"2016","origin":"BL","dest":"SG","count":61},{"year":"2016","origin":"BL","dest":"GR","count":71},{"year":"2016","origin":"BL","dest":"AG","count":1160},{"year":"2016","origin":"BL","dest":"TG","count":42},{"year":"2016","origin":"BL","dest":"TI","count":34},{"year":"2016","origin":"BL","dest":"VD","count":57},{"year":"2016","origin":"BL","dest":"VS","count":39},{"year":"2016","origin":"BL","dest":"NE","count":3},{"year":"2016","origin":"BL","dest":"GE","count":10},{"year":"2016","origin":"BL","dest":"JU","count":62},{"year":"2016","origin":"SH","dest":"ZH","count":970},{"year":"2016","origin":"SH","dest":"BE","count":75},{"year":"2016","origin":"SH","dest":"LU","count":33},{"year":"2016","origin":"SH","dest":"UR","count":2},{"year":"2016","origin":"SH","dest":"SZ","count":26},{"year":"2016","origin":"SH","dest":"OW","count":1},{"year":"2016","origin":"SH","dest":"NW","count":2},{"year":"2016","origin":"SH","dest":"GL","count":6},{"year":"2016","origin":"SH","dest":"ZG","count":18},{"year":"2016","origin":"SH","dest":"FR","count":7},{"year":"2016","origin":"SH","dest":"SO","count":24},{"year":"2016","origin":"SH","dest":"BS","count":24},{"year":"2016","origin":"SH","dest":"BL","count":23},{"year":"2016","origin":"SH","dest":"SH","count":2334},{"year":"2016","origin":"SH","dest":"AR","count":28},{"year":"2016","origin":"SH","dest":"AI","count":1},{"year":"2016","origin":"SH","dest":"SG","count":66},{"year":"2016","origin":"SH","dest":"GR","count":35},{"year":"2016","origin":"SH","dest":"AG","count":103},{"year":"2016","origin":"SH","dest":"TG","count":303},{"year":"2016","origin":"SH","dest":"TI","count":6},{"year":"2016","origin":"SH","dest":"VD","count":10},{"year":"2016","origin":"SH","dest":"VS","count":5},{"year":"2016","origin":"SH","dest":"NE","count":4},{"year":"2016","origin":"SH","dest":"GE","count":7},{"year":"2016","origin":"SH","dest":"JU","count":0},{"year":"2016","origin":"AR","dest":"ZH","count":263},{"year":"2016","origin":"AR","dest":"BE","count":50},{"year":"2016","origin":"AR","dest":"LU","count":31},{"year":"2016","origin":"AR","dest":"UR","count":2},{"year":"2016","origin":"AR","dest":"SZ","count":11},{"year":"2016","origin":"AR","dest":"OW","count":5},{"year":"2016","origin":"AR","dest":"NW","count":3},{"year":"2016","origin":"AR","dest":"GL","count":2},{"year":"2016","origin":"AR","dest":"ZG","count":8},{"year":"2016","origin":"AR","dest":"FR","count":1},{"year":"2016","origin":"AR","dest":"SO","count":9},{"year":"2016","origin":"AR","dest":"BS","count":13},{"year":"2016","origin":"AR","dest":"BL","count":16},{"year":"2016","origin":"AR","dest":"SH","count":10},{"year":"2016","origin":"AR","dest":"AR","count":1005},{"year":"2016","origin":"AR","dest":"AI","count":105},{"year":"2016","origin":"AR","dest":"SG","count":1334},{"year":"2016","origin":"AR","dest":"GR","count":29},{"year":"2016","origin":"AR","dest":"AG","count":49},{"year":"2016","origin":"AR","dest":"TG","count":215},{"year":"2016","origin":"AR","dest":"TI","count":8},{"year":"2016","origin":"AR","dest":"VD","count":11},{"year":"2016","origin":"AR","dest":"VS","count":8},{"year":"2016","origin":"AR","dest":"NE","count":2},{"year":"2016","origin":"AR","dest":"GE","count":3},{"year":"2016","origin":"AR","dest":"JU","count":2},{"year":"2016","origin":"AI","dest":"ZH","count":39},{"year":"2016","origin":"AI","dest":"BE","count":12},{"year":"2016","origin":"AI","dest":"LU","count":10},{"year":"2016","origin":"AI","dest":"UR","count":0},{"year":"2016","origin":"AI","dest":"SZ","count":9},{"year":"2016","origin":"AI","dest":"OW","count":2},{"year":"2016","origin":"AI","dest":"NW","count":3},{"year":"2016","origin":"AI","dest":"GL","count":2},{"year":"2016","origin":"AI","dest":"ZG","count":2},{"year":"2016","origin":"AI","dest":"FR","count":1},{"year":"2016","origin":"AI","dest":"SO","count":1},{"year":"2016","origin":"AI","dest":"BS","count":4},{"year":"2016","origin":"AI","dest":"BL","count":1},{"year":"2016","origin":"AI","dest":"SH","count":2},{"year":"2016","origin":"AI","dest":"AR","count":115},{"year":"2016","origin":"AI","dest":"AI","count":335},{"year":"2016","origin":"AI","dest":"SG","count":164},{"year":"2016","origin":"AI","dest":"GR","count":14},{"year":"2016","origin":"AI","dest":"AG","count":9},{"year":"2016","origin":"AI","dest":"TG","count":32},{"year":"2016","origin":"AI","dest":"TI","count":5},{"year":"2016","origin":"AI","dest":"VD","count":0},{"year":"2016","origin":"AI","dest":"VS","count":2},{"year":"2016","origin":"AI","dest":"NE","count":0},{"year":"2016","origin":"AI","dest":"GE","count":0},{"year":"2016","origin":"AI","dest":"JU","count":0},{"year":"2016","origin":"SG","dest":"ZH","count":3327},{"year":"2016","origin":"SG","dest":"BE","count":443},{"year":"2016","origin":"SG","dest":"LU","count":242},{"year":"2016","origin":"SG","dest":"UR","count":9},{"year":"2016","origin":"SG","dest":"SZ","count":561},{"year":"2016","origin":"SG","dest":"OW","count":19},{"year":"2016","origin":"SG","dest":"NW","count":24},{"year":"2016","origin":"SG","dest":"GL","count":272},{"year":"2016","origin":"SG","dest":"ZG","count":130},{"year":"2016","origin":"SG","dest":"FR","count":47},{"year":"2016","origin":"SG","dest":"SO","count":142},{"year":"2016","origin":"SG","dest":"BS","count":111},{"year":"2016","origin":"SG","dest":"BL","count":96},{"year":"2016","origin":"SG","dest":"SH","count":91},{"year":"2016","origin":"SG","dest":"AR","count":1441},{"year":"2016","origin":"SG","dest":"AI","count":174},{"year":"2016","origin":"SG","dest":"SG","count":17683},{"year":"2016","origin":"SG","dest":"GR","count":775},{"year":"2016","origin":"SG","dest":"AG","count":430},{"year":"2016","origin":"SG","dest":"TG","count":2549},{"year":"2016","origin":"SG","dest":"TI","count":98},{"year":"2016","origin":"SG","dest":"VD","count":72},{"year":"2016","origin":"SG","dest":"VS","count":65},{"year":"2016","origin":"SG","dest":"NE","count":12},{"year":"2016","origin":"SG","dest":"GE","count":32},{"year":"2016","origin":"SG","dest":"JU","count":6},{"year":"2016","origin":"GR","dest":"ZH","count":1375},{"year":"2016","origin":"GR","dest":"BE","count":365},{"year":"2016","origin":"GR","dest":"LU","count":240},{"year":"2016","origin":"GR","dest":"UR","count":24},{"year":"2016","origin":"GR","dest":"SZ","count":115},{"year":"2016","origin":"GR","dest":"OW","count":30},{"year":"2016","origin":"GR","dest":"NW","count":13},{"year":"2016","origin":"GR","dest":"GL","count":47},{"year":"2016","origin":"GR","dest":"ZG","count":41},{"year":"2016","origin":"GR","dest":"FR","count":26},{"year":"2016","origin":"GR","dest":"SO","count":72},{"year":"2016","origin":"GR","dest":"BS","count":84},{"year":"2016","origin":"GR","dest":"BL","count":55},{"year":"2016","origin":"GR","dest":"SH","count":49},{"year":"2016","origin":"GR","dest":"AR","count":49},{"year":"2016","origin":"GR","dest":"AI","count":26},{"year":"2016","origin":"GR","dest":"SG","count":825},{"year":"2016","origin":"GR","dest":"GR","count":7267},{"year":"2016","origin":"GR","dest":"AG","count":277},{"year":"2016","origin":"GR","dest":"TG","count":155},{"year":"2016","origin":"GR","dest":"TI","count":321},{"year":"2016","origin":"GR","dest":"VD","count":31},{"year":"2016","origin":"GR","dest":"VS","count":42},{"year":"2016","origin":"GR","dest":"NE","count":2},{"year":"2016","origin":"GR","dest":"GE","count":12},{"year":"2016","origin":"GR","dest":"JU","count":2},{"year":"2016","origin":"AG","dest":"ZH","count":4546},{"year":"2016","origin":"AG","dest":"BE","count":1160},{"year":"2016","origin":"AG","dest":"LU","count":1372},{"year":"2016","origin":"AG","dest":"UR","count":21},{"year":"2016","origin":"AG","dest":"SZ","count":264},{"year":"2016","origin":"AG","dest":"OW","count":69},{"year":"2016","origin":"AG","dest":"NW","count":69},{"year":"2016","origin":"AG","dest":"GL","count":47},{"year":"2016","origin":"AG","dest":"ZG","count":415},{"year":"2016","origin":"AG","dest":"FR","count":62},{"year":"2016","origin":"AG","dest":"SO","count":1609},{"year":"2016","origin":"AG","dest":"BS","count":613},{"year":"2016","origin":"AG","dest":"BL","count":1021},{"year":"2016","origin":"AG","dest":"SH","count":128},{"year":"2016","origin":"AG","dest":"AR","count":37},{"year":"2016","origin":"AG","dest":"AI","count":6},{"year":"2016","origin":"AG","dest":"SG","count":405},{"year":"2016","origin":"AG","dest":"GR","count":223},{"year":"2016","origin":"AG","dest":"AG","count":31880},{"year":"2016","origin":"AG","dest":"TG","count":274},{"year":"2016","origin":"AG","dest":"TI","count":146},{"year":"2016","origin":"AG","dest":"VD","count":119},{"year":"2016","origin":"AG","dest":"VS","count":94},{"year":"2016","origin":"AG","dest":"NE","count":22},{"year":"2016","origin":"AG","dest":"GE","count":41},{"year":"2016","origin":"AG","dest":"JU","count":19},{"year":"2016","origin":"TG","dest":"ZH","count":2285},{"year":"2016","origin":"TG","dest":"BE","count":278},{"year":"2016","origin":"TG","dest":"LU","count":102},{"year":"2016","origin":"TG","dest":"UR","count":7},{"year":"2016","origin":"TG","dest":"SZ","count":99},{"year":"2016","origin":"TG","dest":"OW","count":3},{"year":"2016","origin":"TG","dest":"NW","count":6},{"year":"2016","origin":"TG","dest":"GL","count":22},{"year":"2016","origin":"TG","dest":"ZG","count":40},{"year":"2016","origin":"TG","dest":"FR","count":26},{"year":"2016","origin":"TG","dest":"SO","count":80},{"year":"2016","origin":"TG","dest":"BS","count":48},{"year":"2016","origin":"TG","dest":"BL","count":51},{"year":"2016","origin":"TG","dest":"SH","count":366},{"year":"2016","origin":"TG","dest":"AR","count":176},{"year":"2016","origin":"TG","dest":"AI","count":22},{"year":"2016","origin":"TG","dest":"SG","count":2061},{"year":"2016","origin":"TG","dest":"GR","count":132},{"year":"2016","origin":"TG","dest":"AG","count":256},{"year":"2016","origin":"TG","dest":"TG","count":9974},{"year":"2016","origin":"TG","dest":"TI","count":19},{"year":"2016","origin":"TG","dest":"VD","count":23},{"year":"2016","origin":"TG","dest":"VS","count":13},{"year":"2016","origin":"TG","dest":"NE","count":6},{"year":"2016","origin":"TG","dest":"GE","count":21},{"year":"2016","origin":"TG","dest":"JU","count":3},{"year":"2016","origin":"TI","dest":"ZH","count":877},{"year":"2016","origin":"TI","dest":"BE","count":230},{"year":"2016","origin":"TI","dest":"LU","count":130},{"year":"2016","origin":"TI","dest":"UR","count":22},{"year":"2016","origin":"TI","dest":"SZ","count":38},{"year":"2016","origin":"TI","dest":"OW","count":6},{"year":"2016","origin":"TI","dest":"NW","count":10},{"year":"2016","origin":"TI","dest":"GL","count":16},{"year":"2016","origin":"TI","dest":"ZG","count":57},{"year":"2016","origin":"TI","dest":"FR","count":84},{"year":"2016","origin":"TI","dest":"SO","count":39},{"year":"2016","origin":"TI","dest":"BS","count":58},{"year":"2016","origin":"TI","dest":"BL","count":56},{"year":"2016","origin":"TI","dest":"SH","count":12},{"year":"2016","origin":"TI","dest":"AR","count":8},{"year":"2016","origin":"TI","dest":"AI","count":4},{"year":"2016","origin":"TI","dest":"SG","count":92},{"year":"2016","origin":"TI","dest":"GR","count":333},{"year":"2016","origin":"TI","dest":"AG","count":174},{"year":"2016","origin":"TI","dest":"TG","count":46},{"year":"2016","origin":"TI","dest":"TI","count":16625},{"year":"2016","origin":"TI","dest":"VD","count":265},{"year":"2016","origin":"TI","dest":"VS","count":88},{"year":"2016","origin":"TI","dest":"NE","count":48},{"year":"2016","origin":"TI","dest":"GE","count":79},{"year":"2016","origin":"TI","dest":"JU","count":5},{"year":"2016","origin":"VD","dest":"ZH","count":820},{"year":"2016","origin":"VD","dest":"BE","count":690},{"year":"2016","origin":"VD","dest":"LU","count":80},{"year":"2016","origin":"VD","dest":"UR","count":5},{"year":"2016","origin":"VD","dest":"SZ","count":55},{"year":"2016","origin":"VD","dest":"OW","count":10},{"year":"2016","origin":"VD","dest":"NW","count":4},{"year":"2016","origin":"VD","dest":"GL","count":4},{"year":"2016","origin":"VD","dest":"ZG","count":112},{"year":"2016","origin":"VD","dest":"FR","count":3221},{"year":"2016","origin":"VD","dest":"SO","count":51},{"year":"2016","origin":"VD","dest":"BS","count":121},{"year":"2016","origin":"VD","dest":"BL","count":71},{"year":"2016","origin":"VD","dest":"SH","count":13},{"year":"2016","origin":"VD","dest":"AR","count":6},{"year":"2016","origin":"VD","dest":"AI","count":1},{"year":"2016","origin":"VD","dest":"SG","count":70},{"year":"2016","origin":"VD","dest":"GR","count":44},{"year":"2016","origin":"VD","dest":"AG","count":98},{"year":"2016","origin":"VD","dest":"TG","count":17},{"year":"2016","origin":"VD","dest":"TI","count":178},{"year":"2016","origin":"VD","dest":"VD","count":47540},{"year":"2016","origin":"VD","dest":"VS","count":2391},{"year":"2016","origin":"VD","dest":"NE","count":867},{"year":"2016","origin":"VD","dest":"GE","count":1656},{"year":"2016","origin":"VD","dest":"JU","count":151},{"year":"2016","origin":"VS","dest":"ZH","count":319},{"year":"2016","origin":"VS","dest":"BE","count":584},{"year":"2016","origin":"VS","dest":"LU","count":136},{"year":"2016","origin":"VS","dest":"UR","count":5},{"year":"2016","origin":"VS","dest":"SZ","count":38},{"year":"2016","origin":"VS","dest":"OW","count":22},{"year":"2016","origin":"VS","dest":"NW","count":18},{"year":"2016","origin":"VS","dest":"GL","count":9},{"year":"2016","origin":"VS","dest":"ZG","count":33},{"year":"2016","origin":"VS","dest":"FR","count":399},{"year":"2016","origin":"VS","dest":"SO","count":80},{"year":"2016","origin":"VS","dest":"BS","count":55},{"year":"2016","origin":"VS","dest":"BL","count":31},{"year":"2016","origin":"VS","dest":"SH","count":7},{"year":"2016","origin":"VS","dest":"AR","count":9},{"year":"2016","origin":"VS","dest":"AI","count":2},{"year":"2016","origin":"VS","dest":"SG","count":48},{"year":"2016","origin":"VS","dest":"GR","count":49},{"year":"2016","origin":"VS","dest":"AG","count":114},{"year":"2016","origin":"VS","dest":"TG","count":32},{"year":"2016","origin":"VS","dest":"TI","count":66},{"year":"2016","origin":"VS","dest":"VD","count":1652},{"year":"2016","origin":"VS","dest":"VS","count":14977},{"year":"2016","origin":"VS","dest":"NE","count":153},{"year":"2016","origin":"VS","dest":"GE","count":282},{"year":"2016","origin":"VS","dest":"JU","count":60},{"year":"2016","origin":"NE","dest":"ZH","count":115},{"year":"2016","origin":"NE","dest":"BE","count":889},{"year":"2016","origin":"NE","dest":"LU","count":16},{"year":"2016","origin":"NE","dest":"UR","count":2},{"year":"2016","origin":"NE","dest":"SZ","count":4},{"year":"2016","origin":"NE","dest":"OW","count":0},{"year":"2016","origin":"NE","dest":"NW","count":0},{"year":"2016","origin":"NE","dest":"GL","count":1},{"year":"2016","origin":"NE","dest":"ZG","count":11},{"year":"2016","origin":"NE","dest":"FR","count":461},{"year":"2016","origin":"NE","dest":"SO","count":34},{"year":"2016","origin":"NE","dest":"BS","count":30},{"year":"2016","origin":"NE","dest":"BL","count":9},{"year":"2016","origin":"NE","dest":"SH","count":0},{"year":"2016","origin":"NE","dest":"AR","count":2},{"year":"2016","origin":"NE","dest":"AI","count":0},{"year":"2016","origin":"NE","dest":"SG","count":10},{"year":"2016","origin":"NE","dest":"GR","count":3},{"year":"2016","origin":"NE","dest":"AG","count":20},{"year":"2016","origin":"NE","dest":"TG","count":1},{"year":"2016","origin":"NE","dest":"TI","count":48},{"year":"2016","origin":"NE","dest":"VD","count":1087},{"year":"2016","origin":"NE","dest":"VS","count":203},{"year":"2016","origin":"NE","dest":"NE","count":7237},{"year":"2016","origin":"NE","dest":"GE","count":148},{"year":"2016","origin":"NE","dest":"JU","count":202},{"year":"2016","origin":"GE","dest":"ZH","count":481},{"year":"2016","origin":"GE","dest":"BE","count":190},{"year":"2016","origin":"GE","dest":"LU","count":23},{"year":"2016","origin":"GE","dest":"UR","count":0},{"year":"2016","origin":"GE","dest":"SZ","count":44},{"year":"2016","origin":"GE","dest":"OW","count":2},{"year":"2016","origin":"GE","dest":"NW","count":3},{"year":"2016","origin":"GE","dest":"GL","count":1},{"year":"2016","origin":"GE","dest":"ZG","count":79},{"year":"2016","origin":"GE","dest":"FR","count":242},{"year":"2016","origin":"GE","dest":"SO","count":19},{"year":"2016","origin":"GE","dest":"BS","count":59},{"year":"2016","origin":"GE","dest":"BL","count":19},{"year":"2016","origin":"GE","dest":"SH","count":5},{"year":"2016","origin":"GE","dest":"AR","count":1},{"year":"2016","origin":"GE","dest":"AI","count":0},{"year":"2016","origin":"GE","dest":"SG","count":26},{"year":"2016","origin":"GE","dest":"GR","count":10},{"year":"2016","origin":"GE","dest":"AG","count":32},{"year":"2016","origin":"GE","dest":"TG","count":6},{"year":"2016","origin":"GE","dest":"TI","count":81},{"year":"2016","origin":"GE","dest":"VD","count":2619},{"year":"2016","origin":"GE","dest":"VS","count":511},{"year":"2016","origin":"GE","dest":"NE","count":177},{"year":"2016","origin":"GE","dest":"GE","count":29941},{"year":"2016","origin":"GE","dest":"JU","count":45},{"year":"2016","origin":"JU","dest":"ZH","count":38},{"year":"2016","origin":"JU","dest":"BE","count":353},{"year":"2016","origin":"JU","dest":"LU","count":3},{"year":"2016","origin":"JU","dest":"UR","count":0},{"year":"2016","origin":"JU","dest":"SZ","count":0},{"year":"2016","origin":"JU","dest":"OW","count":1},{"year":"2016","origin":"JU","dest":"NW","count":0},{"year":"2016","origin":"JU","dest":"GL","count":0},{"year":"2016","origin":"JU","dest":"ZG","count":1},{"year":"2016","origin":"JU","dest":"FR","count":90},{"year":"2016","origin":"JU","dest":"SO","count":22},{"year":"2016","origin":"JU","dest":"BS","count":38},{"year":"2016","origin":"JU","dest":"BL","count":58},{"year":"2016","origin":"JU","dest":"SH","count":2},{"year":"2016","origin":"JU","dest":"AR","count":1},{"year":"2016","origin":"JU","dest":"AI","count":0},{"year":"2016","origin":"JU","dest":"SG","count":5},{"year":"2016","origin":"JU","dest":"GR","count":0},{"year":"2016","origin":"JU","dest":"AG","count":19},{"year":"2016","origin":"JU","dest":"TG","count":5},{"year":"2016","origin":"JU","dest":"TI","count":3},{"year":"2016","origin":"JU","dest":"VD","count":217},{"year":"2016","origin":"JU","dest":"VS","count":61},{"year":"2016","origin":"JU","dest":"NE","count":181},{"year":"2016","origin":"JU","dest":"GE","count":42},{"year":"2016","origin":"JU","dest":"JU","count":3112}] ================================================ FILE: examples/public/data/flows-diff-2015-2016.json ================================================ [{"origin":"ZH","dest":"ZH","count":-3095},{"origin":"ZH","dest":"BE","count":12},{"origin":"ZH","dest":"LU","count":87},{"origin":"ZH","dest":"UR","count":-29},{"origin":"ZH","dest":"SZ","count":117},{"origin":"ZH","dest":"OW","count":23},{"origin":"ZH","dest":"NW","count":-5},{"origin":"ZH","dest":"GL","count":36},{"origin":"ZH","dest":"ZG","count":10},{"origin":"ZH","dest":"FR","count":-13},{"origin":"ZH","dest":"SO","count":35},{"origin":"ZH","dest":"BS","count":-37},{"origin":"ZH","dest":"BL","count":-11},{"origin":"ZH","dest":"SH","count":23},{"origin":"ZH","dest":"AR","count":-21},{"origin":"ZH","dest":"AI","count":-5},{"origin":"ZH","dest":"SG","count":48},{"origin":"ZH","dest":"GR","count":-111},{"origin":"ZH","dest":"AG","count":-102},{"origin":"ZH","dest":"TG","count":188},{"origin":"ZH","dest":"TI","count":-30},{"origin":"ZH","dest":"VD","count":-65},{"origin":"ZH","dest":"VS","count":32},{"origin":"ZH","dest":"NE","count":6},{"origin":"ZH","dest":"GE","count":-28},{"origin":"ZH","dest":"JU","count":-13},{"origin":"BE","dest":"ZH","count":89},{"origin":"BE","dest":"BE","count":-1782},{"origin":"BE","dest":"LU","count":70},{"origin":"BE","dest":"UR","count":-1},{"origin":"BE","dest":"SZ","count":46},{"origin":"BE","dest":"OW","count":-26},{"origin":"BE","dest":"NW","count":36},{"origin":"BE","dest":"GL","count":2},{"origin":"BE","dest":"ZG","count":-12},{"origin":"BE","dest":"FR","count":38},{"origin":"BE","dest":"SO","count":-106},{"origin":"BE","dest":"BS","count":4},{"origin":"BE","dest":"BL","count":6},{"origin":"BE","dest":"SH","count":12},{"origin":"BE","dest":"AR","count":34},{"origin":"BE","dest":"AI","count":2},{"origin":"BE","dest":"SG","count":35},{"origin":"BE","dest":"GR","count":-6},{"origin":"BE","dest":"AG","count":-54},{"origin":"BE","dest":"TG","count":-15},{"origin":"BE","dest":"TI","count":-21},{"origin":"BE","dest":"VD","count":-12},{"origin":"BE","dest":"VS","count":73},{"origin":"BE","dest":"NE","count":-3},{"origin":"BE","dest":"GE","count":-19},{"origin":"BE","dest":"JU","count":-120},{"origin":"LU","dest":"ZH","count":-20},{"origin":"LU","dest":"BE","count":29},{"origin":"LU","dest":"LU","count":-1053},{"origin":"LU","dest":"UR","count":-28},{"origin":"LU","dest":"SZ","count":70},{"origin":"LU","dest":"OW","count":32},{"origin":"LU","dest":"NW","count":51},{"origin":"LU","dest":"GL","count":-11},{"origin":"LU","dest":"ZG","count":54},{"origin":"LU","dest":"FR","count":-12},{"origin":"LU","dest":"SO","count":-32},{"origin":"LU","dest":"BS","count":-10},{"origin":"LU","dest":"BL","count":20},{"origin":"LU","dest":"SH","count":-9},{"origin":"LU","dest":"AR","count":3},{"origin":"LU","dest":"AI","count":-1},{"origin":"LU","dest":"SG","count":-23},{"origin":"LU","dest":"GR","count":16},{"origin":"LU","dest":"AG","count":-119},{"origin":"LU","dest":"TG","count":-10},{"origin":"LU","dest":"TI","count":-14},{"origin":"LU","dest":"VD","count":8},{"origin":"LU","dest":"VS","count":37},{"origin":"LU","dest":"NE","count":2},{"origin":"LU","dest":"GE","count":-8},{"origin":"LU","dest":"JU","count":-3},{"origin":"UR","dest":"ZH","count":-4},{"origin":"UR","dest":"BE","count":-10},{"origin":"UR","dest":"LU","count":41},{"origin":"UR","dest":"UR","count":-219},{"origin":"UR","dest":"SZ","count":-9},{"origin":"UR","dest":"OW","count":-11},{"origin":"UR","dest":"NW","count":7},{"origin":"UR","dest":"GL","count":-3},{"origin":"UR","dest":"ZG","count":5},{"origin":"UR","dest":"FR","count":-2},{"origin":"UR","dest":"SO","count":13},{"origin":"UR","dest":"BS","count":-1},{"origin":"UR","dest":"BL","count":-10},{"origin":"UR","dest":"SH","count":2},{"origin":"UR","dest":"AR","count":2},{"origin":"UR","dest":"AI","count":-1},{"origin":"UR","dest":"SG","count":-11},{"origin":"UR","dest":"GR","count":7},{"origin":"UR","dest":"AG","count":5},{"origin":"UR","dest":"TI","count":-2},{"origin":"UR","dest":"VD","count":-1},{"origin":"UR","dest":"VS","count":-4},{"origin":"UR","dest":"NE","count":1},{"origin":"UR","dest":"GE","count":1},{"origin":"SZ","dest":"ZH","count":112},{"origin":"SZ","dest":"BE","count":34},{"origin":"SZ","dest":"LU","count":14},{"origin":"SZ","dest":"UR","count":-22},{"origin":"SZ","dest":"SZ","count":116},{"origin":"SZ","dest":"OW","count":18},{"origin":"SZ","dest":"NW","count":13},{"origin":"SZ","dest":"GL","count":29},{"origin":"SZ","dest":"ZG","count":15},{"origin":"SZ","dest":"FR","count":6},{"origin":"SZ","dest":"SO","count":-10},{"origin":"SZ","dest":"BS","count":12},{"origin":"SZ","dest":"BL","count":12},{"origin":"SZ","dest":"SH","count":-4},{"origin":"SZ","dest":"AR","count":1},{"origin":"SZ","dest":"AI","count":-4},{"origin":"SZ","dest":"SG","count":29},{"origin":"SZ","dest":"GR","count":-12},{"origin":"SZ","dest":"AG","count":-37},{"origin":"SZ","dest":"TG","count":-7},{"origin":"SZ","dest":"TI","count":4},{"origin":"SZ","dest":"VD","count":-9},{"origin":"SZ","dest":"VS","count":9},{"origin":"SZ","dest":"NE","count":-6},{"origin":"SZ","dest":"GE","count":7},{"origin":"SZ","dest":"JU","count":2},{"origin":"OW","dest":"ZH","count":-8},{"origin":"OW","dest":"BE","count":-15},{"origin":"OW","dest":"LU","count":48},{"origin":"OW","dest":"UR","count":-11},{"origin":"OW","dest":"SZ","count":4},{"origin":"OW","dest":"OW","count":-93},{"origin":"OW","dest":"NW","count":15},{"origin":"OW","dest":"GL","count":-1},{"origin":"OW","dest":"ZG","count":-12},{"origin":"OW","dest":"FR","count":2},{"origin":"OW","dest":"SO","count":-3},{"origin":"OW","dest":"BS","count":1},{"origin":"OW","dest":"BL","count":1},{"origin":"OW","dest":"AR","count":1},{"origin":"OW","dest":"SG","count":6},{"origin":"OW","dest":"GR","count":5},{"origin":"OW","dest":"AG","count":6},{"origin":"OW","dest":"TG","count":16},{"origin":"OW","dest":"TI","count":-2},{"origin":"OW","dest":"VD","count":5},{"origin":"OW","dest":"VS","count":6},{"origin":"OW","dest":"GE","count":-2},{"origin":"OW","dest":"JU","count":3},{"origin":"NW","dest":"ZH","count":-14},{"origin":"NW","dest":"LU","count":23},{"origin":"NW","dest":"UR","count":-14},{"origin":"NW","dest":"SZ","count":-3},{"origin":"NW","dest":"OW","count":20},{"origin":"NW","dest":"NW","count":25},{"origin":"NW","dest":"GL","count":-8},{"origin":"NW","dest":"ZG","count":-14},{"origin":"NW","dest":"FR","count":-6},{"origin":"NW","dest":"SO","count":3},{"origin":"NW","dest":"BS","count":-5},{"origin":"NW","dest":"BL","count":-1},{"origin":"NW","dest":"SH","count":-3},{"origin":"NW","dest":"AR","count":2},{"origin":"NW","dest":"SG","count":-1},{"origin":"NW","dest":"GR","count":-1},{"origin":"NW","dest":"AG","count":-10},{"origin":"NW","dest":"TG","count":-7},{"origin":"NW","dest":"VD","count":-1},{"origin":"NW","dest":"VS","count":4},{"origin":"NW","dest":"NE","count":1},{"origin":"NW","dest":"GE","count":3},{"origin":"NW","dest":"JU","count":-1},{"origin":"GL","dest":"ZH","count":12},{"origin":"GL","dest":"BE","count":6},{"origin":"GL","dest":"LU","count":12},{"origin":"GL","dest":"UR","count":3},{"origin":"GL","dest":"SZ","count":-19},{"origin":"GL","dest":"OW","count":5},{"origin":"GL","dest":"NW","count":-1},{"origin":"GL","dest":"GL","count":-45},{"origin":"GL","dest":"ZG","count":14},{"origin":"GL","dest":"FR","count":1},{"origin":"GL","dest":"SO","count":-10},{"origin":"GL","dest":"BL","count":-1},{"origin":"GL","dest":"SH","count":-1},{"origin":"GL","dest":"AR","count":10},{"origin":"GL","dest":"AI","count":-2},{"origin":"GL","dest":"SG","count":2},{"origin":"GL","dest":"GR","count":4},{"origin":"GL","dest":"AG","count":-31},{"origin":"GL","dest":"TG","count":-6},{"origin":"GL","dest":"TI","count":-1},{"origin":"GL","dest":"VD","count":-1},{"origin":"GL","dest":"VS","count":8},{"origin":"GL","dest":"NE","count":2},{"origin":"GL","dest":"GE","count":1},{"origin":"GL","dest":"JU","count":5},{"origin":"ZG","dest":"ZH","count":-143},{"origin":"ZG","dest":"BE","count":13},{"origin":"ZG","dest":"LU","count":15},{"origin":"ZG","dest":"UR","count":-3},{"origin":"ZG","dest":"SZ","count":-9},{"origin":"ZG","dest":"OW","count":-1},{"origin":"ZG","dest":"NW","count":18},{"origin":"ZG","dest":"GL","count":-5},{"origin":"ZG","dest":"ZG","count":-8},{"origin":"ZG","dest":"FR","count":-6},{"origin":"ZG","dest":"SO","count":-14},{"origin":"ZG","dest":"BS","count":-20},{"origin":"ZG","dest":"BL","count":-6},{"origin":"ZG","dest":"SH","count":5},{"origin":"ZG","dest":"AR","count":6},{"origin":"ZG","dest":"AI","count":2},{"origin":"ZG","dest":"SG","count":6},{"origin":"ZG","dest":"AG","count":-65},{"origin":"ZG","dest":"TG","count":16},{"origin":"ZG","dest":"TI","count":-29},{"origin":"ZG","dest":"VD","count":-4},{"origin":"ZG","dest":"VS","count":4},{"origin":"ZG","dest":"NE","count":7},{"origin":"ZG","dest":"GE","count":2},{"origin":"ZG","dest":"JU","count":-4},{"origin":"FR","dest":"ZH","count":66},{"origin":"FR","dest":"BE","count":-5},{"origin":"FR","dest":"LU","count":26},{"origin":"FR","dest":"UR","count":2},{"origin":"FR","dest":"SZ","count":7},{"origin":"FR","dest":"OW","count":-1},{"origin":"FR","dest":"NW","count":3},{"origin":"FR","dest":"GL","count":1},{"origin":"FR","dest":"ZG","count":1},{"origin":"FR","dest":"FR","count":-583},{"origin":"FR","dest":"SO","count":36},{"origin":"FR","dest":"BS","count":20},{"origin":"FR","dest":"BL","count":28},{"origin":"FR","dest":"SH","count":4},{"origin":"FR","dest":"AR","count":2},{"origin":"FR","dest":"SG","count":5},{"origin":"FR","dest":"AG","count":-2},{"origin":"FR","dest":"TI","count":2},{"origin":"FR","dest":"VD","count":-13},{"origin":"FR","dest":"VS","count":-40},{"origin":"FR","dest":"NE","count":-2},{"origin":"FR","dest":"GE","count":30},{"origin":"FR","dest":"JU","count":2},{"origin":"SO","dest":"ZH","count":-4},{"origin":"SO","dest":"BE","count":10},{"origin":"SO","dest":"LU","count":44},{"origin":"SO","dest":"UR","count":-7},{"origin":"SO","dest":"SZ","count":-13},{"origin":"SO","dest":"OW","count":2},{"origin":"SO","dest":"NW","count":10},{"origin":"SO","dest":"GL","count":-2},{"origin":"SO","dest":"FR","count":-8},{"origin":"SO","dest":"SO","count":-43},{"origin":"SO","dest":"BS","count":-57},{"origin":"SO","dest":"BL","count":-4},{"origin":"SO","dest":"SH","count":-12},{"origin":"SO","dest":"AR","count":14},{"origin":"SO","dest":"AI","count":-1},{"origin":"SO","dest":"SG","count":10},{"origin":"SO","dest":"GR","count":-16},{"origin":"SO","dest":"AG","count":-34},{"origin":"SO","dest":"TG","count":20},{"origin":"SO","dest":"TI","count":21},{"origin":"SO","dest":"VD","count":-11},{"origin":"SO","dest":"VS","count":6},{"origin":"SO","dest":"NE","count":-1},{"origin":"SO","dest":"GE","count":-4},{"origin":"SO","dest":"JU","count":-9},{"origin":"BS","dest":"ZH","count":-122},{"origin":"BS","dest":"BE","count":31},{"origin":"BS","dest":"LU","count":-18},{"origin":"BS","dest":"UR","count":6},{"origin":"BS","dest":"SZ","count":-10},{"origin":"BS","dest":"OW","count":-2},{"origin":"BS","dest":"NW","count":8},{"origin":"BS","dest":"GL","count":-1},{"origin":"BS","dest":"ZG","count":-12},{"origin":"BS","dest":"FR","count":-9},{"origin":"BS","dest":"SO","count":83},{"origin":"BS","dest":"BS","count":-13},{"origin":"BS","dest":"BL","count":-327},{"origin":"BS","dest":"SH","count":-2},{"origin":"BS","dest":"AR","count":1},{"origin":"BS","dest":"SG","count":24},{"origin":"BS","dest":"GR","count":-5},{"origin":"BS","dest":"AG","count":-9},{"origin":"BS","dest":"TG","count":4},{"origin":"BS","dest":"TI","count":-15},{"origin":"BS","dest":"VD","count":13},{"origin":"BS","dest":"VS","count":-2},{"origin":"BS","dest":"NE","count":-8},{"origin":"BS","dest":"GE","count":9},{"origin":"BS","dest":"JU","count":-8},{"origin":"BL","dest":"ZH","count":-75},{"origin":"BL","dest":"BE","count":-7},{"origin":"BL","dest":"LU","count":-37},{"origin":"BL","dest":"UR","count":5},{"origin":"BL","dest":"SZ","count":-3},{"origin":"BL","dest":"OW","count":-6},{"origin":"BL","dest":"NW","count":-13},{"origin":"BL","dest":"GL","count":5},{"origin":"BL","dest":"ZG","count":3},{"origin":"BL","dest":"FR","count":-4},{"origin":"BL","dest":"SO","count":-42},{"origin":"BL","dest":"BS","count":80},{"origin":"BL","dest":"BL","count":-316},{"origin":"BL","dest":"SH","count":6},{"origin":"BL","dest":"AR","count":-6},{"origin":"BL","dest":"AI","count":3},{"origin":"BL","dest":"SG","count":11},{"origin":"BL","dest":"GR","count":-6},{"origin":"BL","dest":"AG","count":12},{"origin":"BL","dest":"TG","count":2},{"origin":"BL","dest":"TI","count":11},{"origin":"BL","dest":"VD","count":-10},{"origin":"BL","dest":"VS","count":-10},{"origin":"BL","dest":"NE","count":8},{"origin":"BL","dest":"GE","count":1},{"origin":"BL","dest":"JU","count":-16},{"origin":"SH","dest":"ZH","count":30},{"origin":"SH","dest":"BE","count":-17},{"origin":"SH","dest":"LU","count":3},{"origin":"SH","dest":"SZ","count":-14},{"origin":"SH","dest":"OW","count":2},{"origin":"SH","dest":"NW","count":1},{"origin":"SH","dest":"GL","count":4},{"origin":"SH","dest":"FR","count":-1},{"origin":"SH","dest":"SO","count":-6},{"origin":"SH","dest":"BS","count":2},{"origin":"SH","dest":"BL","count":-4},{"origin":"SH","dest":"SH","count":-48},{"origin":"SH","dest":"AR","count":-18},{"origin":"SH","dest":"AI","count":3},{"origin":"SH","dest":"SG","count":56},{"origin":"SH","dest":"GR","count":6},{"origin":"SH","dest":"AG","count":-1},{"origin":"SH","dest":"TG","count":32},{"origin":"SH","dest":"TI","count":5},{"origin":"SH","dest":"VD","count":5},{"origin":"SH","dest":"VS","count":3},{"origin":"SH","dest":"NE","count":-3},{"origin":"SH","dest":"GE","count":-1},{"origin":"SH","dest":"JU","count":3},{"origin":"AR","dest":"ZH","count":-54},{"origin":"AR","dest":"BE","count":8},{"origin":"AR","dest":"LU","count":-10},{"origin":"AR","dest":"UR","count":-2},{"origin":"AR","dest":"SZ","count":1},{"origin":"AR","dest":"OW","count":-5},{"origin":"AR","dest":"NW","count":-1},{"origin":"AR","dest":"GL","count":8},{"origin":"AR","dest":"ZG","count":-2},{"origin":"AR","dest":"FR","count":5},{"origin":"AR","dest":"SO","count":-2},{"origin":"AR","dest":"BS","count":-2},{"origin":"AR","dest":"BL","count":-3},{"origin":"AR","dest":"SH","count":-4},{"origin":"AR","dest":"AR","count":-57},{"origin":"AR","dest":"AI","count":33},{"origin":"AR","dest":"SG","count":-103},{"origin":"AR","dest":"GR","count":10},{"origin":"AR","dest":"AG","count":11},{"origin":"AR","dest":"TG","count":-5},{"origin":"AR","dest":"TI","count":8},{"origin":"AR","dest":"VD","count":-4},{"origin":"AR","dest":"NE","count":-2},{"origin":"AR","dest":"JU","count":-1},{"origin":"AI","dest":"ZH","count":11},{"origin":"AI","dest":"LU","count":-4},{"origin":"AI","dest":"UR","count":1},{"origin":"AI","dest":"SZ","count":-9},{"origin":"AI","dest":"NW","count":-3},{"origin":"AI","dest":"GL","count":-1},{"origin":"AI","dest":"FR","count":-1},{"origin":"AI","dest":"SO","count":-1},{"origin":"AI","dest":"BS","count":-4},{"origin":"AI","dest":"BL","count":4},{"origin":"AI","dest":"SH","count":-1},{"origin":"AI","dest":"AR","count":-2},{"origin":"AI","dest":"AI","count":23},{"origin":"AI","dest":"SG","count":2},{"origin":"AI","dest":"GR","count":-6},{"origin":"AI","dest":"AG","count":2},{"origin":"AI","dest":"TG","count":-7},{"origin":"AI","dest":"TI","count":-2},{"origin":"AI","dest":"VD","count":2},{"origin":"AI","dest":"VS","count":3},{"origin":"AI","dest":"NE","count":2},{"origin":"SG","dest":"ZH","count":-101},{"origin":"SG","dest":"BE","count":-17},{"origin":"SG","dest":"LU","count":42},{"origin":"SG","dest":"UR","count":21},{"origin":"SG","dest":"SZ","count":-63},{"origin":"SG","dest":"OW","count":-3},{"origin":"SG","dest":"NW","count":-1},{"origin":"SG","dest":"GL","count":-16},{"origin":"SG","dest":"ZG","count":-13},{"origin":"SG","dest":"FR","count":-8},{"origin":"SG","dest":"SO","count":-10},{"origin":"SG","dest":"BS","count":2},{"origin":"SG","dest":"BL","count":8},{"origin":"SG","dest":"SH","count":-10},{"origin":"SG","dest":"AR","count":-20},{"origin":"SG","dest":"AI","count":-18},{"origin":"SG","dest":"SG","count":-694},{"origin":"SG","dest":"GR","count":-34},{"origin":"SG","dest":"AG","count":35},{"origin":"SG","dest":"TG","count":25},{"origin":"SG","dest":"TI","count":-11},{"origin":"SG","dest":"VD","count":-2},{"origin":"SG","dest":"NE","count":2},{"origin":"SG","dest":"GE","count":-2},{"origin":"SG","dest":"JU","count":4},{"origin":"GR","dest":"ZH","count":-20},{"origin":"GR","dest":"BE","count":58},{"origin":"GR","dest":"LU","count":-13},{"origin":"GR","dest":"UR","count":7},{"origin":"GR","dest":"SZ","count":-6},{"origin":"GR","dest":"OW","count":-6},{"origin":"GR","dest":"NW","count":19},{"origin":"GR","dest":"GL","count":5},{"origin":"GR","dest":"ZG","count":28},{"origin":"GR","dest":"FR","count":7},{"origin":"GR","dest":"SO","count":-6},{"origin":"GR","dest":"BS","count":-26},{"origin":"GR","dest":"BL","count":15},{"origin":"GR","dest":"SH","count":-24},{"origin":"GR","dest":"AR","count":-13},{"origin":"GR","dest":"AI","count":-10},{"origin":"GR","dest":"SG","count":-37},{"origin":"GR","dest":"GR","count":-13},{"origin":"GR","dest":"AG","count":-43},{"origin":"GR","dest":"TG","count":-16},{"origin":"GR","dest":"TI","count":-22},{"origin":"GR","dest":"VD","count":7},{"origin":"GR","dest":"VS","count":2},{"origin":"GR","dest":"NE","count":1},{"origin":"GR","dest":"GE","count":8},{"origin":"GR","dest":"JU","count":-1},{"origin":"AG","dest":"ZH","count":165},{"origin":"AG","dest":"BE","count":44},{"origin":"AG","dest":"LU","count":107},{"origin":"AG","dest":"UR","count":-5},{"origin":"AG","dest":"SZ","count":17},{"origin":"AG","dest":"OW","count":-5},{"origin":"AG","dest":"NW","count":-15},{"origin":"AG","dest":"GL","count":-4},{"origin":"AG","dest":"ZG","count":6},{"origin":"AG","dest":"FR","count":11},{"origin":"AG","dest":"SO","count":5},{"origin":"AG","dest":"BS","count":-86},{"origin":"AG","dest":"BL","count":20},{"origin":"AG","dest":"SH","count":-14},{"origin":"AG","dest":"AR","count":3},{"origin":"AG","dest":"AI","count":4},{"origin":"AG","dest":"SG","count":-60},{"origin":"AG","dest":"GR","count":8},{"origin":"AG","dest":"AG","count":-1392},{"origin":"AG","dest":"TG","count":7},{"origin":"AG","dest":"TI","count":-53},{"origin":"AG","dest":"VD","count":-4},{"origin":"AG","dest":"VS","count":-15},{"origin":"AG","dest":"NE","count":5},{"origin":"AG","dest":"GE","count":-12},{"origin":"AG","dest":"JU","count":1},{"origin":"TG","dest":"ZH","count":-184},{"origin":"TG","dest":"BE","count":-68},{"origin":"TG","dest":"LU","count":11},{"origin":"TG","dest":"UR","count":5},{"origin":"TG","dest":"SZ","count":-21},{"origin":"TG","dest":"OW","count":7},{"origin":"TG","dest":"GL","count":-8},{"origin":"TG","dest":"ZG","count":17},{"origin":"TG","dest":"FR","count":25},{"origin":"TG","dest":"SO","count":18},{"origin":"TG","dest":"BS","count":15},{"origin":"TG","dest":"BL","count":16},{"origin":"TG","dest":"SH","count":-87},{"origin":"TG","dest":"AR","count":12},{"origin":"TG","dest":"AI","count":10},{"origin":"TG","dest":"SG","count":85},{"origin":"TG","dest":"GR","count":1},{"origin":"TG","dest":"AG","count":8},{"origin":"TG","dest":"TG","count":182},{"origin":"TG","dest":"TI","count":19},{"origin":"TG","dest":"VD","count":1},{"origin":"TG","dest":"VS","count":10},{"origin":"TG","dest":"NE","count":2},{"origin":"TG","dest":"GE","count":-15},{"origin":"TG","dest":"JU","count":-1},{"origin":"TI","dest":"ZH","count":-83},{"origin":"TI","dest":"BE","count":-10},{"origin":"TI","dest":"LU","count":-11},{"origin":"TI","dest":"UR","count":-4},{"origin":"TI","dest":"SZ","count":17},{"origin":"TI","dest":"OW","count":-4},{"origin":"TI","dest":"NW","count":-3},{"origin":"TI","dest":"GL","count":-4},{"origin":"TI","dest":"ZG","count":2},{"origin":"TI","dest":"FR","count":15},{"origin":"TI","dest":"SO","count":12},{"origin":"TI","dest":"BS","count":3},{"origin":"TI","dest":"BL","count":-19},{"origin":"TI","dest":"SH","count":4},{"origin":"TI","dest":"AR","count":-3},{"origin":"TI","dest":"AI","count":1},{"origin":"TI","dest":"SG","count":18},{"origin":"TI","dest":"GR","count":60},{"origin":"TI","dest":"AG","count":-10},{"origin":"TI","dest":"TG","count":4},{"origin":"TI","dest":"TI","count":-556},{"origin":"TI","dest":"VD","count":6},{"origin":"TI","dest":"VS","count":12},{"origin":"TI","dest":"NE","count":-12},{"origin":"TI","dest":"GE","count":30},{"origin":"TI","dest":"JU","count":4},{"origin":"VD","dest":"ZH","count":25},{"origin":"VD","dest":"BE","count":22},{"origin":"VD","dest":"UR","count":-2},{"origin":"VD","dest":"SZ","count":9},{"origin":"VD","dest":"OW","count":1},{"origin":"VD","dest":"NW","count":7},{"origin":"VD","dest":"GL","count":1},{"origin":"VD","dest":"ZG","count":40},{"origin":"VD","dest":"FR","count":-16},{"origin":"VD","dest":"SO","count":28},{"origin":"VD","dest":"BS","count":-14},{"origin":"VD","dest":"BL","count":-28},{"origin":"VD","dest":"SH","count":2},{"origin":"VD","dest":"AR","count":-2},{"origin":"VD","dest":"AI","count":6},{"origin":"VD","dest":"SG","count":-17},{"origin":"VD","dest":"GR","count":-11},{"origin":"VD","dest":"AG","count":31},{"origin":"VD","dest":"TG","count":1},{"origin":"VD","dest":"TI","count":-20},{"origin":"VD","dest":"VD","count":-2512},{"origin":"VD","dest":"VS","count":63},{"origin":"VD","dest":"NE","count":-9},{"origin":"VD","dest":"GE","count":131},{"origin":"VD","dest":"JU","count":-6},{"origin":"VS","dest":"ZH","count":18},{"origin":"VS","dest":"BE","count":32},{"origin":"VS","dest":"LU","count":17},{"origin":"VS","dest":"UR","count":13},{"origin":"VS","dest":"SZ","count":7},{"origin":"VS","dest":"OW","count":-10},{"origin":"VS","dest":"NW","count":-6},{"origin":"VS","dest":"GL","count":-2},{"origin":"VS","dest":"ZG","count":-10},{"origin":"VS","dest":"FR","count":-9},{"origin":"VS","dest":"SO","count":-1},{"origin":"VS","dest":"BS","count":-12},{"origin":"VS","dest":"BL","count":22},{"origin":"VS","dest":"SH","count":4},{"origin":"VS","dest":"AR","count":-3},{"origin":"VS","dest":"AI","count":4},{"origin":"VS","dest":"SG","count":5},{"origin":"VS","dest":"GR","count":3},{"origin":"VS","dest":"AG","count":-6},{"origin":"VS","dest":"TG","count":-1},{"origin":"VS","dest":"TI","count":-10},{"origin":"VS","dest":"VD","count":-12},{"origin":"VS","dest":"VS","count":-477},{"origin":"VS","dest":"NE","count":17},{"origin":"VS","dest":"GE","count":-11},{"origin":"VS","dest":"JU","count":-17},{"origin":"NE","dest":"ZH","count":-1},{"origin":"NE","dest":"BE","count":29},{"origin":"NE","dest":"LU","count":-2},{"origin":"NE","dest":"UR","count":-2},{"origin":"NE","dest":"SZ","count":1},{"origin":"NE","dest":"NW","count":1},{"origin":"NE","dest":"GL","count":-1},{"origin":"NE","dest":"ZG","count":9},{"origin":"NE","dest":"FR","count":-78},{"origin":"NE","dest":"SO","count":-6},{"origin":"NE","dest":"BS","count":-8},{"origin":"NE","dest":"BL","count":-2},{"origin":"NE","dest":"SH","count":7},{"origin":"NE","dest":"AR","count":-1},{"origin":"NE","dest":"SG","count":1},{"origin":"NE","dest":"GR","count":4},{"origin":"NE","dest":"AG","count":3},{"origin":"NE","dest":"TG","count":2},{"origin":"NE","dest":"TI","count":-6},{"origin":"NE","dest":"VD","count":8},{"origin":"NE","dest":"VS","count":-4},{"origin":"NE","dest":"NE","count":132},{"origin":"NE","dest":"GE","count":25},{"origin":"NE","dest":"JU","count":-32},{"origin":"GE","dest":"ZH","count":-68},{"origin":"GE","dest":"BE","count":16},{"origin":"GE","dest":"LU","count":-3},{"origin":"GE","dest":"UR","count":2},{"origin":"GE","dest":"SZ","count":-15},{"origin":"GE","dest":"OW","count":-1},{"origin":"GE","dest":"GL","count":-1},{"origin":"GE","dest":"ZG","count":-13},{"origin":"GE","dest":"FR","count":-4},{"origin":"GE","dest":"SO","count":-9},{"origin":"GE","dest":"BS","count":21},{"origin":"GE","dest":"BL","count":-12},{"origin":"GE","dest":"SG","count":3},{"origin":"GE","dest":"GR","count":9},{"origin":"GE","dest":"AG","count":9},{"origin":"GE","dest":"TI","count":7},{"origin":"GE","dest":"VD","count":190},{"origin":"GE","dest":"VS","count":-78},{"origin":"GE","dest":"NE","count":-5},{"origin":"GE","dest":"GE","count":-3079},{"origin":"GE","dest":"JU","count":-15},{"origin":"JU","dest":"ZH","count":-4},{"origin":"JU","dest":"BE","count":-35},{"origin":"JU","dest":"LU","count":4},{"origin":"JU","dest":"SZ","count":2},{"origin":"JU","dest":"OW","count":-1},{"origin":"JU","dest":"ZG","count":5},{"origin":"JU","dest":"FR","count":-11},{"origin":"JU","dest":"SO","count":8},{"origin":"JU","dest":"BS","count":-16},{"origin":"JU","dest":"BL","count":-34},{"origin":"JU","dest":"AR","count":-1},{"origin":"JU","dest":"SG","count":-1},{"origin":"JU","dest":"GR","count":2},{"origin":"JU","dest":"AG","count":-6},{"origin":"JU","dest":"TG","count":-4},{"origin":"JU","dest":"TI","count":8},{"origin":"JU","dest":"VD","count":-12},{"origin":"JU","dest":"VS","count":28},{"origin":"JU","dest":"NE","count":14},{"origin":"JU","dest":"GE","count":3},{"origin":"JU","dest":"JU","count":-130}] ================================================ FILE: examples/public/data/locations.json ================================================ {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"abbr":"ZH","name":"Zürich","no":1,"centroid":[8.654789284720719,47.412606122294044]},"geometry":{"type":"Polygon","coordinates":[[[8.808068406840686,47.220891289128915],[8.793098109810982,47.223080508050806],[8.74092889288929,47.211935393539356],[8.703729972997301,47.201785378537856],[8.681047704770478,47.18387358735873],[8.691481548154815,47.163971597159716],[8.674696669666968,47.15879707970797],[8.661540954095411,47.160986298629865],[8.661087308730874,47.170937293729374],[8.626156615661568,47.171932393239324],[8.627971197119713,47.17909710971097],[8.615722772277229,47.198800080008],[8.597123312331235,47.20397459745975],[8.594855085508552,47.211935393539356],[8.581699369936995,47.212930493049306],[8.553573357335734,47.223080508050806],[8.484165616561658,47.20775597559756],[8.460576057605762,47.21392559255926],[8.440615661566158,47.22507070707071],[8.422016201620163,47.223080508050806],[8.411128712871289,47.24795799579958],[8.402963096309632,47.2537295729573],[8.40205580558056,47.2740296029603],[8.388900090009002,47.289951195119514],[8.415211521152116,47.29273747374737],[8.4274599459946,47.301096309630964],[8.431996399639965,47.31184338433843],[8.446059405940595,47.31980418041804],[8.449688568856887,47.32975517551755],[8.416118811881189,47.32398359835984],[8.416572457245724,47.33393459345935],[8.403870387038705,47.346870887088706],[8.406592259225924,47.36995719571957],[8.393890189018903,47.380704270427046],[8.392075607560757,47.39602880288029],[8.366217821782179,47.401999399939996],[8.384363636363638,47.405780778077805],[8.392075607560757,47.41991119111911],[8.387539153915393,47.42807100710071],[8.374383438343836,47.43006120612061],[8.384363636363638,47.43702690269027],[8.38663186318632,47.44876907690769],[8.366217821782179,47.470064206420645],[8.373022502250226,47.4810103010301],[8.363949594959497,47.4810103010301],[8.358052205220524,47.49693189318932],[8.360774077407742,47.50986818681868],[8.376651665166518,47.511858385838586],[8.380734473447346,47.524794679467945],[8.402963096309632,47.532954495449545],[8.417933393339336,47.56400160016002],[8.426099009900991,47.56778297829783],[8.438347434743475,47.56698689868987],[8.495053105310532,47.580918291829185],[8.488702070207022,47.587883988398836],[8.466473447344736,47.583903590359036],[8.456493249324932,47.602014401440144],[8.478721872187219,47.61276147614761],[8.50820882088209,47.61793599359936],[8.515920792079209,47.63405660566057],[8.539056705670568,47.630872287228726],[8.568997299729974,47.61694089408941],[8.562646264626464,47.59902910291029],[8.53497389738974,47.586888888888886],[8.550851485148517,47.55703590359036],[8.559470747074709,47.55285648564856],[8.56945094509451,47.57196239623963],[8.579431143114313,47.57594279427943],[8.578070207020703,47.58887908790879],[8.594855085508552,47.5968398839884],[8.595762376237625,47.60579577957796],[8.604835283528354,47.61276147614761],[8.594855085508552,47.643012501250126],[8.604835283528354,47.651968396839685],[8.613454545454546,47.64778897889789],[8.604835283528354,47.63684288428843],[8.623888388838886,47.641022302230226],[8.628878487848786,47.652963496349635],[8.606196219621964,47.66908410841084],[8.613454545454546,47.6780400040004],[8.62706390639064,47.6780400040004],[8.624795679567958,47.69197139713972],[8.64248784878488,47.69495669566957],[8.662448244824484,47.68600080008001],[8.670160216021603,47.68500570057006],[8.681047704770478,47.65674487448745],[8.689213321332135,47.64878407840784],[8.721422142214223,47.641022302230226],[8.746826282628264,47.63883308330833],[8.764518451845186,47.64878407840784],[8.7858397839784,47.654754675467544],[8.788561656165617,47.66470567056706],[8.804439243924394,47.66470567056706],[8.804439243924394,47.651968396839685],[8.826214221422143,47.646793879387936],[8.82802880288029,47.639828182818285],[8.813058505850586,47.62709090909091],[8.800810081008102,47.602014401440144],[8.788108010801082,47.60997519751975],[8.745918991899192,47.62171737173718],[8.740021602160217,47.61176637663766],[8.748187218721874,47.592063406340635],[8.764064806480649,47.593058505850586],[8.805800180018004,47.582908490849086],[8.806707470747076,47.57196239623963],[8.830750675067508,47.57395259525953],[8.838916291629165,47.56499669966997],[8.850711071107112,47.56400160016002],[8.82802880288029,47.55604080408041],[8.841184518451847,47.532954495449545],[8.8829198919892,47.528974097409744],[8.898797479747977,47.523998599859986],[8.892900090009002,47.48797599759976],[8.883827182718273,47.470064206420645],[8.89063186318632,47.4688700870087],[8.902880288028804,47.44399259925993],[8.910592259225924,47.43702690269027],[8.930552655265528,47.43185238523852],[8.907416741674169,47.43085728572857],[8.915128712871288,47.41891609160916],[8.903787578757877,47.401999399939996],[8.919665166516653,47.399810181018104],[8.944162016201622,47.38508270827083],[8.944162016201622,47.37592779277928],[8.96094689468947,47.355826782678264],[8.971834383438345,47.352045404540455],[8.978639063906392,47.337715971597156],[8.97637083708371,47.32696889688969],[8.984990099009902,47.31900810081008],[8.9750099009901,47.30706690669067],[8.956410441044106,47.30487768776877],[8.948698469846985,47.291941394139414],[8.93418181818182,47.28776197619762],[8.947337533753377,47.281990399039906],[8.944162016201622,47.26885508550855],[8.93282088208821,47.25870507050705],[8.872939693969398,47.25173937393739],[8.869764176417643,47.24477367736774],[8.837555355535555,47.24696289628963],[8.826214221422143,47.25074427442744],[8.803078307830784,47.24696289628963],[8.795366336633665,47.23900210021002],[8.808068406840686,47.220891289128915]]]}},{"type":"Feature","properties":{"abbr":"LU","name":"Luzern","no":3,"centroid":[8.110050800932683,47.06748397445195]},"geometry":{"type":"Polygon","coordinates":[[[8.04866606660666,46.78782398239824],[8.020993699369939,46.78881908190819],[7.9837947794779485,46.77508670867087],[7.9538541854185425,46.792003400340036],[7.946595859585959,46.8047406740674],[7.922099009900991,46.82006520652065],[7.876734473447345,46.83698189818982],[7.866754275427543,46.850913291329135],[7.869929792979299,46.864048604860486],[7.856320432043205,46.871014301430144],[7.859042304230424,46.88474667466747],[7.873105310531054,46.88972217221722],[7.883085508550856,46.90405160516052],[7.866754275427543,46.91400260026003],[7.872198019801981,46.923953595359535],[7.898055805580559,46.934899689968994],[7.912118811881189,46.9327104710471],[7.922099009900991,46.9470399039904],[7.932079207920793,46.95082128212821],[7.932079207920793,46.961767376737676],[7.946142214221423,46.96873307330733],[7.952039603960397,46.981072307230725],[7.947956795679569,46.99380958095809],[7.952039603960397,47.0059497949795],[7.932986498649866,47.0037605760576],[7.924367236723673,47.00973117311731],[7.902138613861387,47.0059497949795],[7.899870387038705,47.0180900090009],[7.882178217821783,47.040977297729775],[7.865846984698471,47.05271947194719],[7.874012601260127,47.06207340734073],[7.882178217821783,47.08874207420742],[7.869022502250226,47.112027402740274],[7.873105310531054,47.12476467646765],[7.885807380738075,47.12794899489949],[7.889890189018903,47.14088528852885],[7.878095409540955,47.150836283628365],[7.874466246624664,47.161981398139815],[7.883085508550856,47.17272847284728],[7.874466246624664,47.18387358735873],[7.859949594959497,47.19183438343834],[7.846340234023403,47.216711871187115],[7.836360036003601,47.224075607560756],[7.83908190819082,47.23382758275827],[7.854052205220523,47.232036403640365],[7.868115211521153,47.23681288128813],[7.909850585058507,47.24377857785778],[7.932986498649866,47.23581778177818],[7.961112511251126,47.2537295729573],[7.9488640864086415,47.27303450345035],[7.956122412241225,47.276815881588156],[7.986516651665167,47.2760198019802],[7.99513591359136,47.281990399039906],[8.016003600360037,47.25989918991899],[8.015096309630964,47.24377857785778],[8.029612961296131,47.24178837883788],[8.063182718271829,47.24696289628963],[8.055924392439245,47.25591879187919],[8.072709270927094,47.25492369236924],[8.088133213321333,47.26288448844885],[8.099020702070208,47.25989918991899],[8.10900090009001,47.24477367736774],[8.124878487848786,47.24099229922992],[8.131683168316833,47.24596779677968],[8.174779477947796,47.25273447344735],[8.174779477947796,47.23999719971997],[8.156180018001802,47.23900210021002],[8.176140414041406,47.222085408540856],[8.204720072007202,47.225866786678665],[8.207895589558957,47.24795799579958],[8.220144014401441,47.25273447344735],[8.227855985598561,47.27303450345035],[8.241465346534653,47.2720394039404],[8.25099189918992,47.285771777177715],[8.2868298829883,47.278806080608064],[8.299985598559857,47.270845284528455],[8.29590279027903,47.25870507050705],[8.314502250225024,47.23999719971997],[8.314502250225024,47.231041304130414],[8.325389738973898,47.21492069206921],[8.33809180918092,47.17909710971097],[8.358052205220524,47.156806880688066],[8.362135013501351,47.1460598059806],[8.376198019801981,47.13909410941094],[8.412036003600361,47.14088528852885],[8.417933393339336,47.12177937793779],[8.447420342034205,47.1297401740174],[8.45195679567957,47.11381858185818],[8.480536453645366,47.12595879587959],[8.49414581458146,47.11381858185818],[8.49278487848785,47.099887188718874],[8.474185418541856,47.112027402740274],[8.449688568856887,47.11381858185818],[8.423830783078309,47.104066606660666],[8.408860486048606,47.091926392639266],[8.409767776777679,47.08297049704971],[8.388900090009002,47.0708302830283],[8.391168316831685,47.06107830783078],[8.429728172817283,47.0648596859686],[8.440615661566158,47.06286948694869],[8.49641404140414,47.02883708370837],[8.513652565256526,47.02187138713872],[8.508662466246626,47.0037605760576],[8.482804680468048,46.99679487948795],[8.467380738073809,46.99679487948795],[8.468741674167418,47.00893509350935],[8.4274599459946,47.01709490949095],[8.426099009900991,46.99798899889989],[8.396158415841585,47.00097429742974],[8.384363636363638,46.99798899889989],[8.3839099909991,47.0180900090009],[8.368939693969399,47.01908510851085],[8.334462646264628,46.989033103310334],[8.318585058505851,46.99997919791979],[8.281386138613861,46.99480468046804],[8.264601260126014,46.99798899889989],[8.243733573357336,46.985848784878485],[8.22921692169217,46.992018401840184],[8.217875787578759,46.981072307230725],[8.22921692169217,46.972912491249126],[8.202905490549057,46.966742874287426],[8.18702790279028,46.971917391739176],[8.149375337533755,46.95798599859986],[8.164799279927994,46.93907910791079],[8.134858685868588,46.921963396339635],[8.11036183618362,46.89270747074708],[8.10900090009001,46.90783298329833],[8.09493789378938,46.912012401240126],[8.083143114311433,46.89987218721872],[8.069533753375339,46.89609080908091],[8.071801980198021,46.873800580058],[8.065904590459047,46.859869186918694],[8.041861386138615,46.83698189818982],[8.063636363636364,46.80792499249925],[8.04866606660666,46.78782398239824]]]}},{"type":"Feature","properties":{"abbr":"UR","name":"Uri","no":4,"centroid":[8.628549054737778,46.7719996986948]},"geometry":{"type":"Polygon","coordinates":[[[8.935089108910892,46.919973197319734],[8.952781278127814,46.89808100810081],[8.958225022502251,46.8799701970197],[8.91739693969397,46.861063306330635],[8.908324032403241,46.862058405840585],[8.892900090009002,46.84872407240724],[8.870671467146716,46.841758375837586],[8.882012601260127,46.82902110211021],[8.87656885688569,46.813099509950995],[8.847989198919894,46.797774977497745],[8.82802880288029,46.792003400340036],[8.833926192619263,46.76692689268927],[8.81759495949595,46.76175237523752],[8.809882988298831,46.744039603960395],[8.813058505850586,46.736874887488746],[8.782210621062108,46.727719971997196],[8.765879387938796,46.744835683568354],[8.748187218721874,46.73607880788079],[8.748187218721874,46.71796799679968],[8.717792979297931,46.70602680268027],[8.709173717371739,46.69906110611061],[8.70010081008101,46.70383758375837],[8.673335733573358,46.692095409540954],[8.673335733573358,46.670800280028004],[8.678779477947796,46.66403360336034],[8.654736273627364,46.64970417041704],[8.651107110711072,46.63577277727773],[8.657911791179119,46.625821782178214],[8.67696489648965,46.61885608560856],[8.681501350135015,46.58900310031003],[8.678779477947796,46.57905210521052],[8.653375337533754,46.56790699069907],[8.63477587758776,46.56372757275727],[8.61935193519352,46.57805700570057],[8.589411341134115,46.573081508150814],[8.573080108010803,46.58382858285828],[8.548129612961297,46.58183838383838],[8.539056705670568,46.58701290129013],[8.520457245724574,46.57686288628863],[8.513198919891991,46.55875207520752],[8.519549954995501,46.5388500850085],[8.482804680468048,46.53188438843884],[8.47736093609361,46.52790399039904],[8.465112511251126,46.53606380638064],[8.457400540054007,46.53287948794879],[8.42882088208821,46.542830483048306],[8.419747974797481,46.56571777177717],[8.406138613861387,46.58601780178018],[8.419747974797481,46.6160698069807],[8.422016201620163,46.64791299129913],[8.409767776777679,46.65288848884888],[8.407499549954997,46.66701890189019],[8.397972997299732,46.673984598459846],[8.399787578757877,46.693090509050904],[8.415211521152116,46.68672187218722],[8.45195679567957,46.68891109110911],[8.454225022502252,46.71080328032803],[8.448781278127814,46.71179837983798],[8.440615661566158,46.736874887488746],[8.448781278127814,46.76473767376738],[8.490970297029705,46.77289748974897],[8.47736093609361,46.78881908190819],[8.482804680468048,46.81688088808881],[8.489609360936095,46.83598679867987],[8.506394239423944,46.84494269426943],[8.497321332133215,46.85409760976098],[8.471917191719173,46.85509270927093],[8.466473447344736,46.87479567956795],[8.478721872187219,46.88275647564756],[8.474185418541856,46.89270747074708],[8.458761476147615,46.89907610761076],[8.480536453645366,46.90882808280828],[8.485980198019803,46.917783978397836],[8.502765076507652,46.91400260026003],[8.519549954995501,46.922958495849585],[8.54404680468047,46.92773497349735],[8.54404680468047,46.93907910791079],[8.559017101710172,46.94007420742074],[8.554934293429344,46.96296149614962],[8.573533753375338,46.982067406740676],[8.568997299729974,46.990028202820284],[8.581699369936995,46.993013501350134],[8.602567056705672,46.986843884388435],[8.603927992799282,46.95181638163816],[8.623888388838886,46.95281148114812],[8.670160216021603,46.94385558555855],[8.69238883888389,46.9470399039904],[8.696925292529254,46.92892909290929],[8.707812781278129,46.916788878887886],[8.736846084608462,46.9327104710471],[8.768601260126013,46.94007420742074],[8.788108010801082,46.92992419241924],[8.810790279027904,46.94106930693069],[8.82666786678668,46.935894789478944],[8.82802880288029,46.91877907790779],[8.836194419441945,46.90783298329833],[8.847989198919894,46.913007500750076],[8.847989198919894,46.89071727172717],[8.852979297929794,46.88474667466747],[8.862052205220524,46.89609080908091],[8.87384698469847,46.89489668966897],[8.901065706570659,46.90783298329833],[8.935089108910892,46.919973197319734]]]}},{"type":"Feature","properties":{"abbr":"SZ","name":"Schwyz","no":5,"centroid":[8.756141692382394,47.06179415913812]},"geometry":{"type":"Polygon","coordinates":[[[8.568997299729974,46.990028202820284],[8.54268586858686,46.979878187818784],[8.49641404140414,46.97609680968097],[8.465112511251126,46.984057605760576],[8.467380738073809,46.99679487948795],[8.482804680468048,46.99679487948795],[8.508662466246626,47.0037605760576],[8.513652565256526,47.02187138713872],[8.49641404140414,47.02883708370837],[8.440615661566158,47.06286948694869],[8.429728172817283,47.0648596859686],[8.391168316831685,47.06107830783078],[8.388900090009002,47.0708302830283],[8.409767776777679,47.08297049704971],[8.408860486048606,47.091926392639266],[8.423830783078309,47.104066606660666],[8.449688568856887,47.11381858185818],[8.474185418541856,47.112027402740274],[8.49278487848785,47.099887188718874],[8.520457245724574,47.089737173717374],[8.562646264626464,47.093916591659166],[8.567182718271829,47.08078127812781],[8.589411341134115,47.08595579557956],[8.608918091809182,47.094911691169116],[8.63341494149415,47.093916591659166],[8.671521152115211,47.11580878087808],[8.686945094509452,47.13491469146915],[8.683769576957697,47.14088528852885],[8.701008100810082,47.150836283628365],[8.691481548154815,47.163971597159716],[8.681047704770478,47.18387358735873],[8.703729972997301,47.201785378537856],[8.74092889288929,47.211935393539356],[8.793098109810982,47.223080508050806],[8.808068406840686,47.220891289128915],[8.828482448244825,47.20875107510751],[8.841184518451847,47.20775597559756],[8.940986498649867,47.217706970697066],[8.969566156615663,47.217706970697066],[8.965029702970298,47.19800400040004],[8.958225022502251,47.198800080008],[8.961854185418543,47.18009220922092],[8.981814581458147,47.18287848784878],[9.003589558955897,47.17272847284728],[9.003589558955897,47.17272847284728],[8.966390639063906,47.13690489048905],[8.965029702970298,47.12177937793779],[8.974102610261028,47.11481368136813],[8.970473447344736,47.104066606660666],[8.954142214221424,47.090732273227324],[8.9750099009901,47.08595579557956],[8.965936993699371,47.081776377637766],[8.950513051305132,47.06107830783078],[8.947337533753377,47.04893809380938],[8.921026102610263,47.037792979297926],[8.901972997299731,47.037792979297926],[8.903787578757877,47.02704590459046],[8.895168316831684,47.02087628762876],[8.909684968496851,47.01709490949095],[8.910592259225924,47.00893509350935],[8.940986498649867,46.984853685368535],[8.931913591359137,46.982067406740676],[8.945976597659767,46.966742874287426],[8.970473447344736,46.95878207820782],[8.955049504950496,46.93907910791079],[8.963215121512153,46.9267398739874],[8.935089108910892,46.919973197319734],[8.901065706570659,46.90783298329833],[8.87384698469847,46.89489668966897],[8.862052205220524,46.89609080908091],[8.852979297929794,46.88474667466747],[8.847989198919894,46.89071727172717],[8.847989198919894,46.913007500750076],[8.836194419441945,46.90783298329833],[8.82802880288029,46.91877907790779],[8.82666786678668,46.935894789478944],[8.810790279027904,46.94106930693069],[8.788108010801082,46.92992419241924],[8.768601260126013,46.94007420742074],[8.736846084608462,46.9327104710471],[8.707812781278129,46.916788878887886],[8.696925292529254,46.92892909290929],[8.69238883888389,46.9470399039904],[8.670160216021603,46.94385558555855],[8.623888388838886,46.95281148114812],[8.603927992799282,46.95181638163816],[8.602567056705672,46.986843884388435],[8.581699369936995,46.993013501350134],[8.568997299729974,46.990028202820284]]]}},{"type":"Feature","properties":{"abbr":"OW","name":"Obwalden","no":6,"centroid":[8.243185555987871,46.85245963030377]},"geometry":{"type":"MultiPolygon","coordinates":[[[[8.368939693969399,46.78782398239824],[8.35986678667867,46.782848484848486],[8.333101710171018,46.78085828582858],[8.283200720072008,46.75279647964796],[8.269137713771379,46.75379157915791],[8.259157515751577,46.76374257425743],[8.239197119711973,46.76971317131713],[8.22150495049505,46.76095629562956],[8.207895589558957,46.76891709170917],[8.190203420342035,46.76971317131713],[8.145746174617463,46.75478667866786],[8.121702970297031,46.76971317131713],[8.104918091809182,46.77289748974897],[8.089494149414943,46.78782398239824],[8.04866606660666,46.78782398239824],[8.063636363636364,46.80792499249925],[8.041861386138615,46.83698189818982],[8.065904590459047,46.859869186918694],[8.071801980198021,46.873800580058],[8.069533753375339,46.89609080908091],[8.083143114311433,46.89987218721872],[8.09493789378938,46.912012401240126],[8.10900090009001,46.90783298329833],[8.11036183618362,46.89270747074708],[8.134858685868588,46.921963396339635],[8.164799279927994,46.93907910791079],[8.149375337533755,46.95798599859986],[8.18702790279028,46.971917391739176],[8.202905490549057,46.966742874287426],[8.22921692169217,46.972912491249126],[8.25235283528353,46.979878187818784],[8.281386138613861,46.97609680968097],[8.30089288928893,46.97788798879888],[8.30996579657966,46.96873307330733],[8.30996579657966,46.95181638163816],[8.287737173717373,46.93370557055705],[8.30996579657966,46.923953595359535],[8.33809180918092,46.92892909290929],[8.34308190819082,46.91499769976998],[8.338545454545455,46.89708590859086],[8.32765796579658,46.8779799979998],[8.334462646264628,46.861063306330635],[8.32765796579658,46.84593779377938],[8.335823582358238,46.83280248024803],[8.322214221422144,46.82006520652065],[8.34444284428443,46.799964196419644],[8.368939693969399,46.78782398239824]]],[[[8.471917191719173,46.85509270927093],[8.497321332133215,46.85409760976098],[8.506394239423944,46.84494269426943],[8.489609360936095,46.83598679867987],[8.482804680468048,46.81688088808881],[8.47736093609361,46.78881908190819],[8.490970297029705,46.77289748974897],[8.448781278127814,46.76473767376738],[8.446513051305132,46.76971317131713],[8.413850585058507,46.77389258925893],[8.396158415841585,46.76473767376738],[8.39434383438344,46.77170337033703],[8.416572457245724,46.77807200720072],[8.426099009900991,46.77508670867087],[8.418387038703871,46.78881908190819],[8.39661206120612,46.794988698869886],[8.384363636363638,46.803944594459445],[8.387539153915393,46.813099509950995],[8.373929792979299,46.81588578857885],[8.378012601260128,46.82285148514851],[8.37120792079208,46.83499169916992],[8.369846984698471,46.858874087408736],[8.360774077407742,46.862058405840585],[8.373022502250226,46.87479567956795],[8.384363636363638,46.865839783978394],[8.374383438343836,46.857878987898786],[8.374383438343836,46.847728972897286],[8.398426642664267,46.864844684468444],[8.412943294329434,46.87001920192019],[8.418387038703871,46.852903490349036],[8.471917191719173,46.85509270927093]]]]}},{"type":"Feature","properties":{"abbr":"NW","name":"Nidwalden","no":7,"centroid":[8.405343061072784,46.92618107731872]},"geometry":{"type":"Polygon","coordinates":[[[8.22921692169217,46.972912491249126],[8.217875787578759,46.981072307230725],[8.22921692169217,46.992018401840184],[8.243733573357336,46.985848784878485],[8.264601260126014,46.99798899889989],[8.281386138613861,46.99480468046804],[8.318585058505851,46.99997919791979],[8.334462646264628,46.989033103310334],[8.368939693969399,47.01908510851085],[8.3839099909991,47.0180900090009],[8.384363636363638,46.99798899889989],[8.396158415841585,47.00097429742974],[8.426099009900991,46.99798899889989],[8.4274599459946,47.01709490949095],[8.468741674167418,47.00893509350935],[8.467380738073809,46.99679487948795],[8.465112511251126,46.984057605760576],[8.49641404140414,46.97609680968097],[8.54268586858686,46.979878187818784],[8.568997299729974,46.990028202820284],[8.573533753375338,46.982067406740676],[8.554934293429344,46.96296149614962],[8.559017101710172,46.94007420742074],[8.54404680468047,46.93907910791079],[8.54404680468047,46.92773497349735],[8.519549954995501,46.922958495849585],[8.502765076507652,46.91400260026003],[8.485980198019803,46.917783978397836],[8.480536453645366,46.90882808280828],[8.458761476147615,46.89907610761076],[8.474185418541856,46.89270747074708],[8.478721872187219,46.88275647564756],[8.466473447344736,46.87479567956795],[8.471917191719173,46.85509270927093],[8.418387038703871,46.852903490349036],[8.412943294329434,46.87001920192019],[8.398426642664267,46.864844684468444],[8.374383438343836,46.847728972897286],[8.374383438343836,46.857878987898786],[8.384363636363638,46.865839783978394],[8.373022502250226,46.87479567956795],[8.360774077407742,46.862058405840585],[8.369846984698471,46.858874087408736],[8.37120792079208,46.83499169916992],[8.378012601260128,46.82285148514851],[8.373929792979299,46.81588578857885],[8.387539153915393,46.813099509950995],[8.384363636363638,46.803944594459445],[8.39661206120612,46.794988698869886],[8.418387038703871,46.78881908190819],[8.426099009900991,46.77508670867087],[8.416572457245724,46.77807200720072],[8.39434383438344,46.77170337033703],[8.368939693969399,46.78782398239824],[8.34444284428443,46.799964196419644],[8.322214221422144,46.82006520652065],[8.335823582358238,46.83280248024803],[8.32765796579658,46.84593779377938],[8.334462646264628,46.861063306330635],[8.32765796579658,46.8779799979998],[8.338545454545455,46.89708590859086],[8.34308190819082,46.91499769976998],[8.33809180918092,46.92892909290929],[8.30996579657966,46.923953595359535],[8.287737173717373,46.93370557055705],[8.30996579657966,46.95181638163816],[8.30996579657966,46.96873307330733],[8.30089288928893,46.97788798879888],[8.281386138613861,46.97609680968097],[8.25235283528353,46.979878187818784],[8.22921692169217,46.972912491249126]]]}},{"type":"Feature","properties":{"abbr":"GL","name":"Glarus","no":8,"centroid":[9.065751666238693,46.98114438926425]},"geometry":{"type":"Polygon","coordinates":[[[9.249011701170119,46.915793779377935],[9.226783078307832,46.90604180418042],[9.203647164716472,46.88275647564756],[9.187769576957697,46.87579077907791],[9.152385238523854,46.8799701970197],[9.14013681368137,46.862058405840585],[9.111103510351036,46.849719171917194],[9.094772277227724,46.871014301430144],[9.061202520252026,46.873800580058],[9.045778577857787,46.866834883488345],[9.045778577857787,46.849719171917194],[9.039427542754277,46.846733873387336],[9.009033303330334,46.8109102910291],[8.952781278127814,46.8047406740674],[8.927377137713773,46.795784778477845],[8.915128712871288,46.7987700770077],[8.915128712871288,46.8109102910291],[8.87656885688569,46.813099509950995],[8.882012601260127,46.82902110211021],[8.870671467146716,46.841758375837586],[8.892900090009002,46.84872407240724],[8.908324032403241,46.862058405840585],[8.91739693969397,46.861063306330635],[8.958225022502251,46.8799701970197],[8.952781278127814,46.89808100810081],[8.935089108910892,46.919973197319734],[8.963215121512153,46.9267398739874],[8.955049504950496,46.93907910791079],[8.970473447344736,46.95878207820782],[8.945976597659767,46.966742874287426],[8.931913591359137,46.982067406740676],[8.940986498649867,46.984853685368535],[8.910592259225924,47.00893509350935],[8.909684968496851,47.01709490949095],[8.895168316831684,47.02087628762876],[8.903787578757877,47.02704590459046],[8.901972997299731,47.037792979297926],[8.921026102610263,47.037792979297926],[8.947337533753377,47.04893809380938],[8.950513051305132,47.06107830783078],[8.965936993699371,47.081776377637766],[8.9750099009901,47.08595579557956],[8.954142214221424,47.090732273227324],[8.970473447344736,47.104066606660666],[8.974102610261028,47.11481368136813],[8.965029702970298,47.12177937793779],[8.966390639063906,47.13690489048905],[9.003589558955897,47.17272847284728],[9.004950495049506,47.17272847284728],[9.009033303330334,47.171932393239324],[9.068914491449146,47.1297401740174],[9.10157695769577,47.13272547254726],[9.136054005400542,47.1317303730373],[9.19003780378038,47.12376957695769],[9.185501350135015,47.09789698969897],[9.19139873987399,47.0728204820482],[9.181418541854185,47.05809300930093],[9.137868586858687,47.03878807880788],[9.163726372637264,47.02704590459046],[9.18096489648965,47.02605080508051],[9.20137893789379,47.03500670067007],[9.221339333933393,47.03301650165017],[9.243114311431144,47.01490569056906],[9.249918991899191,46.99480468046804],[9.244475247524754,46.95599579957996],[9.235855985598562,46.94485068506851],[9.249918991899191,46.935894789478944],[9.249011701170119,46.915793779377935]]]}},{"type":"Feature","properties":{"abbr":"ZG","name":"Zug","no":9,"centroid":[8.537489988894073,47.15682372405097]},"geometry":{"type":"Polygon","coordinates":[[[8.49278487848785,47.099887188718874],[8.49414581458146,47.11381858185818],[8.480536453645366,47.12595879587959],[8.45195679567957,47.11381858185818],[8.447420342034205,47.1297401740174],[8.417933393339336,47.12177937793779],[8.412036003600361,47.14088528852885],[8.415211521152116,47.165762776277624],[8.40205580558056,47.17690789078908],[8.406138613861387,47.19501870187019],[8.39434383438344,47.225866786678665],[8.411128712871289,47.24795799579958],[8.422016201620163,47.223080508050806],[8.440615661566158,47.22507070707071],[8.460576057605762,47.21392559255926],[8.484165616561658,47.20775597559756],[8.553573357335734,47.223080508050806],[8.581699369936995,47.212930493049306],[8.594855085508552,47.211935393539356],[8.597123312331235,47.20397459745975],[8.615722772277229,47.198800080008],[8.627971197119713,47.17909710971097],[8.626156615661568,47.171932393239324],[8.661087308730874,47.170937293729374],[8.661540954095411,47.160986298629865],[8.674696669666968,47.15879707970797],[8.691481548154815,47.163971597159716],[8.701008100810082,47.150836283628365],[8.683769576957697,47.14088528852885],[8.686945094509452,47.13491469146915],[8.671521152115211,47.11580878087808],[8.63341494149415,47.093916591659166],[8.608918091809182,47.094911691169116],[8.589411341134115,47.08595579557956],[8.567182718271829,47.08078127812781],[8.562646264626464,47.093916591659166],[8.520457245724574,47.089737173717374],[8.49278487848785,47.099887188718874]]]}},{"type":"Feature","properties":{"abbr":"FR","name":"Fribourg","no":10,"centroid":[7.077054877693406,46.71817056663422]},"geometry":{"type":"MultiPolygon","coordinates":[[[[7.237094509450946,46.553975597559756],[7.223031503150316,46.547805980598056],[7.20715391539154,46.53287948794879],[7.1917299729973,46.5470099009901],[7.158613861386139,46.52690889088909],[7.145004500450046,46.52690889088909],[7.135931593159317,46.50780298029803],[7.105083708370838,46.48889608960896],[7.066070207020703,46.48889608960896],[7.027963996399641,46.46700390039004],[7.016622862286229,46.44789798979898],[6.983506750675068,46.43774797479748],[6.992126012601261,46.44889308930893],[6.979877587758777,46.45904310431043],[6.972165616561657,46.490886288628865],[6.953566156615662,46.502031403140315],[6.934966696669668,46.501036303630364],[6.924986498649866,46.50780298029803],[6.9009432943294335,46.51277847784778],[6.896406840684069,46.51894809480948],[6.881436543654366,46.51277847784778],[6.862837083708372,46.494070607060706],[6.836525652565257,46.49984218421842],[6.811121512151216,46.52491869186919],[6.825184518451846,46.542830483048306],[6.831535553555356,46.53705890589059],[6.855125112511252,46.5408402840284],[6.861022502250226,46.53088928892889],[6.870095409540955,46.54402460246025],[6.887787578757877,46.55775697569757],[6.900036003600361,46.559946194619464],[6.898221422142215,46.571091309130914],[6.885065706570658,46.561936393639364],[6.873724572457246,46.56472267226722],[6.863744374437444,46.57805700570057],[6.848320432043205,46.58283348334833],[6.835164716471648,46.57487268726872],[6.811575157515752,46.58004720472047],[6.794790279027904,46.57487268726872],[6.803409540954096,46.608706070607056],[6.801141314131414,46.63000120012001],[6.803409540954096,46.64592279227923],[6.815204320432044,46.64691789178918],[6.837886588658867,46.662839483948396],[6.853764176417642,46.65607280728073],[6.865105310531054,46.65806300630063],[6.871456345634564,46.672989498949896],[6.862837083708372,46.680950295029504],[6.890055805580559,46.69388658865886],[6.903665166516652,46.71378857885789],[6.916367236723673,46.71796799679968],[6.9358739873987405,46.73309350935094],[6.9408640864086415,46.744835683568354],[6.928615661566157,46.75379157915791],[6.919996399639965,46.75100530053005],[6.908201620162017,46.75797099709971],[6.919996399639965,46.75996119611961],[6.934966696669668,46.77070827082708],[6.942225022502251,46.78404260426043],[6.956741674167417,46.78702790279028],[6.966721872187219,46.802949494949495],[6.958102610261027,46.8109102910291],[6.9658145814581465,46.82802600260026],[6.984414041404141,46.82205540554055],[6.992126012601261,46.83081228122812],[6.988043204320433,46.84394759475948],[6.976702070207021,46.849719171917194],[6.976702070207021,46.861063306330635],[6.988496849684969,46.873004500450044],[6.973526552655266,46.88693589358936],[6.958102610261027,46.88096529652965],[6.964453645364538,46.864844684468444],[6.933152115211522,46.89171237123712],[6.929976597659767,46.89907610761076],[6.8959531953195325,46.924749674967494],[6.928615661566157,46.95281148114812],[6.964453645364538,46.923953595359535],[6.988043204320433,46.917783978397836],[6.988043204320433,46.90405160516052],[7.008457245724573,46.88892609260926],[6.99348694869487,46.87897509750975],[7.008003600360037,46.873004500450044],[7.016169216921693,46.88096529652965],[7.035222322232224,46.873004500450044],[7.029778577857787,46.865839783978394],[7.040212421242125,46.861063306330635],[7.028417641764177,46.850913291329135],[7.042934293429344,46.846733873387336],[7.071060306030604,46.873800580058],[7.061987398739875,46.88375157515752],[7.082855085508552,46.89609080908091],[7.091020702070208,46.90285748574858],[7.057904590459047,46.93907910791079],[7.056543654365437,46.95977717771777],[7.062894689468948,46.970723272327234],[7.052007200720073,46.976892889288926],[7.093742574257426,46.97609680968097],[7.153623762376238,46.985848784878485],[7.217134113411342,47.00694489448945],[7.223031503150316,46.993013501350134],[7.236187218721873,46.984853685368535],[7.20715391539154,46.96395659565957],[7.209422142214222,46.9428604860486],[7.203071107110712,46.9368898889889],[7.208061206120613,46.90882808280828],[7.188100810081009,46.89987218721872],[7.208061206120613,46.90285748574858],[7.217134113411342,46.89808100810081],[7.259323132313232,46.89808100810081],[7.282912691269128,46.88972217221722],[7.288356435643565,46.89390159015902],[7.327823582358237,46.89390159015902],[7.351413141314132,46.88793099309931],[7.358671467146715,46.862058405840585],[7.351413141314132,46.85409760976098],[7.33508190819082,46.849719171917194],[7.330999099909992,46.857878987898786],[7.316028802880289,46.863053505350535],[7.301512151215122,46.851908390839085],[7.330999099909992,46.83379757975798],[7.320111611161117,46.82583678367837],[7.32101890189019,46.8148906890689],[7.311038703870388,46.80991519151915],[7.301512151215122,46.793993599359936],[7.303780378037804,46.782848484848486],[7.290171017101711,46.77289748974897],[7.302873087308732,46.75996119611961],[7.296068406840685,46.736874887488746],[7.305141314131414,46.71796799679968],[7.348691269126913,46.71179837983798],[7.344608460846086,46.69906110611061],[7.36320792079208,46.69906110611061],[7.376363636363637,46.693090509050904],[7.378178217821783,46.68373657365736],[7.368198019801981,46.65507770777078],[7.358671467146715,46.64373357335734],[7.344608460846086,46.65507770777078],[7.32101890189019,46.65507770777078],[7.330091809180919,46.64174337433743],[7.312853285328534,46.63696689668967],[7.317843384338435,46.61885608560856],[7.315121512151216,46.5997501750175],[7.32101890189019,46.59178937893789],[7.298790279027903,46.57905210521052],[7.281551755175518,46.58482368236824],[7.259323132313232,46.562931493149314],[7.237094509450946,46.553975597559756]]],[[[6.779366336633664,46.852903490349036],[6.866012601260127,46.90882808280828],[6.8959531953195325,46.88275647564756],[6.9109234923492355,46.88096529652965],[6.9209036903690375,46.871014301430144],[6.891416741674168,46.864844684468444],[6.913191719171918,46.865839783978394],[6.9308838883888395,46.852903490349036],[6.918635463546355,46.846733873387336],[6.903211521152116,46.82802600260026],[6.9109234923492355,46.802949494949495],[6.919996399639965,46.80991519151915],[6.9308838883888395,46.803944594459445],[6.916367236723673,46.78503770377038],[6.880075607560757,46.77508670867087],[6.858754275427543,46.78782398239824],[6.825184518451846,46.77289748974897],[6.805224122412242,46.78603280328033],[6.797965796579659,46.782848484848486],[6.778459045904591,46.793993599359936],[6.778459045904591,46.800959295929594],[6.741713771377138,46.82703090309031],[6.779366336633664,46.852903490349036]]]]}},{"type":"Feature","properties":{"abbr":"SO","name":"Solothurn","no":11,"centroid":[7.638972980845362,47.30448118709367]},"geometry":{"type":"MultiPolygon","coordinates":[[[[7.557821782178219,47.32179437943795],[7.572792079207922,47.3287600760076],[7.551470747074708,47.3450797079708],[7.536500450045005,47.350055205520555],[7.540129612961297,47.36000620062006],[7.521983798379839,47.36478267826783],[7.523344734473448,47.37174837483748],[7.49113591359136,47.36995719571957],[7.458019801980199,47.37075327532753],[7.436698469846985,47.37970917091709],[7.443503150315032,47.38886408640864],[7.4407812781278135,47.401004300430046],[7.453029702970298,47.403989598959896],[7.477980198019803,47.399810181018104],[7.477980198019803,47.38985918591859],[7.492043204320433,47.38508270827083],[7.518354635463547,47.38806800680068],[7.531056705670568,47.403989598959896],[7.526066606660667,47.411950395039504],[7.579596759675969,47.41473667366736],[7.567801980198021,47.42289648964896],[7.568709270927093,47.43603180318032],[7.578689468946895,47.43006120612061],[7.615888388838885,47.43284748474847],[7.615888388838885,47.44498769876988],[7.625868586858687,47.46309850985099],[7.60500090009001,47.471059305930595],[7.605908190819083,47.48897109710971],[7.64083888388839,47.48280148014801],[7.65490189018902,47.48698089808981],[7.651726372637264,47.49593679367937],[7.665789378937895,47.49693189318932],[7.669418541854186,47.48479167916792],[7.699359135913592,47.4810103010301],[7.709339333933394,47.4688700870087],[7.684842484248426,47.44777397739774],[7.686203420342035,47.43603180318032],[7.679398739873988,47.41692589258926],[7.657170117011702,47.408965096509654],[7.63312691269127,47.409960196019604],[7.643560756075608,47.36697189718972],[7.699359135913592,47.37174837483748],[7.727938793879389,47.36896209620962],[7.733382538253826,47.35801600160016],[7.75334293429343,47.3430895089509],[7.768766876687669,47.33871107110711],[7.793717371737174,47.33871107110711],[7.801429342934294,47.36100130013001],[7.833638163816382,47.36697189718972],[7.836360036003601,47.37274347434744],[7.875827182718273,47.38508270827083],[7.869022502250226,47.39503370337034],[7.888075607560757,47.406775877587755],[7.909850585058507,47.3978199819982],[7.932986498649866,47.404785678567855],[7.934347434743475,47.411950395039504],[7.962019801980199,47.42170237023702],[7.946595859585959,47.44279847984799],[7.956576057605761,47.45493869386939],[7.967917191719173,47.46309850985099],[7.98107290729073,47.44876907690769],[7.986516651665167,47.42807100710071],[7.962927092709272,47.42170237023702],[8.00647704770477,47.406775877587755],[8.009652565256527,47.39503370337034],[8.026437443744374,47.39602880288029],[8.030520252025203,47.38289348934894],[8.026437443744374,47.36896209620962],[8.009652565256527,47.35682188218822],[8.00647704770477,47.33871107110711],[7.976082808280829,47.3227894789479],[7.947956795679569,47.31801300130013],[7.947956795679569,47.3329394939494],[7.9189234923492355,47.33393459345935],[7.906675067506751,47.342094409440946],[7.886714671467147,47.31184338433843],[7.859949594959497,47.30487768776877],[7.844072007200721,47.277810981098106],[7.824565256525654,47.26606880688069],[7.818214221422143,47.26188938893889],[7.800975697569758,47.26805900590059],[7.785098109810982,47.25591879187919],[7.77375697569757,47.26706390639064],[7.756972097209722,47.25790899089909],[7.73292889288929,47.25870507050705],[7.72521692169217,47.2698501850185],[7.711607560756076,47.2740296029603],[7.701173717371738,47.286766876687665],[7.684842484248426,47.29373257325732],[7.648550855085509,47.281990399039906],[7.613166516651666,47.280995299529955],[7.579596759675969,47.2760198019802],[7.593659765976598,47.26606880688069],[7.595927992799281,47.24596779677968],[7.625868586858687,47.225866786678665],[7.639477947794781,47.230046204620464],[7.648097209720973,47.21870207020702],[7.6471899189919,47.201785378537856],[7.661706570657066,47.2007902790279],[7.678037803780379,47.18287848784878],[7.673047704770478,47.16775297529753],[7.659438343834384,47.16775297529753],[7.644921692169218,47.14884608460846],[7.639477947794781,47.15402060206021],[7.593206120612062,47.15302550255026],[7.585947794779479,47.14785098509851],[7.581411341134114,47.15879707970797],[7.558729072907291,47.16775297529753],[7.520169216921693,47.15979217921792],[7.528788478847885,47.14884608460846],[7.510189018901891,47.12595879587959],[7.492043204320433,47.11680388038804],[7.461195319531954,47.10784798479848],[7.474351035103511,47.08695089508951],[7.468000000000001,47.07879107910791],[7.4407812781278135,47.07381558155816],[7.433069306930694,47.099887188718874],[7.395870387038705,47.092921492149216],[7.381353735373538,47.095707770777075],[7.370012601260127,47.12098329832983],[7.386343834383439,47.12595879587959],[7.403582358235824,47.11799799979998],[7.436698469846985,47.12277447744774],[7.422181818181819,47.1359097909791],[7.439873987398741,47.1440696069607],[7.449854185418543,47.15601080108011],[7.468000000000001,47.150836283628365],[7.492950495049506,47.162976497649765],[7.496579657965797,47.16994219421942],[7.481155715571558,47.171932393239324],[7.471175517551756,47.18108730873087],[7.474351035103511,47.19083928392839],[7.448039603960397,47.18904810481048],[7.446225022502251,47.18108730873087],[7.4308010801080115,47.18287848784878],[7.432162016201621,47.17571377137714],[7.410840684068408,47.161981398139815],[7.383168316831684,47.15979217921792],[7.39133393339334,47.165762776277624],[7.380900090009002,47.18387358735873],[7.365929792979299,47.19382458245825],[7.353681368136814,47.19183438343834],[7.340072007200721,47.217706970697066],[7.36320792079208,47.219896189618964],[7.42082088208821,47.24278347834783],[7.415830783078309,47.25173937393739],[7.439873987398741,47.26288448844885],[7.468000000000001,47.26507370737074],[7.484331233123313,47.283980598059806],[7.521076507650766,47.29373257325732],[7.531963996399641,47.29373257325732],[7.546480648064807,47.30587278727873],[7.557821782178219,47.32179437943795]]],[[[7.375909990999101,47.41374157415741],[7.380900090009002,47.43185238523852],[7.399953195319533,47.43483768376838],[7.42082088208821,47.44598279827983],[7.438059405940595,47.44598279827983],[7.454390639063907,47.42588178817882],[7.446678667866787,47.41473667366736],[7.413562556255626,47.410955295529554],[7.375909990999101,47.41374157415741]]],[[[7.444410441044105,47.461904390439045],[7.456205220522053,47.472054405440545],[7.422181818181819,47.48379657965796],[7.434430243024303,47.49792699269927],[7.471175517551756,47.48001520152015],[7.487960396039605,47.482005400540054],[7.511096309630964,47.50290249024903],[7.531963996399641,47.49693189318932],[7.536500450045005,47.48479167916792],[7.526520252025203,47.473845584558454],[7.531056705670568,47.460909290929095],[7.495218721872188,47.459914191419145],[7.454390639063907,47.449764176417645],[7.444410441044105,47.461904390439045]]]]}},{"type":"Feature","properties":{"abbr":"BS","name":"Basel-Stadt","no":12,"centroid":[7.615071303770259,47.56462832136231]},"geometry":{"type":"Polygon","coordinates":[[[7.633580558055806,47.56081728172817],[7.618156615661567,47.55902610261026],[7.619517551755177,47.53892509250925],[7.613620162016202,47.53892509250925],[7.593206120612062,47.519819181918194],[7.58322592259226,47.532954495449545],[7.586401440144015,47.54171137113711],[7.555099909991,47.54370157015701],[7.56281188118812,47.55803100310031],[7.555099909991,47.56499669966997],[7.565987398739875,47.576738873887386],[7.585040504050406,47.57594279427943],[7.588669666966697,47.590073207320735],[7.60500090009001,47.58509770977098],[7.60500090009001,47.577733973397336],[7.619517551755177,47.576738873887386],[7.643107110711072,47.591068306830685],[7.645828982898291,47.5968398839884],[7.674862286228624,47.592063406340635],[7.686203420342035,47.56698689868987],[7.648097209720973,47.55982218221822],[7.633580558055806,47.56081728172817]]]}},{"type":"Feature","properties":{"abbr":"BL","name":"Basel-Landschaft","no":13,"centroid":[7.702698349131468,47.4513567170881]},"geometry":{"type":"MultiPolygon","coordinates":[[[[7.436698469846985,47.37970917091709],[7.413562556255626,47.37970917091709],[7.4144698469847,47.39403860386039],[7.398592259225923,47.39682488248825],[7.375909990999101,47.41374157415741],[7.413562556255626,47.410955295529554],[7.446678667866787,47.41473667366736],[7.454390639063907,47.42588178817882],[7.438059405940595,47.44598279827983],[7.42082088208821,47.44598279827983],[7.429893789378939,47.4587200720072],[7.444410441044105,47.461904390439045],[7.454390639063907,47.449764176417645],[7.495218721872188,47.459914191419145],[7.531056705670568,47.460909290929095],[7.526520252025203,47.473845584558454],[7.536500450045005,47.48479167916792],[7.531963996399641,47.49693189318932],[7.511096309630964,47.50290249024903],[7.506106210621063,47.51404760476048],[7.523344734473448,47.51504270427043],[7.531056705670568,47.526784878487845],[7.518354635463547,47.53474567456745],[7.502023402340235,47.5277799779978],[7.498394239423943,47.5399201920192],[7.518354635463547,47.54589078907891],[7.555099909991,47.56499669966997],[7.56281188118812,47.55803100310031],[7.555099909991,47.54370157015701],[7.586401440144015,47.54171137113711],[7.58322592259226,47.532954495449545],[7.593206120612062,47.519819181918194],[7.613620162016202,47.53892509250925],[7.619517551755177,47.53892509250925],[7.618156615661567,47.55902610261026],[7.633580558055806,47.56081728172817],[7.648550855085509,47.5480800080008],[7.661706570657066,47.54489568956895],[7.676223222322233,47.532954495449545],[7.693008100810082,47.531959395939595],[7.713422142214222,47.53892509250925],[7.737011701170118,47.526784878487845],[7.762869486948696,47.524794679467945],[7.790088208820883,47.51882408240824],[7.792810081008102,47.50071327132713],[7.806419441944195,47.49693189318932],[7.8313699369937,47.51504270427043],[7.833184518451846,47.5337505750575],[7.851330333033304,47.53474567456745],[7.864032403240325,47.51882408240824],[7.874466246624664,47.51782898289829],[7.893972997299731,47.50608680868087],[7.9039531953195326,47.48698089808981],[7.932986498649866,47.4810103010301],[7.944327632763277,47.48598579857986],[7.939791179117913,47.461904390439045],[7.9488640864086415,47.46409360936094],[7.956576057605761,47.45493869386939],[7.946595859585959,47.44279847984799],[7.962019801980199,47.42170237023702],[7.934347434743475,47.411950395039504],[7.932986498649866,47.404785678567855],[7.909850585058507,47.3978199819982],[7.888075607560757,47.406775877587755],[7.869022502250226,47.39503370337034],[7.875827182718273,47.38508270827083],[7.836360036003601,47.37274347434744],[7.833638163816382,47.36697189718972],[7.801429342934294,47.36100130013001],[7.793717371737174,47.33871107110711],[7.768766876687669,47.33871107110711],[7.75334293429343,47.3430895089509],[7.733382538253826,47.35801600160016],[7.727938793879389,47.36896209620962],[7.699359135913592,47.37174837483748],[7.643560756075608,47.36697189718972],[7.63312691269127,47.409960196019604],[7.657170117011702,47.408965096509654],[7.679398739873988,47.41692589258926],[7.686203420342035,47.43603180318032],[7.684842484248426,47.44777397739774],[7.709339333933394,47.4688700870087],[7.699359135913592,47.4810103010301],[7.669418541854186,47.48479167916792],[7.665789378937895,47.49693189318932],[7.651726372637264,47.49593679367937],[7.65490189018902,47.48698089808981],[7.64083888388839,47.48280148014801],[7.605908190819083,47.48897109710971],[7.60500090009001,47.471059305930595],[7.625868586858687,47.46309850985099],[7.615888388838885,47.44498769876988],[7.615888388838885,47.43284748474847],[7.578689468946895,47.43006120612061],[7.568709270927093,47.43603180318032],[7.567801980198021,47.42289648964896],[7.579596759675969,47.41473667366736],[7.526066606660667,47.411950395039504],[7.531056705670568,47.403989598959896],[7.518354635463547,47.38806800680068],[7.492043204320433,47.38508270827083],[7.477980198019803,47.38985918591859],[7.477980198019803,47.399810181018104],[7.453029702970298,47.403989598959896],[7.4407812781278135,47.401004300430046],[7.443503150315032,47.38886408640864],[7.436698469846985,47.37970917091709]]],[[[7.375909990999101,47.41374157415741],[7.356403240324033,47.41473667366736],[7.340072007200721,47.43006120612061],[7.326009000900091,47.43185238523852],[7.326009000900091,47.43981318131813],[7.346423042304231,47.43483768376838],[7.380900090009002,47.43185238523852],[7.375909990999101,47.41374157415741]]]]}},{"type":"Feature","properties":{"abbr":"SH","name":"Schaffhausen","no":14,"centroid":[8.592669293253277,47.71360897867118]},"geometry":{"type":"MultiPolygon","coordinates":[[[[8.662448244824484,47.68600080008001],[8.64248784878488,47.69495669566957],[8.624795679567958,47.69197139713972],[8.62706390639064,47.6780400040004],[8.613454545454546,47.6780400040004],[8.606196219621964,47.66908410841084],[8.598030603060307,47.67286548654865],[8.579431143114313,47.6617203720372],[8.564007200720074,47.67007920792079],[8.526354635463548,47.65992919291929],[8.531798379837985,47.645798779877985],[8.49278487848785,47.646793879387936],[8.466473447344736,47.641022302230226],[8.466019801980199,47.65674487448745],[8.456493249324932,47.652963496349635],[8.436532853285328,47.65674487448745],[8.423830783078309,47.66709390939094],[8.412036003600361,47.665899789979],[8.406138613861387,47.67485568556856],[8.420655265526554,47.68401060106011],[8.403870387038705,47.69774297429743],[8.445152115211522,47.72281948194819],[8.455132313231324,47.722023402340234],[8.449688568856887,47.73794499449945],[8.456039603960397,47.74889108910891],[8.485980198019803,47.771977397739775],[8.509569756975699,47.77575877587759],[8.519549954995501,47.769987198719875],[8.52590099009901,47.77794799479948],[8.553119711971199,47.78471467146715],[8.565821782178219,47.77794799479948],[8.57716291629163,47.78172937293729],[8.561738973897391,47.79287448744874],[8.568090009000901,47.808],[8.574894689468948,47.7998401840184],[8.611186318631864,47.8018303830383],[8.621620162016203,47.79486468646865],[8.614815481548156,47.78272447244724],[8.622527452745276,47.77675387538754],[8.618898289828984,47.766802880288026],[8.627971197119713,47.75884208420842],[8.652921692169219,47.772972497249725],[8.644756075607562,47.78690389038904],[8.657004500450046,47.7998401840184],[8.666984698469848,47.78809800980098],[8.681047704770478,47.78690389038904],[8.682408640864088,47.770982298229825],[8.69238883888389,47.75605580558056],[8.71144194419442,47.76501170117012],[8.725958595859588,47.76302150215022],[8.740021602160217,47.75307050705071],[8.739114311431145,47.74590579057906],[8.723690369036905,47.74590579057906],[8.710988298829884,47.72998419841984],[8.717792979297931,47.722023402340234],[8.735485148514853,47.715853785378535],[8.727773177317733,47.69296649664967],[8.717792979297931,47.690777277727776],[8.701461746174619,47.714858685868585],[8.690120612061207,47.708888088808884],[8.663809180918093,47.713067506750676],[8.67696489648965,47.69774297429743],[8.660180018001801,47.690777277727776],[8.662448244824484,47.68600080008001]]],[[[8.795366336633665,47.6760498049805],[8.796727272727274,47.702917491749176],[8.768601260126013,47.706897889788976],[8.771323132313231,47.71784398439844],[8.795366336633665,47.7349596959696],[8.806707470747076,47.7369498949895],[8.804439243924394,47.72580478047805],[8.82394599459946,47.711077307730775],[8.846174617461747,47.711077307730775],[8.84844284428443,47.702917491749176],[8.870671467146716,47.704708670867085],[8.87520792079208,47.69396159615962],[8.862052205220524,47.69296649664967],[8.84980378037804,47.68082628262826],[8.863866786678669,47.67903510351035],[8.872939693969398,47.67007920792079],[8.87520792079208,47.654754675467544],[8.857515751575159,47.64878407840784],[8.844360036003602,47.652963496349635],[8.853886588658867,47.66092429242924],[8.836194419441945,47.6718703870387],[8.816687668766878,47.6780400040004],[8.795366336633665,47.6760498049805]]],[[[8.562646264626464,47.59902910291029],[8.581699369936995,47.595844784478444],[8.595762376237625,47.60579577957796],[8.594855085508552,47.5968398839884],[8.578070207020703,47.58887908790879],[8.579431143114313,47.57594279427943],[8.56945094509451,47.57196239623963],[8.559470747074709,47.55285648564856],[8.550851485148517,47.55703590359036],[8.53497389738974,47.586888888888886],[8.562646264626464,47.59902910291029]]]]}},{"type":"Feature","properties":{"abbr":"AR","name":"Appenzell Ausserrhoden","no":15,"centroid":[9.36701599748908,47.365427898323425]},"geometry":{"type":"Polygon","coordinates":[[[9.617825382538255,47.43702690269027],[9.558851485148516,47.43384258425843],[9.582441044104412,47.41991119111911],[9.582441044104412,47.409960196019604],[9.554768676867688,47.39682488248825],[9.541612961296131,47.40777097709771],[9.573368136813682,47.406775877587755],[9.576997299729975,47.41473667366736],[9.558851485148516,47.41792099209921],[9.554768676867688,47.42707590759076],[9.538891089108912,47.43185238523852],[9.5225598559856,47.42090629062906],[9.53299369936994,47.410955295529554],[9.515755175517553,47.405780778077805],[9.508950495049506,47.39503370337034],[9.498970297029704,47.36896209620962],[9.502599459945996,47.346870887088706],[9.487175517551757,47.350055205520555],[9.465854185418543,47.346870887088706],[9.43818181818182,47.355826782678264],[9.430923492349237,47.37075327532753],[9.411870387038705,47.36896209620962],[9.400529252925294,47.37791799179918],[9.371949594959498,47.38408760876088],[9.349720972097211,47.38289348934894],[9.36378397839784,47.35801600160016],[9.343823582358237,47.352045404540455],[9.328853285328535,47.330750275027505],[9.314336633663368,47.32696889688969],[9.31796579657966,47.30905710571057],[9.312975697569758,47.291941394139414],[9.332936093609362,47.2698501850185],[9.342916291629164,47.24895309530953],[9.31796579657966,47.24795799579958],[9.273962196219623,47.2698501850185],[9.255816381638166,47.26288448844885],[9.236763276327634,47.26507370737074],[9.205461746174619,47.276815881588156],[9.224061206120613,47.29273747374737],[9.209998199819983,47.32079927992799],[9.233134113411342,47.33194439443945],[9.22451485148515,47.342094409440946],[9.205461746174619,47.33871107110711],[9.19139873987399,47.36179737973797],[9.207729972997301,47.36179737973797],[9.220885688568858,47.37274347434744],[9.218617461746176,47.38508270827083],[9.23086588658866,47.38707290729073],[9.233134113411342,47.3978199819982],[9.272147614761478,47.401004300430046],[9.303902790279029,47.39503370337034],[9.325224122412243,47.401004300430046],[9.35743294329433,47.401999399939996],[9.382837083708372,47.406775877587755],[9.401890189018903,47.401004300430046],[9.425933393339335,47.406775877587755],[9.435006300630064,47.42090629062906],[9.435913591359137,47.43483768376838],[9.449522952295231,47.43085728572857],[9.531179117911792,47.45394359435944],[9.526189018901892,47.461904390439045],[9.542520252025204,47.4688700870087],[9.546149414941496,47.46309850985099],[9.583801980198022,47.461904390439045],[9.62100090009001,47.44777397739774],[9.63007380738074,47.43981318131813],[9.617825382538255,47.43702690269027]]]}},{"type":"Feature","properties":{"abbr":"SG","name":"St. Gallen","no":17,"centroid":[9.27517713240211,47.2337565774484]},"geometry":{"type":"MultiPolygon","coordinates":[[[[9.004950495049506,47.17272847284728],[9.003589558955897,47.17272847284728],[8.981814581458147,47.18287848784878],[8.961854185418543,47.18009220922092],[8.958225022502251,47.198800080008],[8.965029702970298,47.19800400040004],[8.969566156615663,47.217706970697066],[8.940986498649867,47.217706970697066],[8.841184518451847,47.20775597559756],[8.828482448244825,47.20875107510751],[8.808068406840686,47.220891289128915],[8.795366336633665,47.23900210021002],[8.803078307830784,47.24696289628963],[8.826214221422143,47.25074427442744],[8.837555355535555,47.24696289628963],[8.869764176417643,47.24477367736774],[8.872939693969398,47.25173937393739],[8.93282088208821,47.25870507050705],[8.944162016201622,47.26885508550855],[8.947337533753377,47.281990399039906],[8.93418181818182,47.28776197619762],[8.948698469846985,47.291941394139414],[8.956410441044106,47.30487768776877],[8.9750099009901,47.30706690669067],[8.984990099009902,47.31900810081008],[8.97637083708371,47.32696889688969],[8.978639063906392,47.337715971597156],[8.971834383438345,47.352045404540455],[8.96094689468947,47.355826782678264],[8.944162016201622,47.37592779277928],[8.971834383438345,47.390854285428546],[8.97637083708371,47.410955295529554],[8.98907290729073,47.410955295529554],[8.989526552655267,47.42906610661066],[9.011755175517553,47.43284748474847],[9.021735373537355,47.44180338033804],[9.028086408640865,47.43702690269027],[9.063017101710173,47.44598279827983],[9.027179117911793,47.457724972497246],[9.02491089108911,47.4688700870087],[9.010394239423944,47.472054405440545],[9.003589558955897,47.4810103010301],[9.009486948694871,47.48797599759976],[9.043056705670569,47.49494169416942],[9.056212421242126,47.48698089808981],[9.081616561656165,47.49494169416942],[9.089328532853287,47.482005400540054],[9.141497749774977,47.48379657965796],[9.1591899189919,47.48996619661966],[9.16554095409541,47.50389758975898],[9.18232583258326,47.49076227622762],[9.209998199819983,47.48897109710971],[9.21816381638164,47.48379657965796],[9.263074707470748,47.474840684068404],[9.278045004500452,47.4790201020102],[9.303902790279029,47.4790201020102],[9.319780378037805,47.49693189318932],[9.307531953195321,47.49792699269927],[9.311161116111613,47.50986818681868],[9.302995499549956,47.51603780378038],[9.28076687668767,47.50608680868087],[9.28212781278128,47.521013301330136],[9.316604860486049,47.530964296429644],[9.337926192619264,47.528974097409744],[9.342916291629164,47.519819181918194],[9.356072007200721,47.519819181918194],[9.340648064806482,47.50389758975898],[9.37421782178218,47.49971817181718],[9.37285688568857,47.48996619661966],[9.396446444644464,47.474840684068404],[9.405972997299731,47.49374757475748],[9.428655265526555,47.50290249024903],[9.502599459945996,47.54688588858886],[9.51484788478848,47.53673587358736],[9.551139513951396,47.53673587358736],[9.5661098109811,47.49175737573757],[9.58017281728173,47.48379657965796],[9.594689468946896,47.46309850985099],[9.60693789378938,47.470064206420645],[9.624630063006302,47.456729872987296],[9.643683168316834,47.455734773477346],[9.659107110711073,47.44876907690769],[9.643683168316834,47.43483768376838],[9.650941494149416,47.404785678567855],[9.674077407740775,47.390854285428546],[9.673170117011702,47.380704270427046],[9.657746174617463,47.36896209620962],[9.62372277227723,47.36577777777778],[9.601494149414943,47.34886108610861],[9.590153015301532,47.32179437943795],[9.57881188118812,47.30905710571057],[9.553407740774079,47.29373257325732],[9.547056705670569,47.279801180118014],[9.529364536453647,47.270845284528455],[9.518930693069308,47.24178837883788],[9.503960396039606,47.22507070707071],[9.489443744374439,47.19800400040004],[9.485814581458147,47.17790299029903],[9.494887488748876,47.156806880688066],[9.513033303330335,47.1317303730373],[9.519384338433845,47.103071507150716],[9.515755175517553,47.08774697469747],[9.474927092709272,47.06585478547855],[9.475834383438345,47.05192339233923],[9.508043204320433,47.01709490949095],[9.518930693069308,46.99997919791979],[9.546149414941496,46.976892889288926],[9.519384338433845,46.970723272327234],[9.518023402340235,46.95599579957996],[9.504867686768678,46.95380658065807],[9.479463546354637,46.925744774477444],[9.48808280828083,46.912012401240126],[9.467215121512153,46.89987218721872],[9.467215121512153,46.88594079407941],[9.453605760576059,46.88594079407941],[9.449069306930694,46.873004500450044],[9.43319171917192,46.88474667466747],[9.3819297929793,46.89808100810081],[9.360608460846086,46.89708590859086],[9.3383798379838,46.90186238623862],[9.27441584158416,46.90703690369037],[9.26670387038704,46.90405160516052],[9.249011701170119,46.915793779377935],[9.249918991899191,46.935894789478944],[9.235855985598562,46.94485068506851],[9.244475247524754,46.95599579957996],[9.249918991899191,46.99480468046804],[9.243114311431144,47.01490569056906],[9.221339333933393,47.03301650165017],[9.20137893789379,47.03500670067007],[9.18096489648965,47.02605080508051],[9.163726372637264,47.02704590459046],[9.137868586858687,47.03878807880788],[9.181418541854185,47.05809300930093],[9.19139873987399,47.0728204820482],[9.185501350135015,47.09789698969897],[9.19003780378038,47.12376957695769],[9.136054005400542,47.1317303730373],[9.10157695769577,47.13272547254726],[9.068914491449146,47.1297401740174],[9.009033303330334,47.171932393239324],[9.004950495049506,47.17272847284728]],[[9.342916291629164,47.24895309530953],[9.36378397839784,47.24377857785778],[9.3819297929793,47.23382758275827],[9.401890189018903,47.23800700070007],[9.41867506750675,47.24795799579958],[9.43818181818182,47.25074427442744],[9.473112511251127,47.27303450345035],[9.48808280828083,47.28776197619762],[9.488990099009902,47.302091409140914],[9.501692169216923,47.32975517551755],[9.502599459945996,47.346870887088706],[9.498970297029704,47.36896209620962],[9.508950495049506,47.39503370337034],[9.542520252025204,47.399810181018104],[9.554768676867688,47.39682488248825],[9.582441044104412,47.409960196019604],[9.604669666966698,47.41374157415741],[9.58788478847885,47.42488668866886],[9.617825382538255,47.43702690269027],[9.63007380738074,47.43981318131813],[9.62100090009001,47.44777397739774],[9.583801980198022,47.461904390439045],[9.546149414941496,47.46309850985099],[9.542520252025204,47.4688700870087],[9.526189018901892,47.461904390439045],[9.531179117911792,47.45394359435944],[9.449522952295231,47.43085728572857],[9.435913591359137,47.43483768376838],[9.435006300630064,47.42090629062906],[9.425933393339335,47.406775877587755],[9.401890189018903,47.401004300430046],[9.382837083708372,47.406775877587755],[9.35743294329433,47.401999399939996],[9.325224122412243,47.401004300430046],[9.303902790279029,47.39503370337034],[9.272147614761478,47.401004300430046],[9.233134113411342,47.3978199819982],[9.23086588658866,47.38707290729073],[9.218617461746176,47.38508270827083],[9.220885688568858,47.37274347434744],[9.207729972997301,47.36179737973797],[9.19139873987399,47.36179737973797],[9.205461746174619,47.33871107110711],[9.22451485148515,47.342094409440946],[9.233134113411342,47.33194439443945],[9.209998199819983,47.32079927992799],[9.224061206120613,47.29273747374737],[9.205461746174619,47.276815881588156],[9.236763276327634,47.26507370737074],[9.255816381638166,47.26288448844885],[9.273962196219623,47.2698501850185],[9.31796579657966,47.24795799579958],[9.342916291629164,47.24895309530953]]]]}},{"type":"Feature","properties":{"abbr":"AI","name":"Appenzell Innerrhoden","no":16,"centroid":[9.416055917697568,47.317434224349675]},"geometry":{"type":"MultiPolygon","coordinates":[[[[9.502599459945996,47.346870887088706],[9.501692169216923,47.32975517551755],[9.488990099009902,47.302091409140914],[9.48808280828083,47.28776197619762],[9.473112511251127,47.27303450345035],[9.43818181818182,47.25074427442744],[9.41867506750675,47.24795799579958],[9.401890189018903,47.23800700070007],[9.3819297929793,47.23382758275827],[9.36378397839784,47.24377857785778],[9.342916291629164,47.24895309530953],[9.332936093609362,47.2698501850185],[9.312975697569758,47.291941394139414],[9.31796579657966,47.30905710571057],[9.314336633663368,47.32696889688969],[9.328853285328535,47.330750275027505],[9.343823582358237,47.352045404540455],[9.36378397839784,47.35801600160016],[9.349720972097211,47.38289348934894],[9.371949594959498,47.38408760876088],[9.400529252925294,47.37791799179918],[9.411870387038705,47.36896209620962],[9.430923492349237,47.37075327532753],[9.43818181818182,47.355826782678264],[9.465854185418543,47.346870887088706],[9.487175517551757,47.350055205520555],[9.502599459945996,47.346870887088706]]],[[[9.554768676867688,47.39682488248825],[9.542520252025204,47.399810181018104],[9.508950495049506,47.39503370337034],[9.515755175517553,47.405780778077805],[9.53299369936994,47.410955295529554],[9.5225598559856,47.42090629062906],[9.538891089108912,47.43185238523852],[9.554768676867688,47.42707590759076],[9.558851485148516,47.41792099209921],[9.576997299729975,47.41473667366736],[9.573368136813682,47.406775877587755],[9.541612961296131,47.40777097709771],[9.554768676867688,47.39682488248825]]],[[[9.617825382538255,47.43702690269027],[9.58788478847885,47.42488668866886],[9.604669666966698,47.41374157415741],[9.582441044104412,47.409960196019604],[9.582441044104412,47.41991119111911],[9.558851485148516,47.43384258425843],[9.617825382538255,47.43702690269027]]]]}},{"type":"Feature","properties":{"abbr":"AG","name":"Aargau","no":19,"centroid":[8.156747028479444,47.40928349990171]},"geometry":{"type":"Polygon","coordinates":[[[8.411128712871289,47.24795799579958],[8.39434383438344,47.225866786678665],[8.406138613861387,47.19501870187019],[8.40205580558056,47.17690789078908],[8.415211521152116,47.165762776277624],[8.412036003600361,47.14088528852885],[8.376198019801981,47.13909410941094],[8.362135013501351,47.1460598059806],[8.358052205220524,47.156806880688066],[8.33809180918092,47.17909710971097],[8.325389738973898,47.21492069206921],[8.314502250225024,47.231041304130414],[8.314502250225024,47.23999719971997],[8.29590279027903,47.25870507050705],[8.299985598559857,47.270845284528455],[8.2868298829883,47.278806080608064],[8.25099189918992,47.285771777177715],[8.241465346534653,47.2720394039404],[8.227855985598561,47.27303450345035],[8.220144014401441,47.25273447344735],[8.207895589558957,47.24795799579958],[8.204720072007202,47.225866786678665],[8.176140414041406,47.222085408540856],[8.156180018001802,47.23900210021002],[8.174779477947796,47.23999719971997],[8.174779477947796,47.25273447344735],[8.131683168316833,47.24596779677968],[8.124878487848786,47.24099229922992],[8.10900090009001,47.24477367736774],[8.099020702070208,47.25989918991899],[8.088133213321333,47.26288448844885],[8.072709270927094,47.25492369236924],[8.055924392439245,47.25591879187919],[8.063182718271829,47.24696289628963],[8.029612961296131,47.24178837883788],[8.015096309630964,47.24377857785778],[8.016003600360037,47.25989918991899],[7.99513591359136,47.281990399039906],[7.986516651665167,47.2760198019802],[7.956122412241225,47.276815881588156],[7.9488640864086415,47.27303450345035],[7.961112511251126,47.2537295729573],[7.932986498649866,47.23581778177818],[7.909850585058507,47.24377857785778],[7.868115211521153,47.23681288128813],[7.854052205220523,47.232036403640365],[7.83908190819082,47.23382758275827],[7.825926192619263,47.24596779677968],[7.824565256525654,47.26606880688069],[7.844072007200721,47.277810981098106],[7.859949594959497,47.30487768776877],[7.886714671467147,47.31184338433843],[7.906675067506751,47.342094409440946],[7.9189234923492355,47.33393459345935],[7.947956795679569,47.3329394939494],[7.947956795679569,47.31801300130013],[7.976082808280829,47.3227894789479],[8.00647704770477,47.33871107110711],[8.009652565256527,47.35682188218822],[8.026437443744374,47.36896209620962],[8.030520252025203,47.38289348934894],[8.026437443744374,47.39602880288029],[8.009652565256527,47.39503370337034],[8.00647704770477,47.406775877587755],[7.962927092709272,47.42170237023702],[7.986516651665167,47.42807100710071],[7.98107290729073,47.44876907690769],[7.967917191719173,47.46309850985099],[7.956576057605761,47.45493869386939],[7.9488640864086415,47.46409360936094],[7.939791179117913,47.461904390439045],[7.944327632763277,47.48598579857986],[7.932986498649866,47.4810103010301],[7.9039531953195326,47.48698089808981],[7.893972997299731,47.50608680868087],[7.874466246624664,47.51782898289829],[7.864032403240325,47.51882408240824],[7.851330333033304,47.53474567456745],[7.833184518451846,47.5337505750575],[7.8313699369937,47.51504270427043],[7.806419441944195,47.49693189318932],[7.792810081008102,47.50071327132713],[7.790088208820883,47.51882408240824],[7.762869486948696,47.524794679467945],[7.737011701170118,47.526784878487845],[7.713422142214222,47.53892509250925],[7.751074707470748,47.54370157015701],[7.758786678667867,47.54907510751075],[7.783737173717372,47.55285648564856],[7.810048604860487,47.56778297829783],[7.818214221422143,47.586888888888886],[7.846340234023403,47.581913391339135],[7.86085688568857,47.587883988398836],[7.892158415841585,47.587883988398836],[7.911211521152116,47.570768276827685],[7.906675067506751,47.55902610261026],[7.918016201620163,47.54688588858886],[7.946595859585959,47.54370157015701],[7.956122412241225,47.55803100310031],[7.972907290729074,47.55484668466847],[8.00284788478848,47.55604080408041],[8.018725472547256,47.5500702070207],[8.046397839783978,47.55484668466847],[8.069080108010802,47.56499669966997],[8.083143114311433,47.55703590359036],[8.099474347434745,47.56280748074808],[8.103103510351037,47.576738873887386],[8.112630063006302,47.583903590359036],[8.13712691269127,47.583903590359036],[8.138034203420343,47.591068306830685],[8.165706570657067,47.594053605360536],[8.183398739873988,47.60380558055805],[8.201544554455445,47.62072227222722],[8.220144014401441,47.61793599359936],[8.223319531953196,47.60579577957796],[8.237836183618363,47.61276147614761],[8.26006480648065,47.61495069506951],[8.264601260126014,47.6089800980098],[8.2868298829883,47.61077127712771],[8.298171017101712,47.60480068006801],[8.29454185418542,47.593058505850586],[8.323121512151216,47.57295749574958],[8.376198019801981,47.56778297829783],[8.3839099909991,47.56499669966997],[8.39434383438344,47.576738873887386],[8.426099009900991,47.56778297829783],[8.417933393339336,47.56400160016002],[8.402963096309632,47.532954495449545],[8.380734473447346,47.524794679467945],[8.376651665166518,47.511858385838586],[8.360774077407742,47.50986818681868],[8.358052205220524,47.49693189318932],[8.363949594959497,47.4810103010301],[8.373022502250226,47.4810103010301],[8.366217821782179,47.470064206420645],[8.38663186318632,47.44876907690769],[8.384363636363638,47.43702690269027],[8.374383438343836,47.43006120612061],[8.387539153915393,47.42807100710071],[8.392075607560757,47.41991119111911],[8.384363636363638,47.405780778077805],[8.366217821782179,47.401999399939996],[8.392075607560757,47.39602880288029],[8.393890189018903,47.380704270427046],[8.406592259225924,47.36995719571957],[8.403870387038705,47.346870887088706],[8.416572457245724,47.33393459345935],[8.416118811881189,47.32398359835984],[8.449688568856887,47.32975517551755],[8.446059405940595,47.31980418041804],[8.431996399639965,47.31184338433843],[8.4274599459946,47.301096309630964],[8.415211521152116,47.29273747374737],[8.388900090009002,47.289951195119514],[8.40205580558056,47.2740296029603],[8.402963096309632,47.2537295729573],[8.411128712871289,47.24795799579958]]]}},{"type":"Feature","properties":{"abbr":"GR","name":"Graubünden/Grigioni","no":18,"centroid":[9.627882879545327,46.656113814899264]},"geometry":{"type":"Polygon","coordinates":[[[9.1591899189919,46.17006620662066],[9.131517551755177,46.18399759975998],[9.111103510351036,46.204894689468944],[9.08887488748875,46.21205940594059],[9.07617281728173,46.23773297329733],[9.077987398739875,46.260023202320234],[9.063470747074708,46.2727604760476],[9.053944194419444,46.29186638663866],[9.07481188118812,46.314952695269525],[9.063470747074708,46.326893889388934],[9.071182718271828,46.34381058105811],[9.087967596759677,46.34978117811781],[9.094772277227724,46.373066506650666],[9.087060306030605,46.38898809880988],[9.096133213321334,46.39993419341934],[9.089328532853287,46.40709890989099],[9.10157695769577,46.4108802880288],[9.08887488748875,46.431976397639765],[9.087060306030605,46.44371857185718],[9.094772277227724,46.46003820382038],[9.084792079207922,46.47496469646965],[9.073904590459048,46.47974117411741],[9.061202520252026,46.4759597959796],[9.044871287128714,46.480736273627365],[9.03262286228623,46.502031403140315],[9.0235499549955,46.50780298029803],[9.01719891989199,46.53188438843884],[9.019013501350136,46.547805980598056],[9.029447344734475,46.571091309130914],[9.043056705670569,46.58900310031003],[9.005857785778579,46.610895289528955],[8.995877587758777,46.611890389038905],[8.972741674167418,46.60293449344935],[8.961854185418543,46.60293449344935],[8.963215121512153,46.617860986098606],[8.956410441044106,46.63000120012001],[8.94189378937894,46.61885608560856],[8.922840684068408,46.623035503550355],[8.905148514851486,46.611890389038905],[8.916489648964898,46.59696389638964],[8.907416741674169,46.58701290129013],[8.876115211521153,46.57487268726872],[8.853886588658867,46.570096209620964],[8.842999099909992,46.562931493149314],[8.829843384338435,46.571091309130914],[8.800810081008102,46.562931493149314],[8.750909090909092,46.57586778677867],[8.739114311431145,46.572086408640864],[8.727773177317733,46.57686288628863],[8.717792979297931,46.571091309130914],[8.697832583258327,46.58104230423042],[8.678779477947796,46.57905210521052],[8.681501350135015,46.58900310031003],[8.67696489648965,46.61885608560856],[8.657911791179119,46.625821782178214],[8.651107110711072,46.63577277727773],[8.654736273627364,46.64970417041704],[8.678779477947796,46.66403360336034],[8.673335733573358,46.670800280028004],[8.673335733573358,46.692095409540954],[8.70010081008101,46.70383758375837],[8.709173717371739,46.69906110611061],[8.717792979297931,46.70602680268027],[8.748187218721874,46.71796799679968],[8.748187218721874,46.73607880788079],[8.765879387938796,46.744835683568354],[8.782210621062108,46.727719971997196],[8.813058505850586,46.736874887488746],[8.809882988298831,46.744039603960395],[8.81759495949595,46.76175237523752],[8.833926192619263,46.76692689268927],[8.82802880288029,46.792003400340036],[8.847989198919894,46.797774977497745],[8.87656885688569,46.813099509950995],[8.915128712871288,46.8109102910291],[8.915128712871288,46.7987700770077],[8.927377137713773,46.795784778477845],[8.952781278127814,46.8047406740674],[9.009033303330334,46.8109102910291],[9.039427542754277,46.846733873387336],[9.045778577857787,46.849719171917194],[9.045778577857787,46.866834883488345],[9.061202520252026,46.873800580058],[9.094772277227724,46.871014301430144],[9.111103510351036,46.849719171917194],[9.14013681368137,46.862058405840585],[9.152385238523854,46.8799701970197],[9.187769576957697,46.87579077907791],[9.203647164716472,46.88275647564756],[9.226783078307832,46.90604180418042],[9.249011701170119,46.915793779377935],[9.26670387038704,46.90405160516052],[9.27441584158416,46.90703690369037],[9.3383798379838,46.90186238623862],[9.360608460846086,46.89708590859086],[9.3819297929793,46.89808100810081],[9.43319171917192,46.88474667466747],[9.449069306930694,46.873004500450044],[9.453605760576059,46.88594079407941],[9.467215121512153,46.88594079407941],[9.467215121512153,46.89987218721872],[9.48808280828083,46.912012401240126],[9.479463546354637,46.925744774477444],[9.504867686768678,46.95380658065807],[9.518023402340235,46.95599579957996],[9.519384338433845,46.970723272327234],[9.546149414941496,46.976892889288926],[9.518930693069308,46.99997919791979],[9.508043204320433,47.01709490949095],[9.475834383438345,47.05192339233923],[9.491711971197121,47.05689888988899],[9.511672367236725,47.05689888988899],[9.539344734473449,47.0648596859686],[9.553407740774079,47.05092829282928],[9.581080108010802,47.05271947194719],[9.602401440144016,47.06207340734073],[9.625990999099912,47.05092829282928],[9.644590459045906,47.06008320832083],[9.659107110711073,47.05809300930093],[9.681789378937895,47.06207340734073],[9.7067398739874,47.04774397439744],[9.744392439243926,47.040977297729775],[9.749836183618363,47.036797879787976],[9.77977677767777,47.03878807880788],[9.811985598559858,47.02187138713872],[9.83602880288029,47.01271647164717],[9.859164716471648,47.02286648664867],[9.87821782178218,47.02087628762876],[9.872774077407742,47.0059497949795],[9.889105310531054,47.00097429742974],[9.890466246624664,46.98783898389839],[9.87050585058506,46.96296149614962],[9.879125112511252,46.95699089908991],[9.875949594959497,46.934899689968994],[9.911787578757878,46.925744774477444],[9.923582358235825,46.917783978397836],[9.963049504950497,46.912012401240126],[9.978927092709272,46.91499769976998],[9.981195319531954,46.90604180418042],[10.005692169216923,46.89907610761076],[10.017940594059407,46.90086728672867],[10.032003600360037,46.88793099309931],[10.052417641764178,46.87579077907791],[10.05196399639964,46.864048604860486],[10.092338433843386,46.858874087408736],[10.091884788478849,46.851908390839085],[10.105040504050407,46.84076327632763],[10.125000900090011,46.84872407240724],[10.15675607560756,46.847728972897286],[10.177170117011702,46.85409760976098],[10.193954995499551,46.865839783978394],[10.232968496849686,46.865839783978394],[10.233875787578759,46.88474667466747],[10.22616381638164,46.89708590859086],[10.240680468046806,46.919973197319734],[10.240680468046806,46.93191439143914],[10.262909090909092,46.92992419241924],[10.29375697569757,46.921963396339635],[10.315985598559857,46.925744774477444],[10.306005400540055,46.94007420742074],[10.307366336633665,46.9490301030103],[10.322790279027904,46.95181638163816],[10.349101710171018,46.991023302330234],[10.370876687668769,46.991023302330234],[10.385846984698471,47.00097429742974],[10.399002700270028,46.99679487948795],[10.410797479747977,46.983062506250626],[10.428036003600361,46.973907590759076],[10.422592259225924,46.95878207820782],[10.455708370837085,46.95181638163816],[10.487917191719173,46.93788498849885],[10.486102610261028,46.91400260026003],[10.478844284428444,46.90982318231823],[10.475668766876689,46.89390159015902],[10.464781278127814,46.88375157515752],[10.470225022502252,46.8799701970197],[10.46795679567957,46.859869186918694],[10.472039603960397,46.84872407240724],[10.459791179117914,46.83499169916992],[10.457976597659767,46.81688088808881],[10.447996399639965,46.800959295929594],[10.42939693969397,46.796779877987795],[10.426221422142216,46.78881908190819],[10.442099009900991,46.77170337033703],[10.442099009900991,46.752000400040004],[10.434840684068408,46.75279647964796],[10.399909990999102,46.73408860886089],[10.41805580558056,46.71876407640764],[10.414880288028805,46.70901210121012],[10.40263186318632,46.70602680268027],[10.393105310531055,46.68990619061906],[10.381764176417644,46.68572677267726],[10.389022502250226,46.676770877087705],[10.391744374437444,46.65507770777078],[10.401724572457248,46.63696689668967],[10.442552655265528,46.63895709570957],[10.462513051305132,46.63278747874787],[10.492,46.61507470747075],[10.484741674167417,46.60492469246925],[10.484741674167417,46.57686288628863],[10.473400540054007,46.562931493149314],[10.472039603960397,46.54402460246025],[10.45888388838884,46.5408402840284],[10.452079207920793,46.53088928892889],[10.41805580558056,46.550791279127914],[10.397188118811883,46.54402460246025],[10.362711071107112,46.555766776677665],[10.3513699369937,46.555766776677665],[10.338214221422144,46.542830483048306],[10.325058505850587,46.551985398539856],[10.319161116111612,46.547805980598056],[10.296025202520253,46.549796179617964],[10.286952295229524,46.570096209620964],[10.269713771377138,46.57805700570057],[10.253836183618363,46.571091309130914],[10.240680468046806,46.58900310031003],[10.258372637263728,46.609900190019],[10.239773177317733,46.626816881688164],[10.238412241224124,46.63577277727773],[10.224802880288031,46.63000120012001],[10.21436903690369,46.616865886588656],[10.183067506750676,46.624030603060305],[10.164014401440145,46.6160698069807],[10.136342034203421,46.610895289528955],[10.12772277227723,46.60492469246925],[10.10186498649865,46.610895289528955],[10.096421242124213,46.59397859785979],[10.10322592259226,46.58601780178018],[10.095967596759678,46.57686288628863],[10.078729072907292,46.57487268726872],[10.085080108010803,46.56671287128713],[10.061944194419443,46.54601480148015],[10.04697389738974,46.54402460246025],[10.053778577857788,46.53188438843884],[10.051056705670568,46.51277847784778],[10.041983798379839,46.50999219921992],[10.048788478847886,46.49984218421842],[10.042891089108913,46.4779499949995],[10.05196399639964,46.46003820382038],[10.038808280828084,46.44670387038704],[10.05604680468047,46.440932293229324],[10.064212421242125,46.42600580058006],[10.074192619261927,46.428792079207916],[10.080997299729974,46.42083128312831],[10.100957695769578,46.42083128312831],[10.129083708370839,46.431976397639765],[10.143146714671468,46.427796979697966],[10.149951395139515,46.4128704870487],[10.159931593159317,46.41605480548055],[10.166736273627365,46.40709890989099],[10.164014401440145,46.39097829782978],[10.12772277227723,46.377842984298425],[10.129990999099912,46.360727272727274],[10.109123312331235,46.352766476647666],[10.105040504050407,46.33306350635063],[10.116381638163817,46.313957595759575],[10.124093609360937,46.311967396739675],[10.15675607560756,46.2890801080108],[10.163107110711072,46.27096929692969],[10.171726372637265,46.2667898789879],[10.174448244824482,46.255047704770476],[10.150858685868588,46.23972317231723],[10.14541494149415,46.22977217721772],[10.123186318631864,46.22380158015802],[10.094606660666066,46.22897609760976],[10.071017101710172,46.21683588358836],[10.043344734473449,46.22977217721772],[10.06103690369037,46.24788298829883],[10.053778577857788,46.2667898789879],[10.02792079207921,46.27793499349935],[9.996165616561658,46.28490069006901],[9.992082808280829,46.29604580458046],[9.999341134113411,46.312962496249625],[9.979380738073807,46.322913491349134],[9.996165616561658,46.35077627762776],[9.984824482448246,46.35177137713771],[9.963503150315033,46.363911591159116],[9.952615661566158,46.37883808380838],[9.934016201620164,46.375056705670566],[9.924943294329434,46.36590179017902],[9.908158415841585,46.381027302730274],[9.867330333033305,46.362916491649166],[9.831945994599462,46.360727272727274],[9.817429342934295,46.34978117811781],[9.785220522052207,46.34102430243024],[9.777508550855087,46.33485468546854],[9.762084608460848,46.33903410341034],[9.743031503150316,46.35177137713771],[9.723071107110712,46.34102430243024],[9.728061206120614,46.33306350635063],[9.717627362736275,46.32191839183918],[9.725792979297932,46.31992819281928],[9.713998199819983,46.29286148614862],[9.688594059405942,46.29286148614862],[9.675438343834385,46.30301150115012],[9.668633663366338,46.29604580458046],[9.635517551755177,46.28589578957896],[9.619186318631865,46.2870899089909],[9.610113411341136,46.29505070507051],[9.582441044104412,46.29385658565857],[9.549324932493251,46.301817381738175],[9.533447344734475,46.311967396739675],[9.511218721872188,46.33684488448845],[9.508043204320433,46.35077627762776],[9.493526552655267,46.364906690669066],[9.462678667866788,46.374061606160616],[9.468122412241225,46.38998319831983],[9.464039603960398,46.40590479047905],[9.453605760576059,46.42083128312831],[9.460410441044106,46.441927392739274],[9.459049504950496,46.46282448244824],[9.464039603960398,46.47874607460746],[9.460410441044106,46.50481768176817],[9.433645364536455,46.497851985198515],[9.425026102610262,46.48889608960896],[9.42411881188119,46.4759597959796],[9.412777677767778,46.46700390039004],[9.38964176417642,46.472775477547756],[9.371042304230425,46.48889608960896],[9.37285688568857,46.504021602160215],[9.361969396939696,46.50999219921992],[9.355164716471648,46.504021602160215],[9.311161116111613,46.504021602160215],[9.283035103510352,46.496856885688565],[9.273962196219623,46.48392059205921],[9.278045004500452,46.46103330333033],[9.264435643564358,46.45108230823082],[9.246743474347436,46.44670387038704],[9.247650765076509,46.432971497149715],[9.279859585958597,46.4150597059706],[9.283035103510352,46.4049096909691],[9.27577677767777,46.39475967596759],[9.283942394239425,46.38580378037803],[9.27577677767777,46.36789198919892],[9.283035103510352,46.35774197419742],[9.29619081908191,46.35595079507951],[9.299819981998201,46.34381058105811],[9.293015301530154,46.33803900390039],[9.298459045904591,46.32788898889889],[9.28212781278128,46.308783078307826],[9.283942394239425,46.29803600360036],[9.258538253825384,46.27793499349935],[9.252187218721874,46.26579477947794],[9.245382538253827,46.23176237623762],[9.220885688568858,46.22897609760976],[9.218617461746176,46.2138505850585],[9.20137893789379,46.2078799879988],[9.194574257425744,46.193948594859485],[9.194574257425744,46.17882308230823],[9.181418541854185,46.17006620662066],[9.1591899189919,46.17006620662066]]]}},{"type":"Feature","properties":{"abbr":"TI","name":"Ticino","no":21,"centroid":[8.808973068362766,46.29486184302371]},"geometry":{"type":"Polygon","coordinates":[[[8.384363636363638,46.452077407740774],[8.382095409540955,46.46003820382038],[8.392075607560757,46.46799899989999],[8.386178217821783,46.4759597959796],[8.40069486948695,46.492080408040806],[8.431996399639965,46.496856885688565],[8.44288388838884,46.49506570657066],[8.47736093609361,46.52790399039904],[8.482804680468048,46.53188438843884],[8.519549954995501,46.5388500850085],[8.513198919891991,46.55875207520752],[8.520457245724574,46.57686288628863],[8.539056705670568,46.58701290129013],[8.548129612961297,46.58183838383838],[8.573080108010803,46.58382858285828],[8.589411341134115,46.573081508150814],[8.61935193519352,46.57805700570057],[8.63477587758776,46.56372757275727],[8.653375337533754,46.56790699069907],[8.678779477947796,46.57905210521052],[8.697832583258327,46.58104230423042],[8.717792979297931,46.571091309130914],[8.727773177317733,46.57686288628863],[8.739114311431145,46.572086408640864],[8.750909090909092,46.57586778677867],[8.800810081008102,46.562931493149314],[8.829843384338435,46.571091309130914],[8.842999099909992,46.562931493149314],[8.853886588658867,46.570096209620964],[8.876115211521153,46.57487268726872],[8.907416741674169,46.58701290129013],[8.916489648964898,46.59696389638964],[8.905148514851486,46.611890389038905],[8.922840684068408,46.623035503550355],[8.94189378937894,46.61885608560856],[8.956410441044106,46.63000120012001],[8.963215121512153,46.617860986098606],[8.961854185418543,46.60293449344935],[8.972741674167418,46.60293449344935],[8.995877587758777,46.611890389038905],[9.005857785778579,46.610895289528955],[9.043056705670569,46.58900310031003],[9.029447344734475,46.571091309130914],[9.019013501350136,46.547805980598056],[9.01719891989199,46.53188438843884],[9.0235499549955,46.50780298029803],[9.03262286228623,46.502031403140315],[9.044871287128714,46.480736273627365],[9.061202520252026,46.4759597959796],[9.073904590459048,46.47974117411741],[9.084792079207922,46.47496469646965],[9.094772277227724,46.46003820382038],[9.087060306030605,46.44371857185718],[9.08887488748875,46.431976397639765],[9.10157695769577,46.4108802880288],[9.089328532853287,46.40709890989099],[9.096133213321334,46.39993419341934],[9.087060306030605,46.38898809880988],[9.094772277227724,46.373066506650666],[9.087967596759677,46.34978117811781],[9.071182718271828,46.34381058105811],[9.063470747074708,46.326893889388934],[9.07481188118812,46.314952695269525],[9.053944194419444,46.29186638663866],[9.063470747074708,46.2727604760476],[9.077987398739875,46.260023202320234],[9.07617281728173,46.23773297329733],[9.08887488748875,46.21205940594059],[9.111103510351036,46.204894689468944],[9.131517551755177,46.18399759975998],[9.1591899189919,46.17006620662066],[9.156921692169218,46.16170737073707],[9.134693069306932,46.15275147514751],[9.121083708370838,46.135038703870386],[9.071636363636365,46.117723972397236],[9.08887488748875,46.0900602060206],[9.073904590459048,46.061799379937995],[9.049407740774079,46.061799379937995],[9.01719891989199,46.05005720572057],[9.009033303330334,46.02677187718772],[9.023096309630965,46.015825782578254],[9.028086408640865,45.99393359335934],[9.010394239423944,45.98278847884788],[8.994970297029704,45.98000220022002],[8.994063006300632,45.96587178717871],[9.014023402340236,45.961095309530954],[9.011755175517553,45.943979597959796],[9.021735373537355,45.93800900090009],[9.019013501350136,45.92805800580058],[9.043056705670569,45.92805800580058],[9.058934293429344,45.91691289128913],[9.077080108010803,45.91094229422942],[9.077080108010803,45.89880208020802],[9.087060306030605,45.90198639863986],[9.077987398739875,45.8850697069707],[9.06573897389739,45.8749196919692],[9.054851485148516,45.87392459245925],[9.051222322232224,45.85203240324032],[9.035798379837985,45.842081408140814],[9.031715571557157,45.820786278627864],[9.018106210621063,45.818],[8.994063006300632,45.821781378137814],[8.996331233123314,45.83471767176717],[8.98635103510351,45.83889708970897],[8.972741674167418,45.831931393139314],[8.948698469846985,45.844071607160714],[8.92873807380738,45.832926492649264],[8.911953195319533,45.830936293629364],[8.914221422142216,45.842081408140814],[8.936450045004502,45.86795399539954],[8.931913591359137,45.8850697069707],[8.921026102610263,45.896811881188114],[8.92510891089109,45.90298149814981],[8.914221422142216,45.91790799079908],[8.896075607560757,45.92706290629063],[8.893807380738075,45.95890609060906],[8.882012601260127,45.95671687168716],[8.84844284428443,45.97283748374837],[8.832111611161118,45.98796299629963],[8.793098109810982,45.991744374437445],[8.7858397839784,45.98895809580958],[8.793098109810982,46.007864986498646],[8.805800180018004,46.021995399539954],[8.819863186318633,46.02478167816781],[8.82802880288029,46.03373757375737],[8.82802880288029,46.04707190719072],[8.843906390639065,46.04806700670067],[8.854340234023404,46.061799379937995],[8.852072007200722,46.075730773077304],[8.83301890189019,46.083094509450945],[8.805800180018004,46.10100630063006],[8.78311791179118,46.09384158415841],[8.760889288928894,46.10100630063006],[8.741382538253827,46.121903390339035],[8.723690369036905,46.10976317631763],[8.714617461746176,46.09702590259026],[8.68603780378038,46.10180238023802],[8.681047704770478,46.10976317631763],[8.657911791179119,46.112748474847486],[8.647931593159317,46.122898489848986],[8.611639963996401,46.121903390339035],[8.609371737173719,46.133048504850485],[8.593494149414942,46.14678087808781],[8.602567056705672,46.15573677367737],[8.588050405040505,46.15693089308931],[8.573080108010803,46.16489168916892],[8.56945094509451,46.17703190319032],[8.535881188118813,46.204098609860985],[8.533159315931595,46.2180300030003],[8.467380738073809,46.232757475747576],[8.464205220522054,46.24509670967097],[8.443791179117913,46.24887808780878],[8.456039603960397,46.264003600360034],[8.4274599459946,46.29803600360036],[8.440615661566158,46.30400660066007],[8.440615661566158,46.315748774877484],[8.454225022502252,46.32092329232923],[8.465112511251126,46.33385958595859],[8.461936993699371,46.35396059605961],[8.469648964896491,46.361722372237224],[8.460576057605762,46.38679887988799],[8.4710099009901,46.39575477547755],[8.462844284428444,46.40590479047905],[8.467380738073809,46.41187538753875],[8.457400540054007,46.41983618361836],[8.458761476147615,46.436752875287524],[8.466019801980199,46.442922492249224],[8.449688568856887,46.46182938293829],[8.43789378937894,46.46381958195819],[8.384363636363638,46.452077407740774]]]}},{"type":"Feature","properties":{"abbr":"TG","name":"Thurgau","no":20,"centroid":[9.090522451365876,47.568848191389634]},"geometry":{"type":"Polygon","coordinates":[[[8.944162016201622,47.37592779277928],[8.944162016201622,47.38508270827083],[8.919665166516653,47.399810181018104],[8.903787578757877,47.401999399939996],[8.915128712871288,47.41891609160916],[8.907416741674169,47.43085728572857],[8.930552655265528,47.43185238523852],[8.910592259225924,47.43702690269027],[8.902880288028804,47.44399259925993],[8.89063186318632,47.4688700870087],[8.883827182718273,47.470064206420645],[8.892900090009002,47.48797599759976],[8.898797479747977,47.523998599859986],[8.8829198919892,47.528974097409744],[8.841184518451847,47.532954495449545],[8.82802880288029,47.55604080408041],[8.850711071107112,47.56400160016002],[8.838916291629165,47.56499669966997],[8.830750675067508,47.57395259525953],[8.806707470747076,47.57196239623963],[8.805800180018004,47.582908490849086],[8.764064806480649,47.593058505850586],[8.748187218721874,47.592063406340635],[8.740021602160217,47.61176637663766],[8.745918991899192,47.62171737173718],[8.788108010801082,47.60997519751975],[8.800810081008102,47.602014401440144],[8.813058505850586,47.62709090909091],[8.82802880288029,47.639828182818285],[8.826214221422143,47.646793879387936],[8.804439243924394,47.651968396839685],[8.804439243924394,47.66470567056706],[8.788561656165617,47.66470567056706],[8.7858397839784,47.654754675467544],[8.764518451845186,47.64878407840784],[8.746826282628264,47.63883308330833],[8.721422142214223,47.641022302230226],[8.689213321332135,47.64878407840784],[8.681047704770478,47.65674487448745],[8.670160216021603,47.68500570057006],[8.687852385238525,47.69495669566957],[8.717792979297931,47.690777277727776],[8.727773177317733,47.69296649664967],[8.750909090909092,47.690777277727776],[8.788561656165617,47.67485568556856],[8.795366336633665,47.6760498049805],[8.816687668766878,47.6780400040004],[8.836194419441945,47.6718703870387],[8.853886588658867,47.66092429242924],[8.844360036003602,47.652963496349635],[8.857515751575159,47.64878407840784],[8.87520792079208,47.654754675467544],[8.901065706570659,47.64778897889789],[8.940986498649867,47.6557497749775],[8.97637083708371,47.6718703870387],[9.019467146714673,47.68699589958996],[9.054851485148516,47.68500570057006],[9.071182718271828,47.67903510351035],[9.096133213321334,47.67903510351035],[9.121083708370838,47.66709390939094],[9.141497749774977,47.66371057105711],[9.154653465346536,47.66709390939094],[9.170077407740775,47.654754675467544],[9.203193519351936,47.654754675467544],[9.255816381638166,47.65893409340934],[9.267611161116113,47.6557497749775],[9.444986498649866,47.594849684968494],[9.453152115211523,47.590073207320735],[9.502599459945996,47.54688588858886],[9.428655265526555,47.50290249024903],[9.405972997299731,47.49374757475748],[9.396446444644464,47.474840684068404],[9.37285688568857,47.48996619661966],[9.37421782178218,47.49971817181718],[9.340648064806482,47.50389758975898],[9.356072007200721,47.519819181918194],[9.342916291629164,47.519819181918194],[9.337926192619264,47.528974097409744],[9.316604860486049,47.530964296429644],[9.28212781278128,47.521013301330136],[9.28076687668767,47.50608680868087],[9.302995499549956,47.51603780378038],[9.311161116111613,47.50986818681868],[9.307531953195321,47.49792699269927],[9.319780378037805,47.49693189318932],[9.303902790279029,47.4790201020102],[9.278045004500452,47.4790201020102],[9.263074707470748,47.474840684068404],[9.21816381638164,47.48379657965796],[9.209998199819983,47.48897109710971],[9.18232583258326,47.49076227622762],[9.16554095409541,47.50389758975898],[9.1591899189919,47.48996619661966],[9.141497749774977,47.48379657965796],[9.089328532853287,47.482005400540054],[9.081616561656165,47.49494169416942],[9.056212421242126,47.48698089808981],[9.043056705670569,47.49494169416942],[9.009486948694871,47.48797599759976],[9.003589558955897,47.4810103010301],[9.010394239423944,47.472054405440545],[9.02491089108911,47.4688700870087],[9.027179117911793,47.457724972497246],[9.063017101710173,47.44598279827983],[9.028086408640865,47.43702690269027],[9.021735373537355,47.44180338033804],[9.011755175517553,47.43284748474847],[8.989526552655267,47.42906610661066],[8.98907290729073,47.410955295529554],[8.97637083708371,47.410955295529554],[8.971834383438345,47.390854285428546],[8.944162016201622,47.37592779277928]]]}},{"type":"Feature","properties":{"abbr":"VS","name":"Valais/Wallis","no":23,"centroid":[7.6045101050356205,46.20955542046745]},"geometry":{"type":"Polygon","coordinates":[[[6.821101710171018,46.42700090009001],[6.871456345634564,46.383017501750174],[6.882797479747976,46.377842984298425],[6.886426642664267,46.364906690669066],[6.881436543654366,46.35396059605961],[6.898221422142215,46.33903410341034],[6.916367236723673,46.34002920292029],[6.929976597659767,46.33007820782078],[6.9358739873987405,46.29883208320832],[6.953566156615662,46.2890801080108],[6.963546354635464,46.26897909790979],[6.986682268226823,46.252062406240626],[7.009818181818183,46.22101530153015],[7.012993699369938,46.204894689468944],[7.032046804680469,46.187778977897786],[7.071060306030604,46.20091429142914],[7.087845184518453,46.20190939093909],[7.11506390639064,46.21683588358836],[7.125044104410442,46.23693689368937],[7.15090189018902,46.243902590259026],[7.178120612061207,46.26778497849785],[7.188554455445545,46.27096929692969],[7.1858325832583265,46.28271147114711],[7.197173717371738,46.2890801080108],[7.188554455445545,46.30400660066007],[7.22121692169217,46.32888408840884],[7.252972097209722,46.33107330733073],[7.261591359135914,46.33803900390039],[7.261591359135914,46.35774197419742],[7.310131413141315,46.375056705670566],[7.307863186318633,46.35177137713771],[7.315121512151216,46.34381058105811],[7.347783978397841,46.35077627762776],[7.380900090009002,46.363911591159116],[7.399953195319533,46.376847884788475],[7.428079207920793,46.381027302730274],[7.438059405940595,46.38580378037803],[7.462102610261027,46.375852785278525],[7.473443744374438,46.384012601260125],[7.48342394239424,46.382022402240224],[7.484331233123313,46.372071407140716],[7.502023402340235,46.36988218821882],[7.506559855985599,46.375852785278525],[7.526066606660667,46.374061606160616],[7.555099909991,46.38898809880988],[7.528788478847885,46.39993419341934],[7.533778577857786,46.41008420842084],[7.58322592259226,46.4170499049905],[7.593659765976598,46.4108802880288],[7.609537353735375,46.428792079207916],[7.609537353735375,46.436752875287524],[7.625868586858687,46.44471367136713],[7.693008100810082,46.42501070107011],[7.708432043204321,46.41386558655866],[7.73156795679568,46.421826382638265],[7.777839783978399,46.442922492249224],[7.797800180018003,46.45804800480048],[7.82365796579658,46.46700390039004],[7.843164716471648,46.4779499949995],[7.869022502250226,46.4779499949995],[7.894426642664267,46.481731373137315],[7.932986498649866,46.497851985198515],[7.942059405940595,46.50780298029803],[7.9538541854185425,46.50780298029803],[7.971092709270928,46.51695789578958],[7.962927092709272,46.53705890589059],[7.969731773177319,46.5450197019702],[7.986063006300631,46.547805980598056],[7.992867686768678,46.555766776677665],[8.016003600360037,46.562931493149314],[8.034149414941496,46.561936393639364],[8.060914491449147,46.550791279127914],[8.075884788478849,46.555766776677665],[8.113990999099912,46.5470099009901],[8.125785778577859,46.53705890589059],[8.154819081908192,46.53088928892889],[8.173872187218723,46.53088928892889],[8.181130513051306,46.52073927392739],[8.257796579657967,46.52909810981098],[8.303161116111612,46.54601480148015],[8.316770477047706,46.55875207520752],[8.33673087308731,46.560941294129414],[8.364403240324034,46.58283348334833],[8.360774077407742,46.59795899589959],[8.37120792079208,46.6140796079608],[8.368939693969399,46.62900610061006],[8.39434383438344,46.64592279227923],[8.398426642664267,46.65388358835884],[8.409767776777679,46.65288848884888],[8.422016201620163,46.64791299129913],[8.419747974797481,46.6160698069807],[8.406138613861387,46.58601780178018],[8.419747974797481,46.56571777177717],[8.42882088208821,46.542830483048306],[8.457400540054007,46.53287948794879],[8.465112511251126,46.53606380638064],[8.47736093609361,46.52790399039904],[8.44288388838884,46.49506570657066],[8.431996399639965,46.496856885688565],[8.40069486948695,46.492080408040806],[8.386178217821783,46.4759597959796],[8.392075607560757,46.46799899989999],[8.382095409540955,46.46003820382038],[8.384363636363638,46.452077407740774],[8.366671467146716,46.452077407740774],[8.325389738973898,46.42501070107011],[8.307697569756977,46.42600580058006],[8.289098109810983,46.40908910891089],[8.316770477047706,46.39475967596759],[8.313141314131414,46.376847884788475],[8.301346534653465,46.375852785278525],[8.283200720072008,46.363911591159116],[8.273674167416743,46.36709590959096],[8.260972097209722,46.360727272727274],[8.262333033303332,46.34599979998],[8.236021602160218,46.34102430243024],[8.223319531953196,46.33385958595859],[8.224680468046806,46.325898789878984],[8.211524752475249,46.30977817781778],[8.198822682268228,46.301817381738175],[8.161170117011702,46.29604580458046],[8.138034203420343,46.301817381738175],[8.119434743474349,46.29186638663866],[8.112630063006302,46.28072127212721],[8.08087488748875,46.261018301830184],[8.088133213321333,46.254052605260526],[8.109454545454547,46.24987318731873],[8.112630063006302,46.23872807280728],[8.138941494149416,46.22599079907991],[8.1511899189919,46.20290449044904],[8.15255085508551,46.190963296329635],[8.164799279927994,46.181808380838085],[8.1511899189919,46.16489168916892],[8.154819081908192,46.14777597759776],[8.138941494149416,46.135038703870386],[8.113990999099912,46.129864186418644],[8.107186318631864,46.11075827582758],[8.034149414941496,46.10100630063006],[8.028705670567057,46.08090529052905],[8.021900990099011,46.07473567356735],[8.035056705670568,46.04388758875887],[8.01192079207921,46.03095129512951],[8.017364536453647,46.02478167816781],[8.01192079207921,46.012044404440445],[8.001033303330335,46.012044404440445],[7.9887848784878495,45.99592379237924],[7.9089432943294335,45.99691889188919],[7.893972997299731,45.97900710071007],[7.878095409540955,45.97283748374837],[7.868115211521153,45.93701390139014],[7.876734473447345,45.92706290629063],[7.864032403240325,45.91691289128913],[7.818667866786679,45.92706290629063],[7.800068406840685,45.91691289128913],[7.768766876687669,45.93701390139014],[7.747899189918993,45.940795279527954],[7.735197119711972,45.92387858785878],[7.719319531953196,45.92387858785878],[7.707978397839785,45.93502370237024],[7.709339333933394,45.947760976097605],[7.68166696669667,45.95671687168716],[7.668057605760577,45.97383258325832],[7.655809180918093,45.97701690169017],[7.637209720972098,45.97005120512051],[7.586401440144015,45.97104630463046],[7.575060306030604,45.98696789678968],[7.550109810981099,45.98597279727973],[7.541944194419443,45.97582278227823],[7.542851485148516,45.95671687168716],[7.496126012601261,45.95771197119712],[7.472082808280829,45.947760976097605],[7.472082808280829,45.933829582958296],[7.446225022502251,45.93183938393839],[7.426718271827183,45.91472367236724],[7.402221422142215,45.91094229422942],[7.383168316831684,45.896811881188114],[7.360939693969398,45.90298149814981],[7.343701170117012,45.91472367236724],[7.323740774077408,45.90994719471947],[7.316028802880289,45.91691289128913],[7.295161116111612,45.92188838883888],[7.277015301530154,45.90099129912991],[7.25705490549055,45.887855985598556],[7.248435643564357,45.893030503050305],[7.228475247524753,45.892035403540355],[7.199441944194421,45.87591479147915],[7.2008028802880295,45.86277947794779],[7.1908226822682275,45.85899809980998],[7.161335733573358,45.8707402740274],[7.158160216021603,45.878900090009],[7.136385238523853,45.87392459245925],[7.117785778577859,45.85999319931993],[7.097825382538255,45.86178437843784],[7.093742574257426,45.87591479147915],[7.063348334833484,45.89999619961996],[7.061080108010802,45.91273347334733],[7.045202520252026,45.92188838883888],[7.036129612961297,45.93800900090009],[7.035222322232224,45.95572177217721],[7.017983798379839,45.95990119011901],[7.009818181818183,45.97283748374837],[7.02297389738974,45.98000220022002],[7.012086408640865,45.98696789678968],[7.011179117911792,45.99691889188919],[6.9857749774977504,46.00408360836084],[6.9807848784878495,46.02000520052005],[6.963092709270928,46.03095129512951],[6.949936993699371,46.05184838483848],[6.934966696669668,46.05602780278028],[6.938142214221423,46.063988598859886],[6.923625562556256,46.064983698369836],[6.9109234923492355,46.05184838483848],[6.8909630963096316,46.04189738973897],[6.875992799279929,46.04806700670067],[6.872817281728174,46.05602780278028],[6.8909630963096316,46.073939593959395],[6.891416741674168,46.08488568856885],[6.882797479747976,46.09384158415841],[6.89504590459046,46.10797199719972],[6.900036003600361,46.122898489848986],[6.880075607560757,46.122898489848986],[6.842876687668768,46.128869086908686],[6.838340234023403,46.133048504850485],[6.815204320432044,46.128869086908686],[6.797965796579659,46.136829882988295],[6.789346534653466,46.15374657465746],[6.791614761476148,46.16270247024702],[6.811575157515752,46.18081328132813],[6.803409540954096,46.20290449044904],[6.83108190819082,46.23395159515952],[6.840154815481549,46.24788298829883],[6.85149594959496,46.252062406240626],[6.865105310531054,46.27992519251925],[6.858754275427543,46.29087128712871],[6.846505850585059,46.29007520752075],[6.828360036003601,46.30082228222822],[6.825184518451846,46.311967396739675],[6.799326732673268,46.32092329232923],[6.795697569756976,46.33306350635063],[6.787985598559857,46.33206840684068],[6.769386138613862,46.35495569556956],[6.780727272727273,46.36590179017902],[6.803409540954096,46.376847884788475],[6.80159495949595,46.38679887988799],[6.821101710171018,46.42700090009001]]]}},{"type":"Feature","properties":{"abbr":"VD","name":"Vaud","no":22,"centroid":[6.657498407681689,46.57069109142927]},"geometry":{"type":"MultiPolygon","coordinates":[[[[6.821101710171018,46.42700090009001],[6.68137893789379,46.45386858685868],[6.518520252025203,46.45585878587859],[6.4259765976597665,46.41605480548055],[6.33479387938794,46.40371557155716],[6.253137713771378,46.35973217321732],[6.220021602160217,46.311967396739675],[6.198700270027003,46.28370657065707],[6.140633663366337,46.30500170017002],[6.125663366336634,46.316743874387434],[6.136550855085509,46.3358497849785],[6.1578721872187225,46.35077627762776],[6.170120612061207,46.36590179017902],[6.160140414041405,46.37883808380838],[6.150160216021603,46.376847884788475],[6.135643564356436,46.38779397939794],[6.097990999099911,46.40908910891089],[6.06351395139514,46.41605480548055],[6.085742574257426,46.442922492249224],[6.074855085508552,46.453072507250724],[6.073040504050406,46.46600880088009],[6.096630063006301,46.481731373137315],[6.112961296129614,46.50999219921992],[6.137911791179119,46.53088928892889],[6.143355535553556,46.52790399039904],[6.15560396039604,46.5450197019702],[6.110693069306931,46.57686288628863],[6.125663366336634,46.58900310031003],[6.160594059405941,46.609900190019],[6.178739873987399,46.6160698069807],[6.203690369036904,46.63577277727773],[6.228187218721873,46.64791299129913],[6.23363096309631,46.65507770777078],[6.268108010801081,46.675775777577755],[6.269922592259227,46.682940494049404],[6.323452745274528,46.70602680268027],[6.341598559855986,46.71000720072007],[6.370631863186319,46.728715071507146],[6.38333393339334,46.730904290429045],[6.387870387038705,46.743044504450445],[6.4010261026102615,46.75100530053005],[6.423708370837084,46.75379157915791],[6.451380738073808,46.77389258925893],[6.458185418541855,46.78782398239824],[6.435049504950496,46.800959295929594],[6.4309666966696675,46.81190539053905],[6.4409468946894695,46.81588578857885],[6.443215121512152,46.83180738073807],[6.460000000000001,46.851908390839085],[6.481321332133214,46.846733873387336],[6.523056705670568,46.856883888388836],[6.551636363636364,46.873004500450044],[6.586567056705671,46.88375157515752],[6.626487848784879,46.88892609260926],[6.646448244824483,46.910818281828185],[6.685008100810082,46.925744774477444],[6.7208460846084614,46.935894789478944],[6.71721692169217,46.90604180418042],[6.739445544554456,46.89489668966897],[6.739445544554456,46.872009400940094],[6.753508550855086,46.871014301430144],[6.779366336633664,46.852903490349036],[6.741713771377138,46.82703090309031],[6.778459045904591,46.800959295929594],[6.778459045904591,46.793993599359936],[6.797965796579659,46.782848484848486],[6.805224122412242,46.78603280328033],[6.825184518451846,46.77289748974897],[6.858754275427543,46.78782398239824],[6.880075607560757,46.77508670867087],[6.916367236723673,46.78503770377038],[6.9308838883888395,46.803944594459445],[6.919996399639965,46.80991519151915],[6.9109234923492355,46.802949494949495],[6.903211521152116,46.82802600260026],[6.918635463546355,46.846733873387336],[6.9308838883888395,46.852903490349036],[6.913191719171918,46.865839783978394],[6.891416741674168,46.864844684468444],[6.9209036903690375,46.871014301430144],[6.9109234923492355,46.88096529652965],[6.8959531953195325,46.88275647564756],[6.866012601260127,46.90882808280828],[6.8959531953195325,46.924749674967494],[6.929976597659767,46.89907610761076],[6.933152115211522,46.89171237123712],[6.964453645364538,46.864844684468444],[6.958102610261027,46.88096529652965],[6.973526552655266,46.88693589358936],[6.988496849684969,46.873004500450044],[6.976702070207021,46.861063306330635],[6.976702070207021,46.849719171917194],[6.988043204320433,46.84394759475948],[6.992126012601261,46.83081228122812],[6.984414041404141,46.82205540554055],[6.9658145814581465,46.82802600260026],[6.958102610261027,46.8109102910291],[6.966721872187219,46.802949494949495],[6.956741674167417,46.78702790279028],[6.942225022502251,46.78404260426043],[6.934966696669668,46.77070827082708],[6.919996399639965,46.75996119611961],[6.908201620162017,46.75797099709971],[6.919996399639965,46.75100530053005],[6.928615661566157,46.75379157915791],[6.9408640864086415,46.744835683568354],[6.9358739873987405,46.73309350935094],[6.916367236723673,46.71796799679968],[6.903665166516652,46.71378857885789],[6.890055805580559,46.69388658865886],[6.862837083708372,46.680950295029504],[6.871456345634564,46.672989498949896],[6.865105310531054,46.65806300630063],[6.853764176417642,46.65607280728073],[6.837886588658867,46.662839483948396],[6.815204320432044,46.64691789178918],[6.803409540954096,46.64592279227923],[6.801141314131414,46.63000120012001],[6.803409540954096,46.608706070607056],[6.794790279027904,46.57487268726872],[6.811575157515752,46.58004720472047],[6.835164716471648,46.57487268726872],[6.848320432043205,46.58283348334833],[6.863744374437444,46.57805700570057],[6.873724572457246,46.56472267226722],[6.885065706570658,46.561936393639364],[6.898221422142215,46.571091309130914],[6.900036003600361,46.559946194619464],[6.887787578757877,46.55775697569757],[6.870095409540955,46.54402460246025],[6.861022502250226,46.53088928892889],[6.855125112511252,46.5408402840284],[6.831535553555356,46.53705890589059],[6.825184518451846,46.542830483048306],[6.811121512151216,46.52491869186919],[6.836525652565257,46.49984218421842],[6.862837083708372,46.494070607060706],[6.881436543654366,46.51277847784778],[6.896406840684069,46.51894809480948],[6.9009432943294335,46.51277847784778],[6.924986498649866,46.50780298029803],[6.934966696669668,46.501036303630364],[6.953566156615662,46.502031403140315],[6.972165616561657,46.490886288628865],[6.979877587758777,46.45904310431043],[6.992126012601261,46.44889308930893],[6.983506750675068,46.43774797479748],[7.016622862286229,46.44789798979898],[7.027963996399641,46.46700390039004],[7.066070207020703,46.48889608960896],[7.105083708370838,46.48889608960896],[7.135931593159317,46.50780298029803],[7.145004500450046,46.52690889088909],[7.158613861386139,46.52690889088909],[7.1917299729973,46.5470099009901],[7.20715391539154,46.53287948794879],[7.223031503150316,46.547805980598056],[7.237094509450946,46.553975597559756],[7.246167416741675,46.53805400540054],[7.248435643564357,46.51795299529953],[7.243445544554456,46.49984218421842],[7.223485148514852,46.486706870687065],[7.231197119711972,46.46600880088009],[7.223031503150316,46.441927392739274],[7.194905490549056,46.440932293229324],[7.19309090909091,46.433966596659666],[7.209422142214222,46.41605480548055],[7.193544554455446,46.37883808380838],[7.208061206120613,46.37883808380838],[7.223031503150316,46.34898509850985],[7.22121692169217,46.32888408840884],[7.188554455445545,46.30400660066007],[7.197173717371738,46.2890801080108],[7.1858325832583265,46.28271147114711],[7.188554455445545,46.27096929692969],[7.178120612061207,46.26778497849785],[7.15090189018902,46.243902590259026],[7.125044104410442,46.23693689368937],[7.11506390639064,46.21683588358836],[7.087845184518453,46.20190939093909],[7.071060306030604,46.20091429142914],[7.032046804680469,46.187778977897786],[7.012993699369938,46.204894689468944],[7.009818181818183,46.22101530153015],[6.986682268226823,46.252062406240626],[6.963546354635464,46.26897909790979],[6.953566156615662,46.2890801080108],[6.9358739873987405,46.29883208320832],[6.929976597659767,46.33007820782078],[6.916367236723673,46.34002920292029],[6.898221422142215,46.33903410341034],[6.881436543654366,46.35396059605961],[6.886426642664267,46.364906690669066],[6.882797479747976,46.377842984298425],[6.871456345634564,46.383017501750174],[6.821101710171018,46.42700090009001]]],[[[7.052007200720073,46.976892889288926],[7.062894689468948,46.970723272327234],[7.056543654365437,46.95977717771777],[7.057904590459047,46.93907910791079],[7.091020702070208,46.90285748574858],[7.082855085508552,46.89609080908091],[7.061987398739875,46.88375157515752],[7.071060306030604,46.873800580058],[7.042934293429344,46.846733873387336],[7.028417641764177,46.850913291329135],[7.040212421242125,46.861063306330635],[7.029778577857787,46.865839783978394],[7.035222322232224,46.873004500450044],[7.016169216921693,46.88096529652965],[7.008003600360037,46.873004500450044],[6.99348694869487,46.87897509750975],[7.008457245724573,46.88892609260926],[6.988043204320433,46.90405160516052],[6.988043204320433,46.917783978397836],[6.964453645364538,46.923953595359535],[6.928615661566157,46.95281148114812],[7.002106210621063,46.986843884388435],[7.040212421242125,46.979878187818784],[7.052007200720073,46.976892889288926]]]]}},{"type":"Feature","properties":{"abbr":"GE","name":"Genève","no":25,"centroid":[6.131090584714894,46.21657264516906]},"geometry":{"type":"Polygon","coordinates":[[[6.125663366336634,46.316743874387434],[6.140633663366337,46.30500170017002],[6.198700270027003,46.28370657065707],[6.220021602160217,46.311967396739675],[6.248601260126013,46.301817381738175],[6.253137713771378,46.29007520752075],[6.238167416741675,46.27574577457746],[6.267200720072008,46.24788298829883],[6.278541854185419,46.250868286828684],[6.294873087308732,46.26479967996799],[6.294873087308732,46.255843784378435],[6.309843384338435,46.255843784378435],[6.309843384338435,46.243902590259026],[6.291697569756976,46.22280648064806],[6.248601260126013,46.204894689468944],[6.23363096309631,46.205889788978894],[6.1859981998199824,46.17802700270027],[6.188720072007201,46.16608580858086],[6.175110711071108,46.15792599259926],[6.151521152115212,46.15195539553955],[6.126570657065707,46.14001420142014],[6.098444644464447,46.14379557955795],[6.0916399639964,46.15195539553955],[6.075762376237624,46.1489700970097],[6.051719171917192,46.1509602960296],[6.033573357335734,46.135834783478344],[6.023139513951396,46.142004400440044],[5.991384338433844,46.14379557955795],[5.975960396039604,46.133048504850485],[5.956453645364537,46.127873987398736],[5.96507290729073,46.1447906790679],[5.995013501350136,46.183002500250026],[5.96371197119712,46.196734873487344],[5.97777497749775,46.21683588358836],[5.99319891989199,46.21484568456845],[6.013159315931594,46.23076727672767],[6.033573357335734,46.23773297329733],[6.045821782178218,46.23076727672767],[6.059884788478849,46.24509670967097],[6.069864986498651,46.240718271827184],[6.088010801080109,46.24609180918092],[6.101620162016202,46.23773297329733],[6.123395139513952,46.250868286828684],[6.101620162016202,46.28490069006901],[6.116590459045905,46.29385658565857],[6.125663366336634,46.316743874387434]]]}},{"type":"Feature","properties":{"abbr":"NE","name":"Neuchâtel","no":24,"centroid":[6.779945336999951,46.99553143445798]},"geometry":{"type":"Polygon","coordinates":[[[7.040212421242125,46.979878187818784],[7.002106210621063,46.986843884388435],[6.928615661566157,46.95281148114812],[6.8959531953195325,46.924749674967494],[6.866012601260127,46.90882808280828],[6.779366336633664,46.852903490349036],[6.753508550855086,46.871014301430144],[6.739445544554456,46.872009400940094],[6.739445544554456,46.89489668966897],[6.71721692169217,46.90604180418042],[6.7208460846084614,46.935894789478944],[6.685008100810082,46.925744774477444],[6.646448244824483,46.910818281828185],[6.626487848784879,46.88892609260926],[6.586567056705671,46.88375157515752],[6.551636363636364,46.873004500450044],[6.523056705670568,46.856883888388836],[6.481321332133214,46.846733873387336],[6.460000000000001,46.851908390839085],[6.463629162916292,46.88972217221722],[6.458185418541855,46.90086728672867],[6.4327812781278135,46.92773497349735],[6.445029702970298,46.9327104710471],[6.480867686768677,46.96395659565957],[6.496745274527453,46.973907590759076],[6.50491089108911,46.96594679467947],[6.518520252025203,46.970723272327234],[6.572957695769578,46.982067406740676],[6.596093609360937,46.993013501350134],[6.617868586858687,46.992018401840184],[6.633746174617462,46.99798899889989],[6.657789378937895,47.02605080508051],[6.678203420342035,47.03500670067007],[6.696349234923493,47.036797879787976],[6.719938793879389,47.05192339233923],[6.691359135913592,47.06585478547855],[6.7058757875787585,47.07381558155816],[6.70315391539154,47.08078127812781],[6.721753375337534,47.08874207420742],[6.743074707470748,47.091926392639266],[6.739445544554456,47.10884308430843],[6.764849684968498,47.11998819881988],[6.797965796579659,47.12894409440944],[6.840154815481549,47.14984118411841],[6.858754275427543,47.164767676767674],[6.861022502250226,47.165762776277624],[6.888694869486949,47.13073527352735],[6.88098289828983,47.10784798479848],[6.871002700270028,47.102076407640766],[6.866466246624663,47.08496069606961],[6.926347434743475,47.10884308430843],[6.996662466246625,47.11899309930993],[7.008003600360037,47.12376957695769],[7.040212421242125,47.12376957695769],[7.02297389738974,47.112027402740274],[7.028417641764177,47.104066606660666],[7.041119711971198,47.10884308430843],[7.056543654365437,47.099887188718874],[7.076050405040505,47.095707770777075],[7.086484248424843,47.08595579557956],[7.076504050405041,47.07600480048005],[7.088752475247525,47.05908810881088],[7.062894689468948,47.043962596259625],[7.051099909991,47.042967496749675],[7.032954095409542,47.030827282728275],[7.033407740774078,47.01709490949095],[7.024334833483349,47.0059497949795],[7.040212421242125,46.979878187818784]]]}},{"type":"Feature","properties":{"abbr":"JU","name":"Jura","no":26,"centroid":[7.1559970715181755,47.35090437597201]},"geometry":{"type":"Polygon","coordinates":[[[7.436698469846985,47.37970917091709],[7.458019801980199,47.37075327532753],[7.49113591359136,47.36995719571957],[7.523344734473448,47.37174837483748],[7.521983798379839,47.36478267826783],[7.540129612961297,47.36000620062006],[7.536500450045005,47.350055205520555],[7.551470747074708,47.3450797079708],[7.533778577857786,47.33393459345935],[7.527881188118813,47.3227894789479],[7.557821782178219,47.32179437943795],[7.521983798379839,47.31383358335834],[7.500208820882089,47.30288748874887],[7.442142214221423,47.30905710571057],[7.42082088208821,47.30706690669067],[7.412201620162017,47.31283848384838],[7.374549054905492,47.31383358335834],[7.350052205220523,47.30487768776877],[7.336442844284429,47.30487768776877],[7.316482448244825,47.289951195119514],[7.280190819081909,47.283980598059806],[7.24299189918992,47.284776677667764],[7.193544554455446,47.29572277227723],[7.168594059405941,47.290946294629464],[7.168594059405941,47.27502470247025],[7.15090189018902,47.2698501850185],[7.1431899189919,47.25591879187919],[7.1558919891989206,47.24795799579958],[7.112795679567958,47.24178837883788],[7.096010801080109,47.23681288128813],[7.078772277227723,47.24795799579958],[7.057904590459047,47.24178837883788],[7.058358235823583,47.231041304130414],[7.046563456345635,47.226861886188615],[7.02297389738974,47.19601380138014],[6.996208820882089,47.19282948294829],[6.979877587758777,47.17372357235723],[6.969897389738975,47.18108730873087],[6.9408640864086415,47.18486868686868],[6.938142214221423,47.17471867186718],[6.9059333933393345,47.156806880688066],[6.883704770477048,47.14984118411841],[6.871456345634564,47.17372357235723],[6.861022502250226,47.165762776277624],[6.858754275427543,47.164767676767674],[6.841062106210622,47.170937293729374],[6.875085508550856,47.18486868686868],[6.88098289828983,47.19979517951795],[6.939956795679569,47.231041304130414],[6.9558343834383445,47.24377857785778],[6.946761476147615,47.2537295729573],[6.952205220522053,47.26885508550855],[6.9408640864086415,47.285771777177715],[6.953112511251126,47.291941394139414],[6.974433843384339,47.290946294629464],[6.976702070207021,47.29572277227723],[6.996662466246625,47.29572277227723],[7.008003600360037,47.301096309630964],[7.016169216921693,47.31383358335834],[7.009818181818183,47.32398359835984],[7.042934293429344,47.32597379737974],[7.056543654365437,47.33393459345935],[7.061987398739875,47.34408460846085],[7.050192619261927,47.346870887088706],[7.050192619261927,47.36100130013001],[7.032954095409542,47.36995719571957],[7.011179117911792,47.37274347434744],[6.996208820882089,47.36279247924792],[6.9209036903690375,47.354831683168314],[6.9059333933393345,47.35901110111011],[6.880075607560757,47.352045404540455],[6.883704770477048,47.37274347434744],[6.911377137713772,47.38607780778078],[6.910016201620163,47.39602880288029],[6.918635463546355,47.405780778077805],[6.938595859585959,47.405780778077805],[6.938595859585959,47.43384258425843],[6.958102610261027,47.43384258425843],[6.969897389738975,47.44697789778978],[6.994394239423943,47.44876907690769],[6.999837983798381,47.466879887988796],[6.992126012601261,47.465884788478846],[6.983053105310532,47.49275247524753],[7.023427542754276,47.50389758975898],[7.036583258325833,47.49693189318932],[7.071060306030604,47.49175737573757],[7.111434743474348,47.49494169416942],[7.128219621962197,47.50389758975898],[7.1708622862286235,47.48897109710971],[7.1958127812781285,47.49374757475748],[7.178120612061207,47.467874987498746],[7.169501350135015,47.44279847984799],[7.2057929792979305,47.43483768376838],[7.226207020702071,47.43981318131813],[7.238455445544555,47.43384258425843],[7.246167416741675,47.41991119111911],[7.270210621062107,47.42707590759076],[7.282912691269128,47.43483768376838],[7.326009000900091,47.43981318131813],[7.326009000900091,47.43185238523852],[7.340072007200721,47.43006120612061],[7.356403240324033,47.41473667366736],[7.375909990999101,47.41374157415741],[7.398592259225923,47.39682488248825],[7.4144698469847,47.39403860386039],[7.413562556255626,47.37970917091709],[7.436698469846985,47.37970917091709]]]}},{"type":"Feature","properties":{"abbr":"BE","name":"Bern/Berne","no":2,"centroid":[7.625278926431738,46.82205040654653]},"geometry":{"type":"MultiPolygon","coordinates":[[[[7.824565256525654,47.26606880688069],[7.825926192619263,47.24596779677968],[7.83908190819082,47.23382758275827],[7.836360036003601,47.224075607560756],[7.846340234023403,47.216711871187115],[7.859949594959497,47.19183438343834],[7.874466246624664,47.18387358735873],[7.883085508550856,47.17272847284728],[7.874466246624664,47.161981398139815],[7.878095409540955,47.150836283628365],[7.889890189018903,47.14088528852885],[7.885807380738075,47.12794899489949],[7.873105310531054,47.12476467646765],[7.869022502250226,47.112027402740274],[7.882178217821783,47.08874207420742],[7.874012601260127,47.06207340734073],[7.865846984698471,47.05271947194719],[7.882178217821783,47.040977297729775],[7.899870387038705,47.0180900090009],[7.902138613861387,47.0059497949795],[7.924367236723673,47.00973117311731],[7.932986498649866,47.0037605760576],[7.952039603960397,47.0059497949795],[7.947956795679569,46.99380958095809],[7.952039603960397,46.981072307230725],[7.946142214221423,46.96873307330733],[7.932079207920793,46.961767376737676],[7.932079207920793,46.95082128212821],[7.922099009900991,46.9470399039904],[7.912118811881189,46.9327104710471],[7.898055805580559,46.934899689968994],[7.872198019801981,46.923953595359535],[7.866754275427543,46.91400260026003],[7.883085508550856,46.90405160516052],[7.873105310531054,46.88972217221722],[7.859042304230424,46.88474667466747],[7.856320432043205,46.871014301430144],[7.869929792979299,46.864048604860486],[7.866754275427543,46.850913291329135],[7.876734473447345,46.83698189818982],[7.922099009900991,46.82006520652065],[7.946595859585959,46.8047406740674],[7.9538541854185425,46.792003400340036],[7.9837947794779485,46.77508670867087],[8.020993699369939,46.78881908190819],[8.04866606660666,46.78782398239824],[8.089494149414943,46.78782398239824],[8.104918091809182,46.77289748974897],[8.121702970297031,46.76971317131713],[8.145746174617463,46.75478667866786],[8.190203420342035,46.76971317131713],[8.207895589558957,46.76891709170917],[8.22150495049505,46.76095629562956],[8.239197119711973,46.76971317131713],[8.259157515751577,46.76374257425743],[8.269137713771379,46.75379157915791],[8.283200720072008,46.75279647964796],[8.333101710171018,46.78085828582858],[8.35986678667867,46.782848484848486],[8.368939693969399,46.78782398239824],[8.39434383438344,46.77170337033703],[8.396158415841585,46.76473767376738],[8.413850585058507,46.77389258925893],[8.446513051305132,46.76971317131713],[8.448781278127814,46.76473767376738],[8.440615661566158,46.736874887488746],[8.448781278127814,46.71179837983798],[8.454225022502252,46.71080328032803],[8.45195679567957,46.68891109110911],[8.415211521152116,46.68672187218722],[8.399787578757877,46.693090509050904],[8.397972997299732,46.673984598459846],[8.407499549954997,46.66701890189019],[8.409767776777679,46.65288848884888],[8.398426642664267,46.65388358835884],[8.39434383438344,46.64592279227923],[8.368939693969399,46.62900610061006],[8.37120792079208,46.6140796079608],[8.360774077407742,46.59795899589959],[8.364403240324034,46.58283348334833],[8.33673087308731,46.560941294129414],[8.316770477047706,46.55875207520752],[8.303161116111612,46.54601480148015],[8.257796579657967,46.52909810981098],[8.181130513051306,46.52073927392739],[8.173872187218723,46.53088928892889],[8.154819081908192,46.53088928892889],[8.125785778577859,46.53705890589059],[8.113990999099912,46.5470099009901],[8.075884788478849,46.555766776677665],[8.060914491449147,46.550791279127914],[8.034149414941496,46.561936393639364],[8.016003600360037,46.562931493149314],[7.992867686768678,46.555766776677665],[7.986063006300631,46.547805980598056],[7.969731773177319,46.5450197019702],[7.962927092709272,46.53705890589059],[7.971092709270928,46.51695789578958],[7.9538541854185425,46.50780298029803],[7.942059405940595,46.50780298029803],[7.932986498649866,46.497851985198515],[7.894426642664267,46.481731373137315],[7.869022502250226,46.4779499949995],[7.843164716471648,46.4779499949995],[7.82365796579658,46.46700390039004],[7.797800180018003,46.45804800480048],[7.777839783978399,46.442922492249224],[7.73156795679568,46.421826382638265],[7.708432043204321,46.41386558655866],[7.693008100810082,46.42501070107011],[7.625868586858687,46.44471367136713],[7.609537353735375,46.436752875287524],[7.609537353735375,46.428792079207916],[7.593659765976598,46.4108802880288],[7.58322592259226,46.4170499049905],[7.533778577857786,46.41008420842084],[7.528788478847885,46.39993419341934],[7.555099909991,46.38898809880988],[7.526066606660667,46.374061606160616],[7.506559855985599,46.375852785278525],[7.502023402340235,46.36988218821882],[7.484331233123313,46.372071407140716],[7.48342394239424,46.382022402240224],[7.473443744374438,46.384012601260125],[7.462102610261027,46.375852785278525],[7.438059405940595,46.38580378037803],[7.428079207920793,46.381027302730274],[7.399953195319533,46.376847884788475],[7.380900090009002,46.363911591159116],[7.347783978397841,46.35077627762776],[7.315121512151216,46.34381058105811],[7.307863186318633,46.35177137713771],[7.310131413141315,46.375056705670566],[7.261591359135914,46.35774197419742],[7.261591359135914,46.33803900390039],[7.252972097209722,46.33107330733073],[7.22121692169217,46.32888408840884],[7.223031503150316,46.34898509850985],[7.208061206120613,46.37883808380838],[7.193544554455446,46.37883808380838],[7.209422142214222,46.41605480548055],[7.19309090909091,46.433966596659666],[7.194905490549056,46.440932293229324],[7.223031503150316,46.441927392739274],[7.231197119711972,46.46600880088009],[7.223485148514852,46.486706870687065],[7.243445544554456,46.49984218421842],[7.248435643564357,46.51795299529953],[7.246167416741675,46.53805400540054],[7.237094509450946,46.553975597559756],[7.259323132313232,46.562931493149314],[7.281551755175518,46.58482368236824],[7.298790279027903,46.57905210521052],[7.32101890189019,46.59178937893789],[7.315121512151216,46.5997501750175],[7.317843384338435,46.61885608560856],[7.312853285328534,46.63696689668967],[7.330091809180919,46.64174337433743],[7.32101890189019,46.65507770777078],[7.344608460846086,46.65507770777078],[7.358671467146715,46.64373357335734],[7.368198019801981,46.65507770777078],[7.378178217821783,46.68373657365736],[7.376363636363637,46.693090509050904],[7.36320792079208,46.69906110611061],[7.344608460846086,46.69906110611061],[7.348691269126913,46.71179837983798],[7.305141314131414,46.71796799679968],[7.296068406840685,46.736874887488746],[7.302873087308732,46.75996119611961],[7.290171017101711,46.77289748974897],[7.303780378037804,46.782848484848486],[7.301512151215122,46.793993599359936],[7.311038703870388,46.80991519151915],[7.32101890189019,46.8148906890689],[7.320111611161117,46.82583678367837],[7.330999099909992,46.83379757975798],[7.301512151215122,46.851908390839085],[7.316028802880289,46.863053505350535],[7.330999099909992,46.857878987898786],[7.33508190819082,46.849719171917194],[7.351413141314132,46.85409760976098],[7.358671467146715,46.862058405840585],[7.351413141314132,46.88793099309931],[7.327823582358237,46.89390159015902],[7.288356435643565,46.89390159015902],[7.282912691269128,46.88972217221722],[7.259323132313232,46.89808100810081],[7.217134113411342,46.89808100810081],[7.208061206120613,46.90285748574858],[7.188100810081009,46.89987218721872],[7.208061206120613,46.90882808280828],[7.203071107110712,46.9368898889889],[7.209422142214222,46.9428604860486],[7.20715391539154,46.96395659565957],[7.236187218721873,46.984853685368535],[7.223031503150316,46.993013501350134],[7.217134113411342,47.00694489448945],[7.153623762376238,46.985848784878485],[7.093742574257426,46.97609680968097],[7.052007200720073,46.976892889288926],[7.040212421242125,46.979878187818784],[7.024334833483349,47.0059497949795],[7.033407740774078,47.01709490949095],[7.032954095409542,47.030827282728275],[7.051099909991,47.042967496749675],[7.062894689468948,47.043962596259625],[7.088752475247525,47.05908810881088],[7.076504050405041,47.07600480048005],[7.086484248424843,47.08595579557956],[7.076050405040505,47.095707770777075],[7.056543654365437,47.099887188718874],[7.041119711971198,47.10884308430843],[7.028417641764177,47.104066606660666],[7.02297389738974,47.112027402740274],[7.040212421242125,47.12376957695769],[7.008003600360037,47.12376957695769],[6.996662466246625,47.11899309930993],[6.926347434743475,47.10884308430843],[6.866466246624663,47.08496069606961],[6.871002700270028,47.102076407640766],[6.88098289828983,47.10784798479848],[6.888694869486949,47.13073527352735],[6.861022502250226,47.165762776277624],[6.871456345634564,47.17372357235723],[6.883704770477048,47.14984118411841],[6.9059333933393345,47.156806880688066],[6.938142214221423,47.17471867186718],[6.9408640864086415,47.18486868686868],[6.969897389738975,47.18108730873087],[6.979877587758777,47.17372357235723],[6.996208820882089,47.19282948294829],[7.02297389738974,47.19601380138014],[7.046563456345635,47.226861886188615],[7.058358235823583,47.231041304130414],[7.057904590459047,47.24178837883788],[7.078772277227723,47.24795799579958],[7.096010801080109,47.23681288128813],[7.112795679567958,47.24178837883788],[7.1558919891989206,47.24795799579958],[7.1431899189919,47.25591879187919],[7.15090189018902,47.2698501850185],[7.168594059405941,47.27502470247025],[7.168594059405941,47.290946294629464],[7.193544554455446,47.29572277227723],[7.24299189918992,47.284776677667764],[7.280190819081909,47.283980598059806],[7.316482448244825,47.289951195119514],[7.336442844284429,47.30487768776877],[7.350052205220523,47.30487768776877],[7.374549054905492,47.31383358335834],[7.412201620162017,47.31283848384838],[7.42082088208821,47.30706690669067],[7.442142214221423,47.30905710571057],[7.500208820882089,47.30288748874887],[7.521983798379839,47.31383358335834],[7.557821782178219,47.32179437943795],[7.546480648064807,47.30587278727873],[7.531963996399641,47.29373257325732],[7.521076507650766,47.29373257325732],[7.484331233123313,47.283980598059806],[7.468000000000001,47.26507370737074],[7.439873987398741,47.26288448844885],[7.415830783078309,47.25173937393739],[7.42082088208821,47.24278347834783],[7.36320792079208,47.219896189618964],[7.340072007200721,47.217706970697066],[7.353681368136814,47.19183438343834],[7.365929792979299,47.19382458245825],[7.380900090009002,47.18387358735873],[7.39133393339334,47.165762776277624],[7.383168316831684,47.15979217921792],[7.410840684068408,47.161981398139815],[7.432162016201621,47.17571377137714],[7.4308010801080115,47.18287848784878],[7.446225022502251,47.18108730873087],[7.448039603960397,47.18904810481048],[7.474351035103511,47.19083928392839],[7.471175517551756,47.18108730873087],[7.481155715571558,47.171932393239324],[7.496579657965797,47.16994219421942],[7.492950495049506,47.162976497649765],[7.468000000000001,47.150836283628365],[7.449854185418543,47.15601080108011],[7.439873987398741,47.1440696069607],[7.422181818181819,47.1359097909791],[7.436698469846985,47.12277447744774],[7.403582358235824,47.11799799979998],[7.386343834383439,47.12595879587959],[7.370012601260127,47.12098329832983],[7.381353735373538,47.095707770777075],[7.395870387038705,47.092921492149216],[7.433069306930694,47.099887188718874],[7.4407812781278135,47.07381558155816],[7.468000000000001,47.07879107910791],[7.474351035103511,47.08695089508951],[7.461195319531954,47.10784798479848],[7.492043204320433,47.11680388038804],[7.510189018901891,47.12595879587959],[7.528788478847885,47.14884608460846],[7.520169216921693,47.15979217921792],[7.558729072907291,47.16775297529753],[7.581411341134114,47.15879707970797],[7.585947794779479,47.14785098509851],[7.593206120612062,47.15302550255026],[7.639477947794781,47.15402060206021],[7.644921692169218,47.14884608460846],[7.659438343834384,47.16775297529753],[7.673047704770478,47.16775297529753],[7.678037803780379,47.18287848784878],[7.661706570657066,47.2007902790279],[7.6471899189919,47.201785378537856],[7.648097209720973,47.21870207020702],[7.639477947794781,47.230046204620464],[7.625868586858687,47.225866786678665],[7.595927992799281,47.24596779677968],[7.593659765976598,47.26606880688069],[7.579596759675969,47.2760198019802],[7.613166516651666,47.280995299529955],[7.648550855085509,47.281990399039906],[7.684842484248426,47.29373257325732],[7.701173717371738,47.286766876687665],[7.711607560756076,47.2740296029603],[7.72521692169217,47.2698501850185],[7.73292889288929,47.25870507050705],[7.756972097209722,47.25790899089909],[7.77375697569757,47.26706390639064],[7.785098109810982,47.25591879187919],[7.800975697569758,47.26805900590059],[7.818214221422143,47.26188938893889],[7.824565256525654,47.26606880688069]]],[[[7.557821782178219,47.32179437943795],[7.527881188118813,47.3227894789479],[7.533778577857786,47.33393459345935],[7.551470747074708,47.3450797079708],[7.572792079207922,47.3287600760076],[7.557821782178219,47.32179437943795]]]]}}]} ================================================ FILE: examples/public/data/source.url ================================================ https://www.bfs.admin.ch/bfs/de/home/statistiken/bevoelkerung/migration-integration/binnenwanderung.assetdetail.3222163.html ================================================ FILE: examples/src/components/ClusteringExample.tsx ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import * as Cluster from '@flowmap.gl/cluster'; import { Flow, FlowAccessors, isFeatureCollection, Location, LocationAccessors } from '@flowmap.gl/core'; import React from 'react'; import { ViewState } from '@flowmap.gl/core'; import Example from './Example'; export interface Props extends FlowAccessors, LocationAccessors { flows: Flow[]; locations: Location[]; clusterLevels?: Cluster.ClusterLevel[]; } interface State { clusteredLocations: Location[] | undefined; aggregateFlows: Flow[] | undefined; } class ClusteringExample extends React.Component { private readonly clusterIndex: Cluster.ClusterIndex; private readonly aggregateFlowsByZoom: Map; constructor(props: Props) { super(props); const { flows, getLocationId, getLocationCentroid, getFlowOriginId, getFlowDestId, getFlowMagnitude } = this.props; let clusterLevels; const locations = isFeatureCollection(this.props.locations) ? this.props.locations.features : this.props.locations; if (this.props.clusterLevels) { clusterLevels = this.props.clusterLevels; } else { const getLocationWeight = Cluster.makeLocationWeightGetter(flows, { getFlowOriginId, getFlowDestId, getFlowMagnitude, }); clusterLevels = Cluster.clusterLocations(locations, { getLocationId, getLocationCentroid }, getLocationWeight, { makeClusterName: (id: number, numPoints: number) => `Cluster #${id} of ${numPoints} locations`, }); } const clusterIndex = Cluster.buildIndex(clusterLevels); const aggregateFlowsByZoom = new Map(); for (const zoom of clusterIndex.availableZoomLevels) { aggregateFlowsByZoom.set( zoom, clusterIndex.aggregateFlows(flows, zoom, { getFlowOriginId, getFlowDestId, getFlowMagnitude }), ); } const maxZoom = Math.max.apply(null, clusterIndex.availableZoomLevels); this.clusterIndex = clusterIndex; this.aggregateFlowsByZoom = aggregateFlowsByZoom; this.state = { clusteredLocations: this.clusterIndex.getClusterNodesFor(maxZoom), aggregateFlows: this.aggregateFlowsByZoom.get(maxZoom), }; } handleViewStateChange = (viewState: ViewState) => { const { availableZoomLevels } = this.clusterIndex; const { zoom } = viewState; const clusterZoom = Cluster.findAppropriateZoomLevel(availableZoomLevels, zoom); this.setState({ clusteredLocations: this.clusterIndex.getClusterNodesFor(clusterZoom), aggregateFlows: this.aggregateFlowsByZoom.get(clusterZoom), }); }; render() { const { getFlowOriginId, getFlowDestId, getFlowMagnitude } = this.props; const { clusteredLocations, aggregateFlows } = this.state; if (!clusteredLocations || !aggregateFlows) { return null; } return ( loc.id} getLocationCentroid={(loc: Cluster.ClusterNode) => loc.centroid} getFlowOriginId={(flow: Flow) => (Cluster.isAggregateFlow(flow) ? flow.origin : getFlowOriginId(flow))} getFlowDestId={(flow: Flow) => (Cluster.isAggregateFlow(flow) ? flow.dest : getFlowDestId(flow))} getFlowMagnitude={(flow: Flow) => (Cluster.isAggregateFlow(flow) ? flow.count : getFlowMagnitude(flow))} onViewStateChange={this.handleViewStateChange} /> ); } } export default ClusteringExample; ================================================ FILE: examples/src/components/Example.tsx ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Flow, FlowAccessors, FlowLayerPickingInfo, Location, LocationAccessors } from '@flowmap.gl/core'; import FlowMap, { getViewStateForLocations, Highlight, LegendBox, LocationTotalsLegend } from '@flowmap.gl/react'; import React from 'react'; import { ViewState } from '@flowmap.gl/core'; import { mapboxAccessToken } from '../index'; const SHOW_TOP_FLOWS = 10000; export interface Props extends FlowAccessors, LocationAccessors { locations: Location[]; flows: Flow[]; onViewStateChange?: (viewState: ViewState) => void; } interface State { tooltip: FlowLayerPickingInfo | undefined; } const tooltipStyle: React.CSSProperties = { position: 'absolute', pointerEvents: 'none', zIndex: 1, background: '#125', color: '#fff', fontSize: 9, borderRadius: 4, padding: 5, maxWidth: 300, maxHeight: 300, overflow: 'hidden', boxShadow: '2px 2px 4px #ccc', }; export default class Example extends React.Component { state: State = { tooltip: undefined, }; private readonly initialViewState: ViewState; constructor(props: Props) { super(props); const { locations, getLocationCentroid } = props; this.initialViewState = getViewStateForLocations(locations, getLocationCentroid, [ window.innerWidth, window.innerHeight, ]); } handleViewStateChange = (viewState: ViewState) => { const { onViewStateChange } = this.props; if (onViewStateChange) { onViewStateChange(viewState); } const { tooltip } = this.state; if (tooltip) { this.setState({ tooltip: undefined }); } }; handleHighlight = (highlight: Highlight | undefined, info: FlowLayerPickingInfo | undefined) => { if (!info) { this.setState({ tooltip: undefined }); } this.setState({ tooltip: info, }); }; renderTooltip() { const { tooltip } = this.state; if (!tooltip) { return null; } const { object, x, y } = tooltip; if (!object) { return null; } return (
        {JSON.stringify(object, null, 2)}
      
); } render() { const { flows, locations, getLocationId, getLocationCentroid, getFlowMagnitude } = this.props; return ( <> {`Showing ${flows.length > SHOW_TOP_FLOWS ? `top ${SHOW_TOP_FLOWS} of` : ''} ${flows.length} flows. `} {this.renderTooltip()} ); } } ================================================ FILE: examples/src/index.tsx ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import React from 'react'; import './stories'; export const mapboxAccessToken = process.env.MapboxAccessToken || ''; ================================================ FILE: examples/src/stories/index.tsx ================================================ /* * Copyright 2020 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import FlowMapLayer, { Flow, Location } from '@flowmap.gl/core'; import FlowMap, { DiffColorsLegend, getViewStateForFeatures, LegendBox, LocationTotalsLegend } from '@flowmap.gl/react'; import { storiesOf } from '@storybook/react'; import * as d3scaleChromatic from 'd3-scale-chromatic'; import React from 'react'; import { mapboxAccessToken } from '../index'; import pipe from '../utils/pipe'; import { withFetchJson } from '../utils/withFetch'; import withStats from '../utils/withStats'; import ClusteringExample from '../components/ClusteringExample'; import withSheetsFetch from '../utils/withSheetsFetch'; import Example from '../components/Example'; import { DeckGL } from '@deck.gl/react'; import { StaticMap } from 'react-map-gl'; const getLocationId = (loc: Location) => loc.properties.abbr; const DARK_COLORS = { darkMode: true, flows: { scheme: [ 'rgb(0, 22, 61)', 'rgb(0, 27, 62)', 'rgb(0, 36, 68)', 'rgb(0, 48, 77)', 'rgb(3, 65, 91)', 'rgb(48, 87, 109)', 'rgb(85, 115, 133)', 'rgb(129, 149, 162)', 'rgb(179, 191, 197)', 'rgb(240, 240, 240)', ], }, locationAreas: { normal: '#334', }, outlineColor: '#000', }; storiesOf('Basic', module) .add( 'basic as layer', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const flowMapLayer = new FlowMapLayer({ id: 'flow-map-layer', locations, flows, getLocationId: (loc: Location) => loc.properties.abbr, getFlowMagnitude: (f: Flow) => f.count, }); return ( ); }), ) .add( 'basic as interactive component', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( <> )), ) .add( 'custom flow color scheme', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const colors = { flows: { scheme: d3scaleChromatic.schemeGnBu[d3scaleChromatic.schemeGnBu.length - 1] as string[], }, }; return ( <> ); }), ) .add( 'two layers', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { return ( f.origin === 'GE'), getLocationId, }), new FlowMapLayer({ id: 'flow-map-layer-2', colors: { flows: { scheme: d3scaleChromatic.schemeBlues[d3scaleChromatic.schemeBlues.length - 1] as string[], }, }, locations, showLocationAreas: false, flows: flows.filter((f: Flow) => f.origin === 'ZH'), getLocationId, }), ]} > ); }), ) .add( 'bearing pitch', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const viewport = getViewStateForFeatures(locations, [window.innerWidth, window.innerHeight]); return ( <> ); }), ) .add( 'dark mode', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { return ( <> ); }), ) .add( 'custom dark mode color scheme', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const scheme = (d3scaleChromatic.schemeGnBu[d3scaleChromatic.schemeGnBu.length - 1] as string[]) .slice() .reverse(); const colors = { darkMode: true, flows: { scheme, }, locationAreas: { normal: '#334', }, outlineColor: '#000', }; return ( <> ); }), ) .add( 'animated', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'animationTailLength', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const [animationTailLength, setAnimationTailLength] = React.useState(0.7); return ( <> setAnimationTailLength(+evt.currentTarget.value)} /> ); }), ) .add( 'zoom > 12', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'only top 100 flows', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'no location areas', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'no location totals', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'custom zone totals', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const locationIds = locations.features.map(getLocationId).reverse(); const getTotal = (location: Location) => Math.pow(locationIds.indexOf(getLocationId(location)), 10); return ( <> ); }), ) .add( 'flow color override', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( { if (f.origin === 'ZH' && f.dest === 'AG') { return 'orange'; } return undefined; }} /> )), ) .add( 'custom outlines', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'maxLocationCircleSize', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'multiselect', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( )), ) .add( 'non-pickable', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( <> )), ) .add( 'basic', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( <> )), ) .add( 'difference mode', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-diff-2015-2016.json'), )(({ locations, flows }: any) => ( <>
)), ) .add('custom legend', () => ( <>
)) .add( 'opacity', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const [opacity, setOpacity] = React.useState(0.5); return ( <> setOpacity(+evt.currentTarget.value)} /> ); }), ) .add( 'maxFlowThickness', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const [thickness, setThickness] = React.useState(15); return ( <> setThickness(+evt.currentTarget.value)} /> ); }), ) .add( 'flowMagnitudeExtent, locationTotalsExtent', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const [maxFlowMagnitude, setMaxFlowMagnitude] = React.useState(10000); const flowMagnitudeExtent: [number, number] = [0, maxFlowMagnitude]; const [maxLocationTotal, setMaxLocationTotal] = React.useState(500000); const locationTotalsExtent: [number, number] = [0, maxLocationTotal]; return ( <>
setMaxFlowMagnitude(+evt.currentTarget.value)} /> setMaxLocationTotal(+evt.currentTarget.value)} />
Use it to fix the scales of a changing dataset (e.g. over time)
); }), ) .add( 'flowMagnitudeExtent, locationTotalsExtent in diff mode', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-diff-2015-2016.json'), )(({ locations, flows }: any) => { const [maxFlowMagnitude, setMaxFlowMagnitude] = React.useState(500); const flowMagnitudeExtent: [number, number] = [0, maxFlowMagnitude]; const [maxLocationTotal, setMaxLocationTotal] = React.useState(50000); const locationTotalsExtent: [number, number] = [0, maxLocationTotal]; return ( <>
setMaxFlowMagnitude(+evt.currentTarget.value)} /> setMaxLocationTotal(+evt.currentTarget.value)} />
Use it to fix the scales of a changing dataset (e.g. over time)
); }), ) .add( 'maxFlowThickness animated', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { const [thickness, setThickness] = React.useState(15); return ( <> setThickness(+evt.currentTarget.value)} /> ); }), ) .add( 'minPickableFlowThickness', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { return ( <> ); }), ) .add( 'minPickableFlowThickness animated', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => { return ( <> ); }), ); storiesOf('Cluster on zoom', module) .add( 'basic', pipe( withStats, withFetchJson('locations', './data/locations.json'), withFetchJson('flows', './data/flows-2016.json'), )(({ locations, flows }: any) => ( loc.properties.abbr} getLocationCentroid={(loc: Location) => loc.properties.centroid} getFlowOriginId={(flow: Flow) => flow.origin} getFlowDestId={(flow: Flow) => flow.dest} getFlowMagnitude={(flow: Flow) => +flow.count} /> )), ) .add( 'NL commuters', pipe( withStats, withSheetsFetch('1Oe3zM219uSfJ3sjdRT90SAK2kU3xIvzdcCW6cwTsAuc'), )(({ locations, flows }: any) => ( loc.id} getLocationCentroid={(loc: Location): [number, number] => [+loc.lon, +loc.lat]} getFlowOriginId={(flow: Flow) => flow.origin} getFlowDestId={(flow: Flow) => flow.dest} getFlowMagnitude={(flow: Flow) => +flow.count} /> )), ); storiesOf('Other datasets', module) .add( 'London bicycle hire', pipe( withStats, withSheetsFetch('1Z6dVVFFrdooHIs8xnJ_O7eM5bhS5KscCi7G_k0jUNDI'), )(({ locations, flows }: any) => ( loc.id} getLocationCentroid={(location: Location): [number, number] => [+location.lon, +location.lat]} getFlowOriginId={(flow: Flow) => flow.origin} getFlowDestId={(flow: Flow) => flow.dest} getFlowMagnitude={(flow: Flow) => +flow.count} /> )), ) .add( 'NYC citibike', pipe( withStats, withSheetsFetch('1Aum0anWxPx6bHyfcFXWCCTE8u0xtfenIls_kPAJEDIA'), )(({ locations, flows }: any) => ( loc.id} getLocationCentroid={(location: Location): [number, number] => [+location.lon, +location.lat]} getFlowOriginId={(flow: Flow) => flow.origin} getFlowDestId={(flow: Flow) => flow.dest} getFlowMagnitude={(flow: Flow) => +flow.count} /> )), ) .add( 'Chicago taxis', pipe( withStats, withSheetsFetch('1fhX98NFv5gAkkjB2YFCm50-fplFpmWVAZby3dmm9cgQ'), )(({ locations, flows }: any) => ( loc.id} getLocationCentroid={(location: Location): [number, number] => [+location.lon, +location.lat]} getFlowOriginId={(flow: Flow) => flow.origin} getFlowDestId={(flow: Flow) => flow.dest} getFlowMagnitude={(flow: Flow) => +flow.count} /> )), ) .add( 'NL commuters', pipe( withStats, withSheetsFetch('1Oe3zM219uSfJ3sjdRT90SAK2kU3xIvzdcCW6cwTsAuc'), )(({ locations, flows }: any) => ( loc.id} getLocationCentroid={(location: Location): [number, number] => [+location.lon, +location.lat]} getFlowOriginId={(flow: Flow) => flow.origin} getFlowDestId={(flow: Flow) => flow.dest} getFlowMagnitude={(flow: Flow) => +flow.count} /> )), ); ================================================ FILE: examples/src/utils/pipe.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ const pipe = (...args: Function[]): Function => (d: any) => args.reduce((m, f) => f(m), d); export default pipe; ================================================ FILE: examples/src/utils/withFetch.tsx ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { csvParse } from 'd3-dsv'; import React from 'react'; export const Message = ({ children }: { children: string }) =>
{children}
; const withFetch = (mode: 'csv' | 'json', propName: string, url: string) => (Comp: React.ComponentType) => ( props: any, ) => { class Fetcher extends React.Component { state = { error: null, data: null, }; componentDidMount() { fetch(url) .then(response => { if (!response.ok) { throw new Error(response.statusText); } switch (mode) { case 'json': return response.json(); case 'csv': return response.text(); } }) .catch(reason => { console.error(reason); this.setState({ error: true }); }) .then(data => this.setState({ data: mode === 'csv' ? csvParse(data) : data })); } render() { const { data, error } = this.state; if (error) { return Oops… Data fetching failed.; } if (!data) { return Fetching data…; } return ; } } return ; }; export const withFetchCsv = (propName: string, url: string) => withFetch('csv', propName, url); export const withFetchJson = (propName: string, url: string) => withFetch('json', propName, url); ================================================ FILE: examples/src/utils/withSheetsFetch.tsx ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Flow, Location } from '@flowmap.gl/core'; import * as React from 'react'; import pipe from './pipe'; import { withFetchCsv } from './withFetch'; const withSheetsFetch = (sheetKey: string) => (Comp: React.ComponentType) => (props: any) => { const Fetcher = pipe( withFetchCsv('locations', `https://docs.google.com/spreadsheets/d/${sheetKey}/gviz/tq?tqx=out:csv&sheet=locations`), withFetchCsv('flows', `https://docs.google.com/spreadsheets/d/${sheetKey}/gviz/tq?tqx=out:csv&sheet=flows`), )(({ locations, flows }: { locations: Location[]; flows: Flow[] }) => { return ; }); return ; }; export default withSheetsFetch; ================================================ FILE: examples/src/utils/withStats.tsx ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import * as React from 'react'; import Stats from 'stats.js'; export default function withStats

(Comp: React.ComponentType

) { return (props: P) => { class WithStats extends React.Component { private stats: Stats = new Stats(); private statsContainer = React.createRef(); private animateRef: number = 0; componentDidMount() { if (this.statsContainer.current) { this.stats.showPanel(0); this.statsContainer.current.appendChild(this.stats.dom); } const calcFPS = () => { this.stats.begin(); this.stats.end(); this.animateRef = window.requestAnimationFrame(calcFPS); }; this.animateRef = window.requestAnimationFrame(calcFPS); } componentWillUnmount() { window.cancelAnimationFrame(this.animateRef); } render() { return ( <>

); } } return ; }; } ================================================ FILE: examples/tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "module": "commonjs", "esModuleInterop": true, "outDir": "dist", "declaration": true, "declarationMap": true, "sourceMap": true, "strict": true, "skipLibCheck": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, "jsx": "react", "lib": [ "es2015", "dom" ] }, "include": [ "src/**/*", "examples/**/*", "../typings/*" ] } ================================================ FILE: lerna.json ================================================ { "packages": [ "examples", "packages/*" ], "version": "7.3.4", "npmClient": "yarn", "useWorkspaces": true } ================================================ FILE: package.json ================================================ { "name": "flowmap.gl", "description": "Flow map drawing layer for deck.gl", "author": "Teralytics AG", "license": "Apache-2.0", "private": true, "keywords": [ "flows", "flow map", "webgl", "visualization", "overlay", "layer" ], "repository": { "type": "git", "url": "https://github.com/teralytics/flowmap.gl.git" }, "main": "dist/index.js", "module": "dist-esm/index.js", "types": "dist/index.d.ts", "files": [ "src", "dist", "dist-esm" ], "scripts": { "start": "cd examples && yarn start", "dev": "yarn core-dev & yarn react-dev & yarn cluster-dev & yarn start", "core-dev": "cd packages/core && yarn dev", "react-dev": "cd packages/react && yarn dev", "cluster-dev": "cd packages/cluster && yarn dev", "build": "cd packages/core && yarn build && cd ../../packages/react && yarn build && cd ../../packages/cluster && yarn build", "storybook:build": "cd examples && yarn storybook:build", "storybook:deploy": "cd examples && yarn storybook:deploy", "bump": "lerna version --no-push --force-publish", "publish-prerelease": "lerna publish --dist-tag next from-package", "lint": "tslint --fix '{examples,packages/*}/{src,test,stories}/**/*.{ts,tsx}'", "typecheck": "lerna run --stream typecheck" }, "devDependencies": { "gh-pages": "^3.1.0", "husky": "^4.3.0", "lerna": "^3.20.2", "lint-staged": "^10.5.2", "prettier": "^2.2.0", "tslint": "^6.1.2", "tslint-config-prettier": "^1.18.0", "tslint-plugin-prettier": "^2.3.0", "tslint-react": "^5.0.0", "typescript": "^4.1.2" }, "lint-staged": { "{examples,packages/*}/{src,test}/**/*.{ts,tsx}": [ "tslint --fix" ] }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "workspaces": [ "packages/*", "examples" ] } ================================================ FILE: packages/cluster/package.json ================================================ { "name": "@flowmap.gl/cluster", "version": "7.3.4", "description": "Location/flow clustering for @flowmap.gl/core", "main": "dist/index.js", "module": "dist-esm/index.js", "types": "dist/index.d.ts", "files": [ "src", "dist", "dist-esm" ], "scripts": { "dev": "tsc --watch & tsc --watch --project tsconfig.build.esm.json", "build:es5": "rm -rf dist && tsc --project tsconfig.build.json", "build:esm": "rm -rf dist-esm && tsc --project tsconfig.build.esm.json", "build": "yarn build:es5 && yarn build:esm", "typecheck": "tsc --noEmit", "prepare": "yarn build" }, "repository": { "type": "git", "url": "git+https://github.com/teralytics/flowmap.gl.git" }, "author": "Teralytics AG", "license": "Apache-2.0", "bugs": { "url": "https://github.com/teralytics/flowmap.gl/issues" }, "homepage": "https://github.com/teralytics/flowmap.gl#readme", "dependencies": { "@types/d3-array": "^2.0.0", "d3-array": "^2.7.1", "kdbush": "^3.0.0" }, "devDependencies": { "@flowmap.gl/core": "^7.3.4", "@types/geojson": "^7946.0.7", "react": "^16.13.1", "typescript": "^4.0.2" }, "publishConfig": { "access": "public" }, "gitHead": "685236ef24d44ee2c6f0c2aed79dd9c52a4e73fe" } ================================================ FILE: packages/cluster/src/ClusterIndex.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Flow, FlowAccessors, Location, LocationAccessors } from '@flowmap.gl/core'; import { ascending, bisectLeft, extent } from 'd3-array'; import { AggregateFlow, Cluster, ClusterLevels, ClusterNode, FlowCountsMapReduce, isCluster } from './types'; export type FlowItem = Flow | AggregateFlow; export type LocationWeightGetter = (id: string) => number; /** * A data structure representing the cluster levels for efficient flow aggregation. */ export interface ClusterIndex { availableZoomLevels: number[]; getClusterById: (clusterId: string) => Cluster | undefined; /** * List the nodes on the given zoom level. */ getClusterNodesFor: (zoom: number | undefined) => ClusterNode[] | undefined; /** * Get the min zoom level on which the location is not clustered. */ getMinZoomForLocation: (locationId: string) => number; /** * List the IDs of all locations in the cluster (leaves of the subtree starting in the cluster). */ expandCluster: (cluster: Cluster, targetZoom?: number) => string[]; /** * Find the cluster the given location is residing in on the specified zoom level. */ findClusterFor: (locationId: string, zoom: number) => string | undefined; /** * Aggregate flows for the specified zoom level. */ aggregateFlows: ( flows: Flow[], zoom: number, { getFlowOriginId, getFlowDestId, getFlowMagnitude }: FlowAccessors, options?: { flowCountsMapReduce?: FlowCountsMapReduce; }, ) => FlowItem[]; } /** * Build ClusterIndex from the given cluster hierarchy */ export function buildIndex(clusterLevels: ClusterLevels): ClusterIndex { const nodesByZoom = new Map(); const clustersById = new Map(); const minZoomByLocationId = new Map(); for (const { zoom, nodes } of clusterLevels) { nodesByZoom.set(zoom, nodes); for (const node of nodes) { if (isCluster(node)) { clustersById.set(node.id, node); } else { const { id } = node; const mz = minZoomByLocationId.get(id); if (mz == null || mz > zoom) { minZoomByLocationId.set(id, zoom); } } } } const [minZoom, maxZoom] = extent(clusterLevels, cl => cl.zoom); if (minZoom == null || maxZoom == null) { throw new Error('Could not determine minZoom or maxZoom'); } const leavesToClustersByZoom = new Map>(); for (const cluster of clustersById.values()) { const { zoom } = cluster; let leavesToClusters = leavesToClustersByZoom.get(zoom); if (!leavesToClusters) { leavesToClusters = new Map(); leavesToClustersByZoom.set(zoom, leavesToClusters); } visitClusterLeaves(cluster, leafId => { leavesToClusters!.set(leafId, cluster); }); } function visitClusterLeaves(cluster: Cluster, visit: (id: string) => void) { for (const childId of cluster.children) { const child = clustersById.get(childId); if (child) { visitClusterLeaves(child, visit); } else { visit(childId); } } } const expandCluster = (cluster: Cluster, targetZoom: number = maxZoom) => { const ids: string[] = []; const visit = (c: Cluster, expandedIds: string[]) => { if (targetZoom > c.zoom) { for (const childId of c.children) { const child = clustersById.get(childId); if (child) { visit(child, expandedIds); } else { expandedIds.push(childId); } } } else { expandedIds.push(c.id); } }; visit(cluster, ids); return ids; }; function findClusterFor(locationId: string, zoom: number) { const leavesToClusters = leavesToClustersByZoom.get(zoom); if (!leavesToClusters) { return undefined; } const cluster = leavesToClusters.get(locationId); return cluster ? cluster.id : undefined; } const availableZoomLevels = clusterLevels.map(cl => +cl.zoom).sort((a, b) => ascending(a, b)); return { availableZoomLevels, getClusterNodesFor: zoom => { if (zoom === undefined) { return undefined; } return nodesByZoom.get(zoom); }, getClusterById: clusterId => clustersById.get(clusterId), getMinZoomForLocation: locationId => minZoomByLocationId.get(locationId) || minZoom, expandCluster, findClusterFor, aggregateFlows: (flows, zoom, { getFlowOriginId, getFlowDestId, getFlowMagnitude }, options = {}) => { if (zoom > maxZoom) { return flows; } const result: Flow[] = []; const aggFlowsByKey = new Map(); const makeKey = (origin: string, dest: string) => `${origin}:${dest}`; const { flowCountsMapReduce = { map: getFlowMagnitude, reduce: (acc: any, count: number) => (acc || 0) + count, }, } = options; for (const flow of flows) { const origin = getFlowOriginId(flow); const dest = getFlowDestId(flow); const originCluster = findClusterFor(origin, zoom) || origin; const destCluster = findClusterFor(dest, zoom) || dest; const key = makeKey(originCluster, destCluster); if (originCluster === origin && destCluster === dest) { result.push(flow); } else { let aggregateFlow = aggFlowsByKey.get(key); if (!aggregateFlow) { aggregateFlow = { origin: originCluster, dest: destCluster, count: flowCountsMapReduce.map(flow), aggregate: true, }; result.push(aggregateFlow); aggFlowsByKey.set(key, aggregateFlow); } else { aggregateFlow.count = flowCountsMapReduce.reduce(aggregateFlow.count, flowCountsMapReduce.map(flow)); } } } return result; }, }; } export function makeLocationWeightGetter( flows: Flow[], { getFlowOriginId, getFlowDestId, getFlowMagnitude }: 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); } return (id: string) => Math.max(Math.abs(locationTotals.incoming.get(id) || 0), Math.abs(locationTotals.outgoing.get(id) || 0)); } /** * @param availableZoomLevels Must be sorted in ascending order * @param targetZoom */ export function findAppropriateZoomLevel(availableZoomLevels: number[], targetZoom: number) { if (!availableZoomLevels.length) { throw new Error('No available zoom levels'); } return availableZoomLevels[ Math.min(bisectLeft(availableZoomLevels, Math.floor(targetZoom)), availableZoomLevels.length - 1) ]; } ================================================ FILE: packages/cluster/src/cluster.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /** * The code in this file is a based on https://github.com/mapbox/supercluster */ // ISC License // // Copyright (c) 2016, Mapbox // // Permission to use, copy, modify, and/or distribute this software for any purpose // with or without fee is hereby granted, provided that the above copyright notice // and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS // OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF // THIS SOFTWARE. import { Location, LocationAccessors } from '@flowmap.gl/core'; import { rollup } from 'd3-array'; import KDBush from 'kdbush'; import { LocationWeightGetter } from './ClusterIndex'; import { Cluster, ClusterLevel, ClusterNode } from './types'; export interface Options { minZoom: number; // min zoom to generate clusters on maxZoom: number; // max zoom level to cluster the points on radius: number; // cluster radius in pixels extent: number; // tile extent (radius is calculated relative to it) nodeSize: number; // size of the KD-tree leaf node, affects performance makeClusterName: (id: number, numPoints: number) => string | undefined; makeClusterId: (id: number) => string; } const defaultOptions: Options = { minZoom: 0, maxZoom: 16, radius: 40, extent: 512, nodeSize: 64, makeClusterName: (id: number, numPoints: number) => undefined, makeClusterId: (id: number) => `{[${id}]}`, }; interface BasePoint { x: number; // projected point coordinates y: number; weight: number; zoom: number; // the last zoom the point was processed at parentId: number; // parent cluster id } interface LeafPoint extends BasePoint { index: number; // index of the source feature in the original input array, } interface ClusterPoint extends BasePoint { id: number; numPoints: number; } type Point = LeafPoint | ClusterPoint; export function isLeafPoint(p: Point): p is LeafPoint { const { index } = p as LeafPoint; return index != null; } export function isClusterPoint(p: Point): p is ClusterPoint { const { id } = p as ClusterPoint; return id != null; } type ZoomLevelKDBush = any; export function clusterLocations( locations: Location[], locationAccessors: LocationAccessors, getLocationWeight: LocationWeightGetter, options?: Partial, ): ClusterLevel[] { const { getLocationCentroid, getLocationId } = locationAccessors; const opts = { ...defaultOptions, ...options, }; const { minZoom, maxZoom, nodeSize, makeClusterName, makeClusterId } = opts; const trees = new Array(maxZoom + 1); // generate a cluster object for each point and index input points into a KD-tree let clusters = new Array(); for (let i = 0; i < locations.length; i++) { const [x, y] = getLocationCentroid(locations[i]); clusters.push({ x: lngX(x), // projected point coordinates y: latY(y), weight: getLocationWeight(getLocationId(locations[i])), zoom: Infinity, // the last zoom the point was processed at index: i, // index of the source feature in the original input array, parentId: -1, // parent cluster id }); } trees[maxZoom + 1] = new KDBush(clusters, getX, getY, nodeSize, Float32Array); // cluster points on max zoom, then cluster the results on previous zoom, etc.; // results in a cluster hierarchy across zoom levels for (let z = maxZoom; z >= minZoom; z--) { // create a new set of clusters for the zoom and index them with a KD-tree clusters = cluster(clusters, z, trees[z + 1], opts); trees[z] = new KDBush(clusters, getX, getY, nodeSize, Float32Array); } if (trees.length === 0) { return []; } const numbersOfClusters = trees.map(d => d.points.length); const maxAvailZoom = numbersOfClusters.indexOf(numbersOfClusters[numbersOfClusters.length - 1]); const minAvailZoom = Math.min(maxAvailZoom, numbersOfClusters.lastIndexOf(numbersOfClusters[0])); const clusterLevels = new Array(); for (let zoom = minAvailZoom; zoom <= maxAvailZoom; zoom++) { let childrenByParent: Map | undefined; const tree = trees[zoom]; if (zoom < maxAvailZoom) { childrenByParent = rollup( trees[zoom + 1].points, (points: any[]) => points.map((p: any) => (p.id ? makeClusterId(p.id) : getLocationId(locations[p.index]))), (point: any) => point.parentId, ); } const nodes: ClusterNode[] = []; for (const point of tree.points) { const { x, y, numPoints } = point; if (isLeafPoint(point)) { const location = locations[point.index]; nodes.push({ id: getLocationId(location), zoom, centroid: getLocationCentroid(location), }); } else if (isClusterPoint(point)) { const { id } = point; const children = childrenByParent && childrenByParent.get(id); if (!children) { throw new Error(`Cluster ${id} doesn't have children`); } nodes.push({ id: makeClusterId(id), name: makeClusterName(id, numPoints), zoom, centroid: [xLng(x), yLat(y)] as [number, number], children, } as Cluster); } } clusterLevels.push({ zoom, nodes, }); } return clusterLevels; } function createCluster(x: number, y: number, id: number, numPoints: number, weight: number): ClusterPoint { return { x, // weighted cluster center y, zoom: Infinity, // the last zoom the cluster was processed at id, // encodes index of the first child of the cluster and its zoom level parentId: -1, // parent cluster id numPoints, weight, }; } function cluster(points: Point[], zoom: number, tree: ZoomLevelKDBush, options: Options) { const clusters: Point[] = []; const { radius, extent } = options; const r = radius / (extent * Math.pow(2, zoom)); // loop through each point for (let i = 0; i < points.length; i++) { const p = points[i]; // if we've already visited the point at this zoom level, skip it if (p.zoom <= zoom) { continue; } p.zoom = zoom; // find all nearby points const neighborIds = tree.within(p.x, p.y, r); let weight = p.weight || 1; let numPoints = isClusterPoint(p) ? p.numPoints : 1; let wx = p.x * weight; let wy = p.y * weight; // encode both zoom and point index on which the cluster originated const id = (i << 5) + (zoom + 1); for (const neighborId of neighborIds) { const b = tree.points[neighborId]; // filter out neighbors that are already processed if (b.zoom <= zoom) { continue; } b.zoom = zoom; // save the zoom (so it doesn't get processed twice) const weight2 = b.weight || 1; const numPoints2 = b.numPoints || 1; wx += b.x * weight2; // accumulate coordinates for calculating weighted center wy += b.y * weight2; weight += weight2; numPoints += numPoints2; b.parentId = id; } if (numPoints === 1) { clusters.push(p); } else { p.parentId = id; clusters.push(createCluster(wx / weight, wy / weight, id, numPoints, weight)); } } return clusters; } // spherical mercator to longitude/latitude function xLng(x: number) { return (x - 0.5) * 360; } function yLat(y: number) { const y2 = ((180 - y * 360) * Math.PI) / 180; return (360 * Math.atan(Math.exp(y2))) / Math.PI - 90; } // longitude/latitude to spherical mercator in [0..1] range function lngX(lng: number) { return lng / 360 + 0.5; } function latY(lat: number) { const sin = Math.sin((lat * Math.PI) / 180); const y = 0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI; return y < 0 ? 0 : y > 1 ? 1 : y; } function getX(p: Point) { return p.x; } function getY(p: Point) { return p.y; } ================================================ FILE: packages/cluster/src/index.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ export * from './ClusterIndex'; export * from './types'; export { clusterLocations } from './cluster'; ================================================ FILE: packages/cluster/src/types.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Flow } from '@flowmap.gl/core'; export interface ClusterNode { id: string; zoom: number; centroid: [number, number]; } export interface ClusterLevel { zoom: number; nodes: ClusterNode[]; } export type ClusterLevels = ClusterLevel[]; export interface Cluster extends ClusterNode { name?: string; children: string[]; } export function isCluster(c: ClusterNode): c is Cluster { const { children } = c as Cluster; return children && children.length > 0; } export interface AggregateFlow { origin: string; dest: string; count: number; aggregate: true; } export function isAggregateFlow(flow: Flow): flow is AggregateFlow { const { origin, dest, count, aggregate } = flow as AggregateFlow; return origin !== undefined && dest !== undefined && count !== undefined && (aggregate ? true : false); } export interface FlowCountsMapReduce { map: (flow: Flow) => T; reduce: (accumulated: T, val: T) => T; } ================================================ FILE: packages/cluster/tsconfig.build.esm.json ================================================ { "extends": "./tsconfig.build.json", "compilerOptions": { "target": "es2017", "module": "es2015", "outDir": "dist-esm", "moduleResolution": "node" } } ================================================ FILE: packages/cluster/tsconfig.build.json ================================================ { "extends": "./tsconfig.json", "include": [ "src/**/*", "../../typings/*" ] } ================================================ FILE: packages/cluster/tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "module": "commonjs", "esModuleInterop": true, "outDir": "dist", "declaration": true, "declarationMap": true, "sourceMap": true, "strict": true, "skipLibCheck": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, "downlevelIteration": true, "lib": [ "es2015", "dom" ] }, "include": [ "src/**/*", "examples/**/*", "../../typings/*", "stories/**/*" ] } ================================================ FILE: packages/core/package.json ================================================ { "name": "@flowmap.gl/core", "version": "7.3.4", "description": "Flow map drawing layer for deck.gl", "main": "dist/index.js", "module": "dist-esm/index.js", "types": "dist/index.d.ts", "files": [ "src", "dist", "dist-esm" ], "scripts": { "dev": "tsc --watch & tsc --watch --project tsconfig.build.esm.json", "build:es5": "rm -rf dist && tsc --project tsconfig.build.json", "build:esm": "rm -rf dist-esm && tsc --project tsconfig.build.esm.json", "build": "yarn build:es5 && yarn build:esm", "typecheck": "tsc --noEmit", "prepare": "yarn build" }, "repository": { "type": "git", "url": "git+https://github.com/teralytics/flowmap.gl.git" }, "keywords": [ "flows", "flow", "map", "webgl", "visualization", "overlay", "layer" ], "author": "Teralytics AG", "license": "Apache-2.0", "bugs": { "url": "https://github.com/teralytics/flowmap.gl/issues" }, "homepage": "https://github.com/teralytics/flowmap.gl#readme", "dependencies": { "d3-array": "^2.7.1", "d3-collection": "^1.0.7", "d3-color": "^2.0.0", "d3-interpolate": "^2.0.1", "d3-scale": "^3.2.2", "reselect": "^4.0.0" }, "devDependencies": { "@deck.gl/core": "^8.2.8", "@deck.gl/layers": "^8.2.8", "@luma.gl/constants": "^8.2.0", "@luma.gl/core": "^8.2.0", "@types/d3-array": "^2.0.0", "@types/d3-collection": "^1.0.8", "@types/d3-color": "^1.2.2", "@types/d3-interpolate": "^1.3.1", "@types/d3-scale": "^2.2.0", "@types/geojson": "^7946.0.7", "@types/node": "^14.10.1", "lint-staged": "^10.3.0", "typescript": "^4.0.2" }, "peerDependencies": { "@deck.gl/core": ">= 8.0.0", "@deck.gl/layers": ">= 8.0.0", "@luma.gl/constants": ">= 8.0.0", "@luma.gl/core": ">= 8.0.0" }, "gitHead": "685236ef24d44ee2c6f0c2aed79dd9c52a4e73fe", "publishConfig": { "access": "public" } } ================================================ FILE: packages/core/src/AnimatedFlowLinesLayer/AnimatedFlowLinesLayer.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * This file was modified by Teralytics. The original file is licenced under: * * Copyright (c) 2015-2017 Uber Technologies, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ import { Layer, picking, project32 } from '@deck.gl/core'; import GL from '@luma.gl/constants'; import { Geometry, Model } from '@luma.gl/core'; import { RGBA } from '../colors'; import { AccessorObjectInfo, Flow } from '../types'; import FragmentShader from './AnimatedFlowLinesLayerFragment.glsl'; import VertexShader from './AnimatedFlowLinesLayerVertex.glsl'; import { LayerProps } from '../LayerProps'; export interface Props extends LayerProps { id: string; opacity?: number; pickable?: boolean; updateTriggers?: { [key: string]: {} }; data: Flow[]; drawOutline: boolean; outlineColor?: RGBA; outlineThickness?: number; currentTime?: number; thicknessUnit?: number; animationTailLength?: number; getSourcePosition?: (d: Flow) => [number, number]; getTargetPosition?: (d: Flow) => [number, number]; getStaggering?: (d: Flow, info: AccessorObjectInfo) => number; getPickable?: (d: Flow, { index }: { index: number }) => number; // >= 1.0 -> true getColor?: (d: Flow) => RGBA; getThickness?: (d: Flow) => number; getEndpointOffsets?: (d: Flow) => [number, number]; } const DEFAULT_COLOR: RGBA = [0, 132, 193, 255]; export default class AnimatedFlowLinesLayer extends Layer { static defaultProps = { currentTime: 0, animationTailLength: 0.7, getSourcePosition: { type: 'accessor', value: (d: Flow) => d.sourcePosition }, getTargetPosition: { type: 'accessor', value: (d: Flow) => d.targetPosition }, getPickable: { type: 'accessor', value: (d: Flow) => 1.0 }, getStaggering: { type: 'accessor', value: (d: Flow, { index }: { index: number }) => Math.random() }, getColor: { type: 'accessor', value: DEFAULT_COLOR }, getThickness: { type: 'accessor', value: 1 }, thicknessUnit: 15 * 2, parameters: { depthTest: false, }, }; constructor(props: Props) { super(props); } getShaders() { return super.getShaders({ vs: VertexShader, fs: FragmentShader, modules: [project32, picking], shaderCache: this.context.shaderCache, }); } initializeState() { const attributeManager = this.getAttributeManager(); /* eslint-disable max-len */ attributeManager.addInstanced({ instanceSourcePositions: { size: 3, transition: true, accessor: 'getSourcePosition', }, instanceTargetPositions: { size: 3, transition: true, accessor: 'getTargetPosition', }, instanceColors: { size: 4, type: GL.UNSIGNED_BYTE, transition: true, accessor: 'getColor', defaultValue: [0, 0, 0, 255], }, instanceWidths: { size: 1, transition: true, accessor: 'getThickness', defaultValue: 1, }, instanceStaggering: { accessor: 'getStaggering', size: 1, transition: false, }, instancePickable: { accessor: 'getPickable', size: 1, transition: false, }, }); /* eslint-enable max-len */ } updateState({ props, oldProps, changeFlags }: any) { super.updateState({ props, oldProps, changeFlags }); if (changeFlags.extensionsChanged) { const { gl } = this.context; if (this.state.model) { this.state.model.delete(); } this.setState({ model: this._getModel(gl) }); this.getAttributeManager().invalidateAll(); } } draw({ uniforms }: any) { const { currentTime, thicknessUnit, animationTailLength } = this.props; this.state.model .setUniforms({ ...uniforms, thicknessUnit: thicknessUnit! * 4, animationTailLength, currentTime, }) .draw(); } _getModel(gl: WebGLRenderingContext) { /* * (0, -1)-------------_(1, -1) * | _,-" | * o _,-" o * | _,-" | * (0, 1)"-------------(1, 1) */ const positions = [0, -1, 0, 0, 1, 0, 1, -1, 0, 1, 1, 0]; return new Model( gl, Object.assign({}, this.getShaders(), { id: this.props.id, geometry: new Geometry({ drawMode: GL.TRIANGLE_STRIP, attributes: { positions: new Float32Array(positions), }, }), isInstanced: true, }), ); } } ================================================ FILE: packages/core/src/AnimatedFlowLinesLayer/AnimatedFlowLinesLayerFragment.glsl.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * This file was modified by Teralytics. The original file is licenced under: * * Copyright (c) 2015-2017 Uber Technologies, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ export default `\ #define SHADER_NAME animated-flow-lines-layer-fragment-shader precision highp float; uniform float animationTailLength; varying vec4 vColor; varying float sourceToTarget; varying vec2 uv; void main(void) { geometry.uv = uv; gl_FragColor = vec4(vColor.xyz, vColor.w * smoothstep(1.0 - animationTailLength, 1.0, fract(sourceToTarget))); DECKGL_FILTER_COLOR(gl_FragColor, geometry); } `; ================================================ FILE: packages/core/src/AnimatedFlowLinesLayer/AnimatedFlowLinesLayerVertex.glsl.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * This file was modified by Teralytics. The original file is licenced under: * * Copyright (c) 2015-2017 Uber Technologies, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ export default `\ #define SHADER_NAME animated-flow-lines-layer-vertex-shader #define SPEED 0.015 #define NUM_PARTS 5.0 attribute vec3 positions; attribute vec3 instanceSourcePositions; attribute vec3 instanceTargetPositions; attribute vec3 instanceSourcePositions64Low; attribute vec3 instanceTargetPositions64Low; attribute vec4 instanceColors; attribute vec3 instancePickingColors; attribute float instanceWidths; attribute float instancePickable; attribute float instanceStaggering; uniform float opacity; uniform float currentTime; uniform float thicknessUnit; varying vec4 vColor; varying float sourceToTarget; varying vec2 uv; // offset vector by strokeWidth pixels // offset_direction is -1 (left) or 1 (right) vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction, float width) { // normalized direction of the line vec2 dir_screenspace = normalize(line_clipspace * project_uViewportSize); // rotate by 90 degrees dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x); return dir_screenspace * offset_direction * width / 2.0; } void main(void) { geometry.worldPosition = instanceSourcePositions; geometry.worldPositionAlt = instanceTargetPositions; // Position vec4 source_commonspace; vec4 target_commonspace; vec4 source = project_position_to_clipspace(instanceSourcePositions, instanceSourcePositions64Low, vec3(0.), source_commonspace); vec4 target = project_position_to_clipspace(instanceTargetPositions, instanceTargetPositions64Low, vec3(0.), target_commonspace); float widthPixels = instanceWidths * thicknessUnit; // linear interpolation of source & target to pick right coord float segmentIndex = positions.x; vec4 p = mix(source, target, segmentIndex); geometry.position = mix(source_commonspace, target_commonspace, segmentIndex); uv = positions.xy; geometry.uv = uv; if (instancePickable > 0.5) { geometry.pickingColor = instancePickingColors; } // extrude vec3 offset = vec3( getExtrusionOffset(target.xy - source.xy, positions.y, widthPixels), 0.0); DECKGL_FILTER_SIZE(offset, geometry); gl_Position = p + vec4(project_pixel_size_to_clipspace(offset.xy), 0.0, 0.0); DECKGL_FILTER_GL_POSITION(gl_Position, geometry); // Color vColor = vec4(instanceColors.rgb, instanceColors.a * opacity) / 255.; DECKGL_FILTER_COLOR(vColor, geometry); sourceToTarget = positions.x * length(source - target) * NUM_PARTS - currentTime * SPEED + instanceStaggering; } `; ================================================ FILE: packages/core/src/AnimatedFlowLinesLayer/index.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import AnimatedFlowLinesLayer from './AnimatedFlowLinesLayer'; export default AnimatedFlowLinesLayer; ================================================ FILE: packages/core/src/FlowCirclesLayer/FlowCirclesLayer.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Layer, picking, project32 } from '@deck.gl/core'; import GL from '@luma.gl/constants'; import { Geometry, Model } from '@luma.gl/core'; import { RGBA } from '../colors'; import FragmentShader from './FlowCirclesLayerFragment.glsl'; import VertexShader from './FlowCirclesLayerVertex.glsl'; import { LayerProps } from '../LayerProps'; export type FlowCirclesDatum = any; export interface Props extends LayerProps { id: string; opacity?: number; pickable?: boolean; updateTriggers?: { [key: string]: {} }; getColor?: (d: FlowCirclesDatum) => RGBA; getPosition?: (d: FlowCirclesDatum) => [number, number]; getRadius?: (d: FlowCirclesDatum) => number; data: FlowCirclesDatum[]; } const DEFAULT_COLOR = [0, 0, 0, 255]; class FlowCirclesLayer extends Layer { static layerName: string = 'FlowCirclesLayer'; static defaultProps = { getColor: { type: 'accessor', value: DEFAULT_COLOR }, getPosition: { type: 'accessor', value: (d: FlowCirclesDatum) => d.position }, getRadius: { type: 'accessor', value: 1 }, parameters: { depthTest: false, }, }; props!: Props; constructor(props: Props) { super(props); } getShaders() { return super.getShaders({ vs: VertexShader, fs: FragmentShader, modules: [project32, picking], }); } initializeState() { this.getAttributeManager().addInstanced({ instancePositions: { size: 3, type: GL.DOUBLE, fp64: this.use64bitPositions(), transition: true, accessor: 'getPosition', }, instanceRadius: { size: 1, transition: true, accessor: 'getRadius', defaultValue: 1, }, instanceColors: { size: 4, transition: true, type: GL.UNSIGNED_BYTE, accessor: 'getColor', defaultValue: DEFAULT_COLOR, }, }); } updateState({ props, oldProps, changeFlags }: any) { super.updateState({ props, oldProps, changeFlags }); if (changeFlags.extensionsChanged) { const { gl } = this.context; if (this.state.model) { this.state.model.delete(); } this.setState({ model: this._getModel(gl) }); this.getAttributeManager().invalidateAll(); } } draw({ uniforms }: any) { this.state.model.setUniforms(uniforms).draw(); } _getModel(gl: WebGLRenderingContext) { // a square that minimally cover the unit circle const positions = [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]; return new Model( gl, Object.assign(this.getShaders(), { id: this.props.id, geometry: new Geometry({ drawMode: GL.TRIANGLE_FAN, vertexCount: 4, attributes: { positions: { size: 3, value: new Float32Array(positions) }, }, }), isInstanced: true, }), ); } } export default FlowCirclesLayer; ================================================ FILE: packages/core/src/FlowCirclesLayer/FlowCirclesLayerFragment.glsl.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ export default `\ #define SHADER_NAME flow-circles-layer-fragment-shader #define SOFT_OUTLINE 0.1 precision highp float; varying vec4 vColor; varying vec2 unitPosition; varying float outerRadiusPixels; void main(void) { geometry.uv = unitPosition; float distToCenter = length(unitPosition); if (distToCenter > 1.0) { discard; } gl_FragColor = vColor; gl_FragColor.a *= smoothstep(0.0, SOFT_OUTLINE, 1.0 - distToCenter); DECKGL_FILTER_COLOR(gl_FragColor, geometry); } `; ================================================ FILE: packages/core/src/FlowCirclesLayer/FlowCirclesLayerVertex.glsl.ts ================================================ // Copyright (c) 2015 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. export default `\ #define SHADER_NAME flow-circles-layer-vertex-shader #define radiusScale 100 attribute vec3 positions; attribute vec3 instancePositions; attribute vec3 instancePositions64Low; attribute float instanceRadius; attribute vec4 instanceColors; attribute vec3 instancePickingColors; uniform float opacity; varying vec4 vColor; varying vec2 unitPosition; varying float outerRadiusPixels; void main(void) { geometry.worldPosition = instancePositions; outerRadiusPixels = instanceRadius; // position on the containing square in [-1, 1] space unitPosition = positions.xy; geometry.uv = unitPosition; geometry.pickingColor = instancePickingColors; // Find the center of the point and add the current vertex vec3 offset = positions * project_pixel_size(outerRadiusPixels); DECKGL_FILTER_SIZE(offset, geometry); gl_Position = project_position_to_clipspace(instancePositions, instancePositions64Low, offset, geometry.position); DECKGL_FILTER_GL_POSITION(gl_Position, geometry); // Apply opacity to instance color, or return instance picking color vColor = vec4(instanceColors.rgb / 255., instanceColors.a / 255. * opacity); DECKGL_FILTER_COLOR(vColor, geometry); } `; ================================================ FILE: packages/core/src/FlowCirclesLayer/index.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import FlowCirclesLayer from './FlowCirclesLayer'; export default FlowCirclesLayer; ================================================ FILE: packages/core/src/FlowLinesLayer/FlowLinesLayer.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Layer, picking, project32 } from '@deck.gl/core'; import GL from '@luma.gl/constants'; import { Geometry, Model } from '@luma.gl/core'; import { RGBA } from '../colors'; import { Flow } from '../types'; import FragmentShader from './FlowLinesLayerFragment.glsl'; import VertexShader from './FlowLinesLayerVertex.glsl'; import { LayerProps } from '../LayerProps'; export interface Props extends LayerProps { id: string; opacity?: number; pickable?: boolean; updateTriggers?: { [key: string]: {} }; data: Flow[]; drawOutline: boolean; outlineColor?: RGBA; outlineThickness?: number; thicknessUnit?: number; getSourcePosition?: (d: Flow) => [number, number]; getTargetPosition?: (d: Flow) => [number, number]; getColor?: (d: Flow) => RGBA; getThickness?: (d: Flow) => number; getPickable?: (d: Flow, { index }: { index: number }) => number; // >= 1.0 -> true getEndpointOffsets?: (d: Flow) => [number, number]; } const DEFAULT_COLOR: RGBA = [0, 132, 193, 255]; const INNER_SIDE_OUTLINE_THICKNESS = 1; class FlowLinesLayer extends Layer { static layerName: string = 'FlowLinesLayer'; static defaultProps = { getSourcePosition: { type: 'accessor', value: (d: Flow) => d.sourcePosition }, getTargetPosition: { type: 'accessor', value: (d: Flow) => d.targetPosition }, getColor: { type: 'accessor', value: DEFAULT_COLOR }, getThickness: { type: 'accessor', value: (d: Flow) => d.thickness }, // 0..0.5 getPickable: { type: 'accessor', value: (d: Flow) => 1.0 }, drawOutline: true, thicknessUnit: 10, outlineThickness: 1, outlineColor: [255, 255, 255, 255], parameters: { depthTest: false, }, }; props!: Props; constructor(props: Props) { super(props); } getShaders() { return super.getShaders({ vs: VertexShader, fs: FragmentShader, modules: [project32, picking], shaderCache: this.context.shaderCache, }); } initializeState() { const { attributeManager } = this.state; attributeManager.addInstanced({ instanceSourcePositions: { accessor: 'getSourcePosition', size: 3, transition: false, type: GL.DOUBLE, }, instanceTargetPositions: { accessor: 'getTargetPosition', size: 3, transition: false, type: GL.DOUBLE, }, instanceThickness: { accessor: 'getThickness', size: 1, transition: false, }, instanceEndpointOffsets: { accessor: 'getEndpointOffsets', size: 2, transition: false, }, instanceColors: { accessor: 'getColor', size: 4, type: GL.UNSIGNED_BYTE, transition: false, }, instancePickable: { accessor: 'getPickable', size: 1, transition: false, }, }); } updateState({ props, oldProps, changeFlags }: any) { super.updateState({ props, oldProps, changeFlags }); if (changeFlags.extensionsChanged) { const { gl } = this.context; if (this.state.model) { this.state.model.delete(); } this.setState({ model: this._getModel(gl) }); this.getAttributeManager().invalidateAll(); } } draw({ uniforms }: any) { const { gl } = this.context; const { outlineColor, thicknessUnit } = this.props; gl.lineWidth(1); this.state.model .setUniforms({ ...uniforms, outlineColor: outlineColor!.map((x: number) => x / 255), thicknessUnit: thicknessUnit! * 2.0, gap: 0.5, }) .draw(); } _getModel(gl: WebGLRenderingContext) { let positions: number[] = []; let pixelOffsets: number[] = []; const { drawOutline, outlineThickness } = this.props; if (drawOutline) { // source_target_mix, perpendicular_offset_in_thickness_units, direction_of_travel_offset_in_thickness_units // prettier-ignore positions = positions.concat([ // Outline 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, -3, 1, 1, -3, 1, 0, 0, 1, 2, -3, 1, 0, -3, ], ); const tout = outlineThickness!; const tin = INNER_SIDE_OUTLINE_THICKNESS; // the outline shouldn't cover the opposite arrow // perpendicular_offset_in_pixels, direction_of_travel_offset_in_pixels, fill_outline_color_mix // prettier-ignore pixelOffsets = pixelOffsets.concat([ // Outline -tin, -tout, 1, tout, -tout, 1, -tin, tout, 1, tout, -tout, 1, -tin, 0, 1, tout, 0, 1, -tin, 3 * tout, 1, 2 * tout, -tout, 1, -tin, -tout, 1, ]); } // prettier-ignore positions = positions.concat([ // Fill 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, -3, 1, 1, -3, 1, 0, 0, 1, 2, -3, 1, 0, -3, ]); // prettier-ignore pixelOffsets = pixelOffsets.concat([ // Fill 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); return new Model(gl, { id: this.props.id, ...this.getShaders(), geometry: new Geometry({ drawType: GL.TRIANGLES, attributes: { positions: new Float32Array(positions), normals: new Float32Array(pixelOffsets), }, }), isInstanced: true, shaderCache: this.context.shaderCache, }); } } export default FlowLinesLayer; ================================================ FILE: packages/core/src/FlowLinesLayer/FlowLinesLayerFragment.glsl.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file was modified by Teralytics. Originally licenced under: * * Copyright (c) 2015 Uber Technologies, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ export default `\ #define SHADER_NAME flow-line-layer-fragment-shader precision highp float; varying vec4 vColor; varying vec2 uv; void main(void) { if (vColor.a == 0.0) { discard; } geometry.uv = uv; gl_FragColor = vColor; DECKGL_FILTER_COLOR(gl_FragColor, geometry); } `; ================================================ FILE: packages/core/src/FlowLinesLayer/FlowLinesLayerVertex.glsl.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file was modified by Teralytics. Originally licenced under: * * Copyright (c) 2016 Uber Technologies, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ export default `\ #define SHADER_NAME flow-line-layer-vertex-shader attribute vec3 positions; attribute vec3 normals; attribute vec4 instanceColors; attribute float instanceThickness; // 0..0.5 attribute vec3 instanceSourcePositions; attribute vec3 instanceTargetPositions; attribute vec3 instanceSourcePositions64Low; attribute vec3 instanceTargetPositions64Low; attribute vec3 instancePickingColors; attribute vec2 instanceEndpointOffsets; attribute float instancePickable; uniform vec4 outlineColor; uniform float thicknessUnit; uniform float gap; uniform float opacity; varying vec4 vColor; varying vec2 uv; void main(void) { geometry.worldPosition = instanceSourcePositions; geometry.worldPositionAlt = instanceTargetPositions; // Position vec4 source_commonspace; vec4 target_commonspace; vec4 source = project_position_to_clipspace(instanceSourcePositions, instanceSourcePositions64Low, vec3(0.), source_commonspace); vec4 target = project_position_to_clipspace(instanceTargetPositions, instanceTargetPositions64Low, vec3(0.), target_commonspace); // linear interpolation of source & target to pick right coord float sourceOrTarget = positions.x; geometry.position = mix(source_commonspace, target_commonspace, sourceOrTarget); uv = positions.xy; geometry.uv = uv; if (instancePickable > 0.5) { geometry.pickingColor = instancePickingColors; } // set the clamp limits in pixel size float lengthCommon = length(target_commonspace - source_commonspace); vec2 offsetDistances = project_pixel_size(positions.yz) * thicknessUnit; vec2 limitedOffsetDistances = clamp( project_pixel_size(positions.yz) * thicknessUnit, -lengthCommon*.8, lengthCommon*.8 ); float startOffsetCommon = project_pixel_size(instanceEndpointOffsets[0]); float endOffsetCommon = project_pixel_size(instanceEndpointOffsets[1]); float endpointOffset = mix( clamp(startOffsetCommon, 0.0, lengthCommon*.2), -clamp(endOffsetCommon, 0.0, lengthCommon*.2), positions.x ); vec2 flowlineDir = normalize(target_commonspace.xy - source_commonspace.xy); vec2 perpendicularDir = vec2(-flowlineDir.y, flowlineDir.x); vec2 normalsCommon = project_pixel_size(normals.xy); float gapCommon = project_pixel_size(gap); vec3 offsetCommon = vec3( flowlineDir * (instanceThickness * limitedOffsetDistances[1] + normalsCommon.y + endpointOffset * 1.05) - perpendicularDir * (instanceThickness * limitedOffsetDistances[0] + gapCommon + normalsCommon.x), 0.0 ); DECKGL_FILTER_SIZE(offsetCommon, geometry); vec4 position_commonspace = mix(source_commonspace, target_commonspace, sourceOrTarget); vec4 offset_commonspace = vec4(offsetCommon, 0.0); gl_Position = project_common_position_to_clipspace(position_commonspace + offset_commonspace); DECKGL_FILTER_GL_POSITION(gl_Position, geometry); vec4 fillColor = vec4(instanceColors.rgb, instanceColors.a * opacity) / 255.; if (instancePickable <= 0.5) { vColor = mix(fillColor, vec4(outlineColor.xyz, instanceThickness), normals.z); } else { vColor = mix(fillColor, vec4(outlineColor.xyz, outlineColor.w * fillColor.w), normals.z); } DECKGL_FILTER_COLOR(vColor, geometry); } `; ================================================ FILE: packages/core/src/FlowLinesLayer/index.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import FlowLinesLayer from './FlowLinesLayer'; export default FlowLinesLayer; ================================================ FILE: packages/core/src/FlowMapLayer.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { CompositeLayer } from '@deck.gl/core'; import { GeoJsonLayer } from '@deck.gl/layers'; import AnimatedFlowLinesLayer from './AnimatedFlowLinesLayer/AnimatedFlowLinesLayer'; import { Colors, DiffColors } from './colors'; import FlowCirclesLayer from './FlowCirclesLayer/FlowCirclesLayer'; import FlowLinesLayer from './FlowLinesLayer/FlowLinesLayer'; import Selectors from './Selectors'; import { LayerProps } from './LayerProps'; import { DeckGLLayer, Flow, FlowAccessor, FlowLayerPickingInfo, isFeatureCollection, Location, LocationAccessor, LocationCircle, LocationCircleAccessor, LocationCircleType, Locations, PickingHandler, PickingType, } from './types'; export interface BasicProps extends LayerProps { locations: Locations; flows: Flow[]; diffMode?: boolean; animate?: boolean; animationCurrentTime?: number; animationTailLength?: number; colors?: Colors | DiffColors; getLocationId?: LocationAccessor; getLocationCentroid?: LocationAccessor<[number, number]>; getLocationTotalIn?: LocationAccessor; getLocationTotalOut?: LocationAccessor; getLocationTotalWithin?: LocationAccessor; getFlowOriginId?: FlowAccessor; getFlowDestId?: FlowAccessor; getFlowMagnitude?: FlowAccessor; getAnimatedFlowLineStaggering?: FlowAccessor; getFlowColor?: FlowAccessor; maxFlowThickness?: number; flowMagnitudeExtent?: [number, number]; locationTotalsExtent?: [number, number]; maxLocationCircleSize?: number; minPickableFlowThickness?: number; showTotals?: boolean; showLocationAreas?: boolean; showOnlyTopFlows?: number; selectedLocationIds?: string[]; highlightedLocationId?: string; highlightedLocationAreaId?: string; highlightedFlow?: Flow; outlineThickness?: number; updateTriggers?: { getFlowLinesSourcePosition?: any; getFlowLinesTargetPosition?: any; getFlowLinesThickness?: any; getFlowLinesEndpointOffsets?: any; getFlowLinesColor?: any; getCirclesRadius?: any; getCirclesColor?: any; getLocationAreasFillColor?: any; getLocationAreasLineColor?: any; }; } export interface Props extends BasicProps { id: string; onClick?: PickingHandler; onHover?: PickingHandler; } enum LayerKind { LOCATIONS = 'LOCATIONS', LOCATION_AREAS = 'LOCATION_AREAS', LOCATION_AREAS_OUTLINES = 'LOCATION_AREAS_OUTLINES', LOCATION_AREAS_SELECTED_AND_HIGHLIGHTED = 'LOCATION_AREAS_SELECTED_AND_HIGHLIGHTED', FLOWS = 'FLOWS', LOCATIONS_HIGHLIGHTED = 'LOCATIONS_HIGHLIGHTED', FLOWS_HIGHLIGHTED = 'FLOWS_HIGHLIGHTED', } const LAYER_ID_SEPARATOR = ':::'; function getLayerId(baseLayerId: string, layerKind: LayerKind) { return `${baseLayerId}${LAYER_ID_SEPARATOR}${layerKind.valueOf()}`; } function getLayerKind(id: string): LayerKind { const kind = id.substr(id.lastIndexOf(LAYER_ID_SEPARATOR) + LAYER_ID_SEPARATOR.length); return LayerKind[kind as keyof typeof LayerKind]; } function getPickType({ id }: DeckGLLayer): PickingType | undefined { switch (getLayerKind(id)) { case LayerKind.FLOWS: case LayerKind.FLOWS_HIGHLIGHTED: return PickingType.FLOW; case LayerKind.LOCATIONS: case LayerKind.LOCATIONS_HIGHLIGHTED: return PickingType.LOCATION; case LayerKind.LOCATION_AREAS: return PickingType.LOCATION_AREA; default: return undefined; } } export default class FlowMapLayer extends CompositeLayer { static layerName: string = 'FlowMapLayer'; static defaultProps = { getLocationId: { type: 'accessor', value: (l: Location) => l.id || l.properties.id }, getLocationCentroid: { type: 'accessor', value: (l: Location) => l.properties.centroid }, getFlowOriginId: { type: 'accessor', value: (f: Flow) => f.origin }, getFlowDestId: { type: 'accessor', value: (f: Flow) => f.dest }, getFlowMagnitude: { type: 'accessor', value: (f: Flow) => f.count }, showTotals: true, maxLocationCircleSize: 15, outlineThickness: 1, showLocationAreas: true, animationTailLength: 0.7, }; props!: Props; constructor(props: Props) { super(props); } initializeState() { const { getLocationTotalIn, getLocationTotalOut, getLocationTotalWithin, getLocationId, getLocationCentroid, getFlowOriginId, getFlowDestId, getFlowMagnitude, getFlowColor, } = this.props; const selectors = new Selectors({ getLocationId: getLocationId!, getLocationCentroid: getLocationCentroid!, getLocationTotalIn, getLocationTotalOut, getLocationTotalWithin, getFlowOriginId: getFlowOriginId!, getFlowDestId: getFlowDestId!, getFlowMagnitude: getFlowMagnitude!, getFlowColor, }); this.setState({ selectors }); } updateState(params: any) { super.updateState(params); const { props, changeFlags } = params; if (changeFlags.propsChanged) { const { getLocationTotalIn, getLocationTotalOut, getLocationTotalWithin, getLocationId, getLocationCentroid, getFlowOriginId, getFlowDestId, getFlowMagnitude, getFlowColor, } = props; this.state.selectors.setInputAccessors({ getLocationId, getLocationCentroid, getLocationTotalIn, getLocationTotalOut, getLocationTotalWithin, getFlowOriginId, getFlowDestId, getFlowMagnitude, getFlowColor, }); } } getPickingInfo(params: any): FlowLayerPickingInfo { const type = getPickType(params.sourceLayer); if (!type) { return params.info; } const info = { ...params.info, type, }; const { selectors } = this.state; if (type === PickingType.FLOW) { const getLocationById = selectors.getLocationByIdGetter(this.props); const { getFlowOriginId, getFlowDestId } = selectors.getInputAccessors(); const flow = info.object as Flow; return { ...info, ...(flow && { origin: getLocationById(getFlowOriginId(flow)), dest: getLocationById(getFlowDestId(flow)), }), }; } if (type === PickingType.LOCATION || type === PickingType.LOCATION_AREA) { const location: Location = type === PickingType.LOCATION ? info.object && info.object.location : info.object; const getLocationTotalIn = selectors.getLocationTotalInGetter(this.props); const getLocationTotalOut = selectors.getLocationTotalOutGetter(this.props); const getLocationTotalWithin = selectors.getLocationTotalWithinGetter(this.props); const getLocationCircleRadius = selectors.getLocationCircleRadiusGetter(this.props); return { ...info, ...(location && { object: location, totalIn: getLocationTotalIn(location), totalOut: getLocationTotalOut(location), totalWithin: getLocationTotalWithin(location), circleRadius: getLocationCircleRadius({ location, type: LocationCircleType.OUTER }), }), }; } return info; } renderLayers() { const { showLocationAreas, locations, highlightedLocationId } = this.props; const { selectors } = this.state; const topFlows = selectors.getTopFlows(this.props); const highlightedFlows = selectors.getHighlightedFlows(this.props); const isLocationHighlighted = highlightedLocationId != null; const locationCircles = selectors.getLocationCircles(this.props); const layers: DeckGLLayer[] = []; if (showLocationAreas && isFeatureCollection(locations)) { layers.push(this.getLocationAreasLayer(getLayerId(this.props.id, LayerKind.LOCATION_AREAS), false)); } layers.push( this.getFlowLinesLayer(getLayerId(this.props.id, LayerKind.FLOWS), topFlows, false, isLocationHighlighted), ); if (showLocationAreas && isFeatureCollection(locations)) { layers.push( this.getHighlightedLocationAreasLayer( getLayerId(this.props.id, LayerKind.LOCATION_AREAS_SELECTED_AND_HIGHLIGHTED), ), ); } if (highlightedFlows) { layers.push( this.getFlowLinesLayer(getLayerId(this.props.id, LayerKind.FLOWS_HIGHLIGHTED), highlightedFlows, true, false), ); } layers.push(this.getLocationCirclesLayer(getLayerId(this.props.id, LayerKind.LOCATIONS), locationCircles, false)); if (isLocationHighlighted) { const highlightedLocationCircles = selectors.getHighlightedLocationCircles(this.props); layers.push( this.getLocationCirclesLayer( getLayerId(this.props.id, LayerKind.LOCATIONS_HIGHLIGHTED), highlightedLocationCircles, true, ), ); } if (showLocationAreas && isFeatureCollection(locations)) { layers.push(this.getLocationAreasLayer(getLayerId(this.props.id, LayerKind.LOCATION_AREAS_OUTLINES), true)); } return layers; } private getLocationAreasLayer(id: string, outline: boolean): DeckGLLayer { const { locations, selectedLocationIds, highlightedLocationId, highlightedFlow, updateTriggers } = this.props; const { selectors } = this.state; const colors = selectors.getColors(this.props); return new GeoJsonLayer( this.getSubLayerProps({ id, getFillColor: selectors.getLocationAreaFillColorGetter(this.props), getLineColor: colors.locationAreas.outline, lineJointRounded: true, data: locations, stroked: outline, filled: !outline, ...(outline && { pickable: false }), lineWidthMinPixels: 1, pointRadiusMinPixels: 1, updateTriggers: { getFillColor: { colors, ...(outline && { selectedLocationIds, highlightedLocationId, highlightedFlow }), ...updateTriggers?.getLocationAreasFillColor, }, getLineColor: { colors, ...updateTriggers?.getLocationAreasLineColor, }, }, }), ); } private getHighlightedLocationAreasLayer(id: string): DeckGLLayer { const { selectors } = this.state; const { highlightedLocationId, highlightedLocationAreaId, updateTriggers } = this.props; const colors = selectors.getColors(this.props); const getLocationById = selectors.getLocationByIdGetter(this.props); return new GeoJsonLayer( this.getSubLayerProps({ id, getFillColor: () => colors.locationAreas.highlighted, getLineColor: colors.locationAreas.outline, lineJointRounded: true, data: highlightedLocationId ? getLocationById(highlightedLocationId) : highlightedLocationAreaId ? getLocationById(highlightedLocationAreaId) : undefined, stroked: false, filled: true, pickable: false, lineWidthMinPixels: 5, updateTriggers: { getFillColor: { colors, ...updateTriggers?.getLocationAreasFillColor, }, getLineColor: { colors, ...updateTriggers?.getLocationAreasLineColor, }, }, }), ); } private getFlowLinesLayer( id: string, flows: Flow[], highlighted: boolean, dimmed: boolean, ): FlowLinesLayer | AnimatedFlowLinesLayer { const { getFlowOriginId, getFlowDestId, getFlowMagnitude, getLocationCentroid, getAnimatedFlowLineStaggering, showTotals, maxLocationCircleSize, outlineThickness, minPickableFlowThickness, maxFlowThickness, flowMagnitudeExtent, locationTotalsExtent, updateTriggers, } = this.props; const { selectors } = this.state; const endpointOffsets: [number, number] = [(maxLocationCircleSize || 0) + 1, (maxLocationCircleSize || 0) + 1]; const getLocationRadius = selectors.getLocationCircleRadiusGetter(this.props); const getLocationById = selectors.getLocationByIdGetter(this.props); const flowThicknessScale = selectors.getFlowThicknessScale(this.props); const getSourcePosition: FlowAccessor<[number, number]> = (flow, info) => getLocationCentroid!(getLocationById(getFlowOriginId!(flow, info))); const getTargetPosition: FlowAccessor<[number, number]> = (flow, info) => getLocationCentroid!(getLocationById(getFlowDestId!(flow, info))); const getThickness: FlowAccessor = (flow, info) => flowThicknessScale(getFlowMagnitude!(flow, info)); const getEndpointOffsets: FlowAccessor<[number, number]> = (flow, info) => { if (!showTotals) { return endpointOffsets; } return [ getLocationRadius({ location: getLocationById(getFlowOriginId!(flow, info)), type: LocationCircleType.OUTLINE, }), getLocationRadius({ location: getLocationById(getFlowDestId!(flow, info)), type: LocationCircleType.OUTLINE, }), ]; }; const flowColorScale = selectors.getFlowColorScale(this.props); const colors = selectors.getColors(this.props); const getColor = selectors.getFlowLinesColorGetter(colors, flowColorScale, highlighted, dimmed); const { animate } = this.props; const thicknessUnit = maxFlowThickness != null ? maxFlowThickness : FlowLinesLayer.defaultProps.thicknessUnit; const baseProps = { id, getSourcePosition, getTargetPosition, getThickness, getEndpointOffsets, getColor, data: flows, ...(highlighted && { pickable: false }), drawOutline: !dimmed, updateTriggers: { getSourcePosition: updateTriggers?.getFlowLinesSourcePosition, getTargetPosition: updateTriggers?.getFlowLinesTargetPosition, getThickness: { flowMagnitudeExtent, maxFlowThickness, ...updateTriggers?.getFlowLinesThickness, }, getColor: { colors, dimmed, ...updateTriggers?.getFlowLinesColor, }, getEndpointOffsets: { showTotals, locationTotalsExtent, ...updateTriggers?.getFlowLinesEndpointOffsets, }, }, thicknessUnit, outlineColor: colors.outlineColor, ...(outlineThickness && { outlineThickness }), ...(minPickableFlowThickness != null && { getPickable: (f: Flow) => (thicknessUnit * getThickness(f) >= minPickableFlowThickness ? 1.0 : 0.0), }), parameters: { ...this.props.parameters, depthTest: false, }, }; if (animate) { return new AnimatedFlowLinesLayer( this.getSubLayerProps({ ...baseProps, currentTime: this.props.animationCurrentTime, ...(this.props.animationTailLength != null && { animationTailLength: this.props.animationTailLength, }), ...(getAnimatedFlowLineStaggering && { getStaggering: getAnimatedFlowLineStaggering, }), }), ); } else { return new FlowLinesLayer(this.getSubLayerProps(baseProps)); } } private getLocationCirclesLayer(id: string, circles: LocationCircle[], highlighted: boolean): FlowCirclesLayer { const { highlightedLocationId, selectedLocationIds, getLocationCentroid, flows, showTotals, updateTriggers, maxLocationCircleSize, locationTotalsExtent, } = this.props; const { selectors } = this.state; const getRadius = showTotals ? selectors.getLocationCircleRadiusGetter(this.props) : () => maxLocationCircleSize; const colors = selectors.getColors(this.props); const getColor = selectors.getLocationCircleColorGetter(this.props); const getPosition: LocationCircleAccessor<[number, number]> = locCircle => getLocationCentroid!(locCircle.location); return new FlowCirclesLayer( this.getSubLayerProps({ id, getColor, getPosition, getRadius, data: circles, updateTriggers: { getRadius: { showTotals, selectedLocationIds, maxLocationCircleSize, locationTotalsExtent, flows, ...updateTriggers?.getCirclesRadius, }, getColor: { colors, highlightedLocationId, selectedLocationIds, flows, ...updateTriggers?.getCirclesColor, }, }, parameters: { ...this.props.parameters, depthTest: false, }, }), ); } } ================================================ FILE: packages/core/src/LayerProps.ts ================================================ // This is for deck.gl Layer export interface LayerProps { id?: string; data?: any; autoHighlight?: boolean; opacity?: number; pickable?: boolean; visible?: boolean; coordinateOrigin?: any; coordinateSystem?: any; extensions?: any; getPolygonOffset?: any; highlightColor?: any; highlightedObjectIndex?: any; lightSettings?: any; modelMatrix?: any; parameters?: any; positionFormat?: any; colorFormat?: any; transitions?: any; wrapLongitude?: any; dataComparator?: any; dataTransform?: any; numInstances?: any; updateTriggers?: any; _dataDiff?: any; onDragStart?: any; onDrag?: any; onDragEnd?: any; onClick?: any; onHover?: any; onDataLoad?: any; } ================================================ FILE: packages/core/src/Selectors.ts ================================================ // tslint:disable:member-ordering import { ascending, extent, max } from 'd3-array'; import { nest } from 'd3-collection'; import { scaleLinear, scalePow } from 'd3-scale'; import { createSelector } from 'reselect'; import { colorAsRgba, ColorScale, ColorsRGBA, createFlowColorScale, DiffColorsRGBA, getColorsRGBA, getDiffColorsRGBA, getDimmedCircleColor, getDimmedCircleOutlineColor, getDimmedColor, isDiffColorsRGBA, RGBA, } from './colors'; import FlowMapLayer, { Props } from './FlowMapLayer'; import { Flow, FlowAccessors, isFeatureCollection, Location, LocationAccessors, LocationCircle, LocationCircleType, NumberScale, } from './types'; export type InputAccessors = LocationAccessors & FlowAccessors; export type PropsSelector = (props: Props) => T; export interface LocationTotals { incoming: { [key: string]: number; }; outgoing: { [key: string]: number; }; within: { [key: string]: number; }; } const CIRCLE_OUTLINE_THICKNESS = 1; export type LocationByIdGetter = (id: string) => Location | undefined; const getDiffMode = (props: Props) => props.diffMode; const getColorsProp = (props: Props) => props.colors; const getAnimate = (props: Props) => props.animate; const getLocationFeatures = (props: Props) => isFeatureCollection(props.locations) ? props.locations.features : props.locations; const getFlows = (props: Props) => props.flows; const getHighlightedFlow = (props: Props) => props.highlightedFlow; const getHighlightedLocationId = (props: Props) => props.highlightedLocationId; const getSelectedLocationIds = (props: Props) => props.selectedLocationIds; const getShowOnlyTopFlows = (props: Props) => props.showOnlyTopFlows; const getMaxLocationCircleSize = (props: Props) => props.maxLocationCircleSize != null ? props.maxLocationCircleSize : FlowMapLayer.defaultProps.maxLocationCircleSize; class Selectors { constructor(private inputAccessors: InputAccessors) {} getColors: PropsSelector = createSelector( [getColorsProp, getDiffMode], (colors, diffMode) => { if (diffMode) { return getDiffColorsRGBA(colors); } return getColorsRGBA(colors); }, ); getLocationByIdGetter: PropsSelector = createSelector([getLocationFeatures], locations => { const locationsById = nest() .key(this.inputAccessors.getLocationId) .rollup(([d]) => d) .object(locations); return (id: string) => { const location = locationsById[id]; if (!location) { console.warn(`No location found for id '${id}'`); } return location; }; }); private getFilteredFlows: PropsSelector = createSelector( [getFlows, getSelectedLocationIds], (flows, selectedLocationIds) => { const { getFlowOriginId, getFlowDestId } = this.inputAccessors; if (selectedLocationIds) { return flows.filter( flow => selectedLocationIds.indexOf(getFlowOriginId(flow)) >= 0 || selectedLocationIds.indexOf(getFlowDestId(flow)) >= 0, ); } return flows; }, ); private getNonSelfFlows: PropsSelector = createSelector([this.getFilteredFlows], flows => { const { getFlowOriginId, getFlowDestId } = this.inputAccessors; return flows.filter(flow => getFlowOriginId(flow) !== getFlowDestId(flow)); }); getSortedNonSelfFlows: PropsSelector = createSelector([this.getNonSelfFlows], flows => { const comparator = (f1: Flow, f2: Flow) => Math.abs(this.inputAccessors.getFlowMagnitude(f1)) - Math.abs(this.inputAccessors.getFlowMagnitude(f2)); return flows.slice().sort(comparator); }); getTopFlows: PropsSelector = createSelector( [this.getSortedNonSelfFlows, getShowOnlyTopFlows], (flows, showOnlyTopFlows) => { if (showOnlyTopFlows != null && showOnlyTopFlows > 0 && flows.length > showOnlyTopFlows) { return flows.slice(flows.length - showOnlyTopFlows, flows.length); } return flows; }, ); getHighlightedFlows: PropsSelector = createSelector( [this.getSortedNonSelfFlows, getHighlightedFlow, getHighlightedLocationId], (flows, highlightedFlow, highlightedLocationId) => { const { getFlowOriginId, getFlowDestId } = this.inputAccessors; if (highlightedFlow) { return [highlightedFlow]; } if (highlightedLocationId) { return flows.filter( flow => getFlowOriginId(flow) === highlightedLocationId || getFlowDestId(flow) === highlightedLocationId, ); } return undefined; }, ); private getFlowMagnitudeExtent: PropsSelector<[number, number] | [undefined, undefined]> = createSelector( [this.getNonSelfFlows, props => props.flowMagnitudeExtent], (flows, flowMagnitudeExtent) => { if (flowMagnitudeExtent != null) return flowMagnitudeExtent; return extent(flows, f => this.inputAccessors.getFlowMagnitude(f)); }, ); getFlowThicknessScale: PropsSelector<(magnitude: number) => number | undefined> = createSelector( [this.getFlowMagnitudeExtent], ([minMagnitude, maxMagnitude]) => { const scale = scaleLinear() .range([0.05, 0.5]) .domain([0, Math.max(Math.abs(minMagnitude || 0), Math.abs(maxMagnitude || 0))]); return (magnitude: number) => scale(Math.abs(magnitude)); }, ); getFlowColorScale: PropsSelector = createSelector( [this.getColors, this.getFlowMagnitudeExtent, getAnimate], (colors, [minMagnitude, maxMagnitude], animate) => { if (isDiffColorsRGBA(colors)) { const posScale = createFlowColorScale([0, maxMagnitude || 0], colors.positive.flows.scheme, animate); const negScale = createFlowColorScale([0, minMagnitude || 0], colors.negative.flows.scheme, animate); return (magnitude: number) => (magnitude >= 0 ? posScale(magnitude) : negScale(magnitude)); } const scale = createFlowColorScale([0, maxMagnitude || 0], colors.flows.scheme, animate); return (magnitude: number) => scale(magnitude); }, ); getFlowLinesColorGetter( colors: ColorsRGBA | DiffColorsRGBA, flowColorScale: ColorScale, highlighted: boolean, dimmed: boolean, ) { const { getFlowMagnitude, getFlowColor } = this.inputAccessors; return (flow: Flow) => { if (getFlowColor) { const color = getFlowColor(flow); if (color) { return colorAsRgba(color); } } if (highlighted) { if (isDiffColorsRGBA(colors)) { const positiveColor = colors.positive.flows.highlighted; const negativeColor = colors.negative.flows.highlighted; const magnitude = getFlowMagnitude(flow); return magnitude >= 0 ? positiveColor : negativeColor; } else { return colors.flows.highlighted; } } else { const magnitude = getFlowMagnitude(flow); const color = flowColorScale(magnitude); if (dimmed) { return getDimmedColor(color, colors.dimmedOpacity); } return color; } }; } private getLocationTotals: PropsSelector = createSelector( [getLocationFeatures, this.getFilteredFlows], (locations, flows) => { const { getFlowOriginId, getFlowDestId, getFlowMagnitude } = this.inputAccessors; return flows.reduce( (acc, curr) => { const originId = getFlowOriginId(curr); const destId = getFlowDestId(curr); const magnitude = getFlowMagnitude(curr); if (originId === destId) { acc.within[originId] = (acc.within[originId] || 0) + magnitude; } else { acc.outgoing[originId] = (acc.outgoing[originId] || 0) + magnitude; acc.incoming[destId] = (acc.incoming[destId] || 0) + magnitude; } return acc; }, { incoming: {}, outgoing: {}, within: {} }, ); }, ); getHighlightedLocationCircles: PropsSelector = createSelector( [this.getLocationByIdGetter, getHighlightedLocationId], (getLocationById, highlightedLocationId) => { if (highlightedLocationId) { const location = getLocationById(highlightedLocationId); if (!location) { return undefined; } return [ { location, type: LocationCircleType.OUTLINE }, { location, type: LocationCircleType.OUTER }, { location, type: LocationCircleType.INNER }, ]; } return undefined; }, ); getLocationTotalInGetter = (props: Props) => { const { getLocationTotalIn, getLocationId } = this.inputAccessors; if (getLocationTotalIn) { return getLocationTotalIn; } const { incoming } = this.getLocationTotals(props); return (location: Location) => incoming[getLocationId(location)] || 0; }; getLocationTotalOutGetter = (props: Props) => { const { getLocationTotalOut, getLocationId } = this.inputAccessors; if (getLocationTotalOut) { return getLocationTotalOut; } const { outgoing } = this.getLocationTotals(props); return (location: Location) => outgoing[getLocationId(location)] || 0; }; getLocationTotalWithinGetter = (props: Props) => { const { getLocationTotalWithin, getLocationId } = this.inputAccessors; if (getLocationTotalWithin) { return getLocationTotalWithin; } const { within } = this.getLocationTotals(props); return (location: Location) => within[getLocationId(location)] || 0; }; private getLocationMaxAbsTotalGetter: PropsSelector<(location: Location) => number> = createSelector( [ getLocationFeatures, this.getLocationTotalInGetter, this.getLocationTotalOutGetter, this.getLocationTotalWithinGetter, ], (locations, getLocationTotalIn, getLocationTotalOut, getLocationTotalWithin) => { return (location: Location) => Math.max( Math.abs(getLocationTotalIn(location) + getLocationTotalWithin(location)), Math.abs(getLocationTotalOut(location) + getLocationTotalWithin(location)), ); }, ); private getMaxLocationMaxAbsTotal: PropsSelector = createSelector( [getLocationFeatures, this.getLocationMaxAbsTotalGetter, props => props.locationTotalsExtent], (locations, getLocationMaxAbsTotal, locationTotalsExtent) => { if (locationTotalsExtent != null) { return max(locationTotalsExtent, Math.abs) || 0; } return max(locations, getLocationMaxAbsTotal) || 0; }, ); getLocationCircles: PropsSelector = createSelector( [getLocationFeatures, this.getLocationMaxAbsTotalGetter], (locations, getLocationMaxAbsTotalGetter) => { const circles = []; const sorted = locations .slice() .sort((a, b) => ascending(getLocationMaxAbsTotalGetter(a), getLocationMaxAbsTotalGetter(b))); for (const location of sorted) { circles.push({ location, type: LocationCircleType.OUTLINE, }); circles.push({ location, type: LocationCircleType.OUTER, }); circles.push({ location, type: LocationCircleType.INNER, }); } return circles; }, ); private getSizeScale: PropsSelector<(v: number) => number> = createSelector( [getMaxLocationCircleSize, this.getMaxLocationMaxAbsTotal], (maxLocationCircleSize, maxTotal) => { const scale = scalePow() .exponent(1 / 2) .domain([0, maxTotal]) .range([0, maxTotal > 0 ? maxLocationCircleSize : 1]); return (v: number) => scale(Math.abs(v)) || 0; }, ); getLocationCircleRadiusGetter: PropsSelector<(locCircle: LocationCircle) => number> = createSelector( [ this.getSizeScale, this.getLocationTotalInGetter, this.getLocationTotalOutGetter, this.getLocationTotalWithinGetter, ], (sizeScale, getLocationTotalIn, getLocationTotalOut, getLocationTotalWithin) => { return ({ location, type }: LocationCircle) => { const getSide = type === LocationCircleType.INNER ? Math.min : Math.max; const totalIn = getLocationTotalIn(location); const totalOut = getLocationTotalOut(location); const totalWithin = getLocationTotalWithin(location); const r = sizeScale(getSide(Math.abs(totalIn + totalWithin), Math.abs(totalOut + totalWithin))); if (type === LocationCircleType.OUTLINE) { return r + CIRCLE_OUTLINE_THICKNESS; } return r; }; }, ); getLocationCircleColorGetter: PropsSelector<(locCircle: LocationCircle) => RGBA> = createSelector( [ this.getColors, getHighlightedLocationId, this.getLocationTotalInGetter, this.getLocationTotalOutGetter, this.getLocationTotalWithinGetter, ], (colors, highlightedLocationId, getLocationTotalIn, getLocationTotalOut, getLocationTotalWithin) => { const { getLocationId } = this.inputAccessors; return ({ location, type }: LocationCircle) => { const isHighlighted = highlightedLocationId && highlightedLocationId === getLocationId(location); const isDimmed = highlightedLocationId && highlightedLocationId !== getLocationId(location); const totalWithin = getLocationTotalWithin(location); const totalIn = getLocationTotalIn(location) + totalWithin; const totalOut = getLocationTotalOut(location) + totalWithin; const isIncoming = type === LocationCircleType.OUTER && Math.abs(totalIn) > Math.abs(totalOut); const isPositive = (isIncoming === true && totalIn >= 0) || totalOut >= 0; const circleColors = (isDiffColorsRGBA(colors) ? (isPositive ? colors.positive : colors.negative) : colors) .locationCircles; if (isHighlighted && type === LocationCircleType.OUTLINE) { return circleColors.highlighted; } if (isDimmed) { if (type === LocationCircleType.OUTLINE) { return getDimmedCircleOutlineColor(colors.outlineColor, colors.dimmedOpacity); } return getDimmedCircleColor(circleColors.inner, colors.dimmedOpacity); } if (type === LocationCircleType.OUTLINE) { return isIncoming ? circleColors.incoming : circleColors.inner; } if (type === LocationCircleType.INNER) { return circleColors.inner; } if (isIncoming === true) { return circleColors.incoming; } return circleColors.outgoing; }; }, ); private isLocationConnectedGetter: PropsSelector<(id: string) => boolean> = createSelector( [this.getFilteredFlows, getHighlightedLocationId, getHighlightedFlow, getSelectedLocationIds], (flows, highlightedLocationId, highlightedFlow, selectedLocationIds) => { const { getFlowOriginId, getFlowDestId } = this.inputAccessors; if (highlightedLocationId) { const isRelated = (flow: Flow) => { const originId = getFlowOriginId(flow); const destId = getFlowDestId(flow); return ( originId === highlightedLocationId || (selectedLocationIds && selectedLocationIds.indexOf(originId) >= 0) || destId === highlightedLocationId || (selectedLocationIds && selectedLocationIds.indexOf(destId) >= 0) ); }; const locations = new Set(); for (const flow of flows) { if (isRelated(flow)) { locations.add(getFlowOriginId(flow)); locations.add(getFlowDestId(flow)); } } return (id: string) => locations.has(id); } return () => false; }, ); getLocationAreaFillColorGetter: PropsSelector<(location: Location) => RGBA> = createSelector( [this.getColors, getSelectedLocationIds, this.isLocationConnectedGetter], (colors, selectedLocationIds, isLocationConnected) => { return (location: Location) => { const locationId = this.inputAccessors.getLocationId(location); if (selectedLocationIds && selectedLocationIds.indexOf(locationId) >= 0) { return colors.locationAreas.selected; } if (isLocationConnected(locationId)) { return colors.locationAreas.connected; } return colors.locationAreas.normal; }; }, ); setInputAccessors(inputAccessors: InputAccessors) { this.inputAccessors = inputAccessors; } getInputAccessors() { return this.inputAccessors; } } export default Selectors; ================================================ FILE: packages/core/src/colors.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { color as d3color, hcl } from 'd3-color'; import { interpolateHcl } from 'd3-interpolate'; import { interpolateRgbBasis } from 'd3-interpolate'; // @ts-ignore import { scaleSequentialPow } from 'd3-scale'; const DEFAULT_FLOW_MIN_COLOR = 'rgba(240,240,240,0.5)'; const DEFAULT_FLOW_COLOR_SCHEME = [DEFAULT_FLOW_MIN_COLOR, '#137CBD']; const DEFAULT_FLOW_COLOR_SCHEME_POSITIVE = [DEFAULT_FLOW_MIN_COLOR, '#f6654e']; const DEFAULT_FLOW_COLOR_SCHEME_NEGATIVE = [DEFAULT_FLOW_MIN_COLOR, '#00a9cc']; const DEFAULT_OUTLINE_COLOR = '#fff'; const DEFAULT_LOCATION_AREA_COLOR = 'rgba(220,220,220,0.5)'; const DEFAULT_DIMMED_OPACITY = 0.4; const CIRCLE_DIMMED_OPACITY_MULTIPLIER = 0.5; const FALLBACK_COLOR_RGBA: RGBA = [255, 255, 255, 255]; export type ColorScale = (value: number) => RGBA; export type RGBA = [number, number, number, number]; export interface FlowColors { scheme?: string[]; highlighted?: string; } export interface LocationCircleColors { inner?: string; outgoing?: string; incoming?: string; highlighted?: string; } export interface LocationAreaColors { outline?: string; normal?: string; selected?: string; highlighted?: string; connected?: string; } export interface BaseColors { darkMode?: boolean; locationAreas?: LocationAreaColors; dimmedOpacity?: number; outlineColor?: string; } export interface Colors extends BaseColors { flows?: FlowColors; locationCircles?: LocationCircleColors; } export interface FlowAndCircleColors { flows?: FlowColors; locationCircles?: LocationCircleColors; } export interface DiffColors extends BaseColors { positive?: FlowAndCircleColors; negative?: FlowAndCircleColors; } // The xxxColorsRGBA objects are mirroring the input colors' objects, // but converted to RGBA and with all the omitted ones set to defaults // or derived. export interface FlowColorsRGBA { scheme: string[]; highlighted: RGBA; } export interface LocationCircleColorsRGBA { inner: RGBA; outgoing: RGBA; incoming: RGBA; highlighted: RGBA; } export interface LocationAreaColorsRGBA { outline: RGBA; normal: RGBA; selected: RGBA; highlighted: RGBA; connected: RGBA; } export interface BaseColorsRGBA { darkMode: boolean; locationAreas: LocationAreaColorsRGBA; dimmedOpacity: number; outlineColor: RGBA; } export interface ColorsRGBA extends BaseColorsRGBA { flows: FlowColorsRGBA; locationCircles: LocationCircleColorsRGBA; } export interface FlowAndCircleColorsRGBA { flows: FlowColorsRGBA; locationCircles: LocationCircleColorsRGBA; } export interface DiffColorsRGBA extends BaseColorsRGBA { positive: FlowAndCircleColorsRGBA; negative: FlowAndCircleColorsRGBA; } export function isDiffColors(colors: DiffColors | Colors): colors is DiffColors { return (colors as DiffColors).positive !== undefined; } export function isDiffColorsRGBA(colors: DiffColorsRGBA | ColorsRGBA): colors is DiffColorsRGBA { return (colors as DiffColorsRGBA).positive !== undefined; } function getBaseColorsRGBA(colors: Colors | DiffColors | undefined): BaseColorsRGBA { const darkMode = colors && colors.darkMode ? true : false; return { darkMode, locationAreas: getLocationAreaColorsRGBA(colors && colors.locationAreas, darkMode), outlineColor: colorAsRgba((colors && colors.outlineColor) || DEFAULT_OUTLINE_COLOR), dimmedOpacity: colors && colors.dimmedOpacity != null ? colors.dimmedOpacity : DEFAULT_DIMMED_OPACITY, }; } export function getColorsRGBA(colors: Colors | undefined): ColorsRGBA { const baseColorsRGBA = getBaseColorsRGBA(colors); return { ...baseColorsRGBA, ...getFlowAndCircleColors(colors, DEFAULT_FLOW_COLOR_SCHEME, baseColorsRGBA.darkMode), }; } export function getDiffColorsRGBA(colors: DiffColors | undefined): DiffColorsRGBA { const baseColorsRGBA = getBaseColorsRGBA(colors); return { ...baseColorsRGBA, positive: getFlowAndCircleColors( colors && colors.positive, DEFAULT_FLOW_COLOR_SCHEME_POSITIVE, baseColorsRGBA.darkMode, ), negative: getFlowAndCircleColors( colors && colors.negative, DEFAULT_FLOW_COLOR_SCHEME_NEGATIVE, baseColorsRGBA.darkMode, ), }; } function getLocationAreaColorsRGBA(colors: LocationAreaColors | undefined, darkMode: boolean): LocationAreaColorsRGBA { const normalColor = (colors && colors.normal) || DEFAULT_LOCATION_AREA_COLOR; const normalColorHcl = hcl(normalColor); const locationAreasNormal = colorAsRgba(normalColor); return { normal: locationAreasNormal, connected: colorAsRgbaOr(colors && colors.connected, locationAreasNormal), highlighted: colorAsRgbaOr( colors && colors.highlighted, opacifyHex(normalColorHcl[darkMode ? 'brighter' : 'darker'](1).toString(), 0.5), ), selected: colorAsRgbaOr( colors && colors.selected, opacifyHex(normalColorHcl[darkMode ? 'brighter' : 'darker'](2).toString(), 0.8), ), outline: colorAsRgbaOr( colors && colors.outline, colorAsRgba(normalColorHcl[darkMode ? 'brighter' : 'darker'](4).toString()), ), }; } function getFlowAndCircleColors( inputColors: FlowAndCircleColors | undefined, defaultFlowColorScheme: string[], darkMode: boolean, ): FlowAndCircleColorsRGBA { const flowColorScheme = (inputColors && inputColors.flows && inputColors.flows.scheme) || defaultFlowColorScheme; const maxFlowColorHcl = hcl(flowColorScheme[flowColorScheme.length - 1]); const flowColorHighlighted = colorAsRgbaOr( inputColors && inputColors.flows && inputColors.flows.highlighted, colorAsRgba(maxFlowColorHcl[darkMode ? 'brighter' : 'darker'](0.7).toString()), ); return { flows: { scheme: flowColorScheme, highlighted: flowColorHighlighted, }, locationCircles: { inner: colorAsRgbaOr( inputColors && inputColors.locationCircles && inputColors.locationCircles.inner, maxFlowColorHcl.toString(), ), outgoing: colorAsRgbaOr( inputColors && inputColors.locationCircles && inputColors.locationCircles.outgoing, darkMode ? '#000' : '#fff', ), incoming: colorAsRgbaOr( inputColors && inputColors.locationCircles && inputColors.locationCircles.incoming, maxFlowColorHcl[darkMode ? 'brighter' : 'darker'](1.25).toString(), ), highlighted: colorAsRgbaOr( inputColors && inputColors.locationCircles && inputColors.locationCircles.highlighted, flowColorHighlighted, ), }, }; } export function colorAsRgba(color: string): RGBA { const col = d3color(color); if (!col) { console.warn('Invalid color: ', color); return FALLBACK_COLOR_RGBA; } const rgbColor = col.rgb(); return [Math.floor(rgbColor.r), Math.floor(rgbColor.g), Math.floor(rgbColor.b), opacityFloatToInteger(col.opacity)]; } function colorAsRgbaOr(color: string | undefined, defaultColor: RGBA | string): RGBA { if (color) { return colorAsRgba(color); } if (typeof defaultColor === 'string') { return colorAsRgba(defaultColor); } return defaultColor; } export function rgbaAsString(color: RGBA): string { return `rgba(${color.join(',')})`; } export function opacifyHex(hexCode: string, opacity: number): string { const c = d3color(hexCode); if (!c) { console.warn('Invalid color: ', hexCode); return `rgba(255, 255, 255, ${opacity})`; } const col = c.rgb(); return `rgba(${col.r}, ${col.g}, ${col.b}, ${opacity})`; } export function opacityFloatToInteger(opacity: number): number { return Math.round(opacity * 255); } export function getDimmedCircleOutlineColor(outlineColor: RGBA, opacity: number): RGBA { const [r, g, b, a] = outlineColor; return [r, g, b, a * opacity * CIRCLE_DIMMED_OPACITY_MULTIPLIER] as RGBA; } export function getDimmedCircleColor(color: RGBA, opacity: number): RGBA { return getDimmedColor(color, opacity * CIRCLE_DIMMED_OPACITY_MULTIPLIER); } export function getDimmedColor(color: RGBA, opacity?: number): RGBA { const col = hcl(rgbaAsString(color)); if (!col) { console.warn('Invalid color: ', color); return FALLBACK_COLOR_RGBA; } col.c *= 0.1; // desaturate color const rgbColor = col.rgb(); return [ Math.floor(rgbColor.r), Math.floor(rgbColor.g), Math.floor(rgbColor.b), opacityFloatToInteger(opacity !== undefined ? opacity : DEFAULT_DIMMED_OPACITY), ]; } export function createFlowColorScale( domain: [number, number], scheme: string[], animate: boolean | undefined, ): ColorScale { const scale = scaleSequentialPow(interpolateRgbBasis(scheme)) // @ts-ignore .exponent(animate ? 1 / 2 : 1 / 3) .domain(domain); return (value: number) => colorAsRgba(scale(value)); } ================================================ FILE: packages/core/src/index.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import FlowMapLayer from './FlowMapLayer'; export * from './FlowMapLayer'; export * from './types'; // required by LocationTotalsLegend export { Colors, DiffColors, ColorsRGBA, getColorsRGBA, getDiffColorsRGBA, DiffColorsRGBA, isDiffColorsRGBA, rgbaAsString, RGBA, } from './colors'; export default FlowMapLayer; ================================================ FILE: packages/core/src/types.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Feature, FeatureCollection, GeometryObject } from 'geojson'; export type Flow = any; export type LocationProperties = any; export type Location = Feature | any; export type Locations = FeatureCollection | Location[]; export function isFeatureCollection( locations: Locations, ): locations is FeatureCollection { return (locations as FeatureCollection).type === 'FeatureCollection'; } export interface ViewState { latitude: number; longitude: number; zoom: number; bearing?: number; pitch?: number; altitude?: number; } export const enum LocationCircleType { INNER = 'inner', OUTER = 'outer', OUTLINE = 'outline', } export interface LocationCircle { location: Location; type: LocationCircleType; } export interface FlowAccessors { getFlowOriginId: FlowAccessor; getFlowDestId: FlowAccessor; getFlowMagnitude: FlowAccessor; getFlowColor?: FlowAccessor; getAnimatedFlowLineStaggering?: FlowAccessor; } export interface LocationAccessors { getLocationId: LocationAccessor; getLocationCentroid: LocationAccessor<[number, number]>; getLocationTotalIn?: LocationAccessor; getLocationTotalOut?: LocationAccessor; getLocationTotalWithin?: LocationAccessor; } export type Data = Flow | Location | LocationCircle; export enum PickingType { LOCATION = 'location', FLOW = 'flow', LOCATION_AREA = 'location-area', } export type DeckGLLayer = any; export interface PickingInfo { layer: DeckGLLayer; index: number; object: T; x: number; y: number; lngLat: [number, number]; } export type PickingHandler = (info: T, event: { srcEvent: MouseEvent }) => void; export interface LocationPickingInfo extends PickingInfo { type: PickingType.LOCATION; object: Location; totalIn: number; totalOut: number; totalWithin: number; circleRadius: number; } export interface LocationAreaPickingInfo extends PickingInfo { type: PickingType.LOCATION_AREA; object: Location; } export interface FlowPickingInfo extends PickingInfo { type: PickingType.FLOW; object: Flow; origin: Location; dest: Location; } export type FlowLayerPickingInfo = LocationPickingInfo | LocationAreaPickingInfo | FlowPickingInfo; // https://deck.gl/#/documentation/developer-guide/using-layers?section=accessors export interface AccessorObjectInfo { index: number; data: any; target: any[]; } export type FlowAccessor = (flow: Flow, objectInfo?: AccessorObjectInfo) => T; export type LocationAccessor = (location: Location) => T; export type LocationCircleAccessor = (locCircle: LocationCircle) => T; export type NumberScale = (value: number) => number; ================================================ FILE: packages/core/tsconfig.build.esm.json ================================================ { "extends": "./tsconfig.build.json", "compilerOptions": { "target": "es2017", "module": "es2015", "outDir": "dist-esm", "moduleResolution": "node" } } ================================================ FILE: packages/core/tsconfig.build.json ================================================ { "extends": "./tsconfig.json", "include": [ "src/**/*", "../../typings/*" ] } ================================================ FILE: packages/core/tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "module": "commonjs", "esModuleInterop": true, "outDir": "dist", "declaration": true, "declarationMap": true, "sourceMap": true, "skipLibCheck": true, "strict": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, "lib": [ "es2015", "dom" ] }, "include": [ "src/**/*", "examples/**/*", "../../typings/*", "stories/**/*" ] } ================================================ FILE: packages/react/package.json ================================================ { "name": "@flowmap.gl/react", "version": "7.3.4", "description": "React components for @flowmap.gl/core", "main": "dist/index.js", "module": "dist-esm/index.js", "types": "dist/index.d.ts", "files": [ "src", "dist", "dist-esm" ], "scripts": { "dev": "tsc --watch & tsc --watch --project tsconfig.build.esm.json", "build:es5": "rm -rf dist && tsc --project tsconfig.build.json", "build:esm": "rm -rf dist-esm && tsc --project tsconfig.build.esm.json", "build": "yarn build:es5 && yarn build:esm", "typecheck": "tsc --noEmit", "prepare": "yarn build" }, "repository": { "type": "git", "url": "git+https://github.com/teralytics/flowmap.gl.git" }, "author": "Teralytics AG", "license": "Apache-2.0", "bugs": { "url": "https://github.com/teralytics/flowmap.gl/issues" }, "homepage": "https://github.com/teralytics/flowmap.gl#readme", "dependencies": { "@flowmap.gl/core": "^7.3.4", "@mapbox/geo-viewport": "^0.4.0", "d3-color": "^2.0.0", "d3-geo": "^2.0.1" }, "devDependencies": { "@deck.gl/react": "^8.2.8", "@types/d3-color": "^1.2.2", "@types/d3-geo": "^1.11.1", "@types/mapbox__geo-viewport": "^0.4.0", "@types/react": "^16.9.49", "react": "^16.7.0", "react-map-gl": "^5.2.8", "typescript": "^4.0.2" }, "peerDependencies": { "@deck.gl/react": ">= 8.0.0", "react": ">= 16.0.0", "react-dom": ">= 16.0.0", "react-map-gl": ">= 5.0.0" }, "gitHead": "685236ef24d44ee2c6f0c2aed79dd9c52a4e73fe", "publishConfig": { "access": "public" } } ================================================ FILE: packages/react/src/FlowMap.tsx ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { DeckGL } from '@deck.gl/react'; import FlowMapLayer, { BasicProps, Flow, FlowLayerPickingInfo, PickingType } from '@flowmap.gl/core'; import { Property } from 'csstype'; import * as React from 'react'; import { StaticMap } from 'react-map-gl'; import { ViewState } from '@flowmap.gl/core'; const FLOW_MAP_LAYER_ID = 'flow-map-layer'; export const enum HighlightType { LOCATION_AREA = 'locationArea', LOCATION = 'location', FLOW = 'flow', } export interface LocationHighlight { type: HighlightType.LOCATION | HighlightType.LOCATION_AREA; locationId: string; } export interface FlowHighlight { type: HighlightType.FLOW; flow: Flow; } export type Highlight = LocationHighlight | FlowHighlight; export interface Props extends BasicProps { initialViewState: ViewState; mapboxAccessToken: string; mapStyle?: string; multiselect?: boolean; mixBlendMode?: Property.MixBlendMode; onSelected?: (locationIds: string[] | undefined) => void; onHighlighted?: (highlight: Highlight | undefined, info: FlowLayerPickingInfo | undefined) => void; onViewStateChange?: (viewState: ViewState) => void; } export interface State { viewState?: ViewState; highlight?: Highlight; selectedLocationIds?: string[]; time: number; } const ESC_KEY = 'Escape'; export default class FlowMap extends React.Component { static defaultProps: Partial = { mixBlendMode: 'multiply', }; static getDerivedStateFromProps(props: Props, state: State): Partial | null { if (props.selectedLocationIds !== state.selectedLocationIds) { return { selectedLocationIds: props.selectedLocationIds, }; } return null; } readonly state: State = { viewState: this.props.initialViewState, time: 0, }; private animationFrame: number = -1; componentDidMount() { document.addEventListener('keydown', this.handleKeyDown); const { animate } = this.props; if (animate) { this.animate(); } const { onViewStateChange } = this.props; if (onViewStateChange) { const { viewState } = this.state; if (viewState) { onViewStateChange(viewState); } } } componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any) { const { animate } = this.props; if (animate !== prevProps.animate) { if (animate) { this.animate(); } else { this.stopAnimation(); } } } componentWillUnmount() { document.removeEventListener('keydown', this.handleKeyDown); this.stopAnimation(); } render() { const { mapboxAccessToken, mapStyle, mixBlendMode } = this.props; const flowMapLayer = this.getFlowMapLayer(); return ( <> ); } private stopAnimation() { if (this.animationFrame) { window.cancelAnimationFrame(this.animationFrame); } } private animate = () => { const loopLength = 1800; // unit corresponds to the timestamp in source data const animationSpeed = 30; // unit time per second const timestamp = Date.now() / 1000; const loopTime = loopLength / animationSpeed; this.setState({ time: ((timestamp % loopTime) / loopTime) * loopLength, }); this.animationFrame = window.requestAnimationFrame(this.animate); }; private getFlowMapLayer() { const { initialViewState, mapboxAccessToken, mixBlendMode, multiselect, animationTailLength, onSelected, onHighlighted, ...flowMapLayerProps } = this.props; const { highlight, selectedLocationIds } = this.state; return new FlowMapLayer({ id: FLOW_MAP_LAYER_ID, animationCurrentTime: this.state.time, ...flowMapLayerProps, selectedLocationIds, animationTailLength, highlightedLocationId: highlight && highlight.type === HighlightType.LOCATION ? highlight.locationId : undefined, highlightedLocationAreaId: highlight && highlight.type === HighlightType.LOCATION_AREA ? highlight.locationId : undefined, highlightedFlow: highlight && highlight.type === HighlightType.FLOW ? highlight.flow : undefined, onHover: this.handleFlowMapHover, onClick: this.handleFlowMapClick, }); } private highlight(highlight: Highlight | undefined, info?: FlowLayerPickingInfo) { this.setState({ highlight }); const { onHighlighted } = this.props; if (onHighlighted) { onHighlighted(highlight, info); } } private selectLocations(selectedLocationIds: string[] | undefined) { this.setState(state => ({ ...state, selectedLocationIds, })); const { onSelected } = this.props; if (onSelected) { onSelected(selectedLocationIds); } } private handleFlowMapHover = (info: FlowLayerPickingInfo) => { const { type, object } = info; switch (type) { case PickingType.FLOW: { if (!object) { this.highlight(undefined); } else { this.highlight( { type: HighlightType.FLOW, flow: object, }, info, ); } break; } case PickingType.LOCATION_AREA: case PickingType.LOCATION: { const { getLocationId } = this.props; if (!object) { this.highlight(undefined); } else { this.highlight( { type: type === PickingType.LOCATION_AREA ? HighlightType.LOCATION_AREA : HighlightType.LOCATION, locationId: (getLocationId || FlowMapLayer.defaultProps.getLocationId.value)(object), }, info, ); } break; } } }; private handleFlowMapClick = ({ type, object }: FlowLayerPickingInfo) => { switch (type) { case PickingType.LOCATION_AREA: case PickingType.LOCATION: { if (object) { const { getLocationId, multiselect } = this.props; const { selectedLocationIds } = this.state; const locationId = (getLocationId || FlowMapLayer.defaultProps.getLocationId.value)(object); const isSelected = selectedLocationIds && selectedLocationIds.indexOf(locationId) >= 0; let next: string[] | undefined; if (multiselect) { if (selectedLocationIds) { if (isSelected) { next = selectedLocationIds.filter(id => id !== locationId); } else { next = [...selectedLocationIds, locationId]; } } else { next = [locationId]; } } else { if (isSelected) { next = undefined; } else { next = [locationId]; } } this.selectLocations(next); this.highlight(undefined); } break; } } }; private handleViewStateChange = ({ viewState }: { viewState: ViewState }) => { this.setState({ viewState, highlight: undefined, }); const { onViewStateChange } = this.props; if (onViewStateChange) { onViewStateChange(viewState); } }; private handleKeyDown = (evt: Event) => { if (evt instanceof KeyboardEvent && evt.key === ESC_KEY) { this.setState({ selectedLocationIds: undefined, highlight: undefined, }); } }; } ================================================ FILE: packages/react/src/index.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ export { default as LocationTotalsLegend } from './legend/LocationTotalsLegend'; export { default as DiffColorsLegend } from './legend/DiffColorsLegend'; export { default as LegendBox } from './legend/LegendBox'; export * from './viewport'; export * from './FlowMap'; import FlowMap from './FlowMap'; export default FlowMap; ================================================ FILE: packages/react/src/legend/DiffColorsLegend.tsx ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { DiffColors, getDiffColorsRGBA } from '@flowmap.gl/core'; import * as React from 'react'; import Disc from './Disc'; export interface Props { colors?: DiffColors; positiveText?: string; negativeText?: string; } const styles = { outer: { display: 'flex', flexDirection: 'column' as 'column', }, disc: { marginRight: 5, }, item: { outer: { display: 'flex', flexDirection: 'row' as 'row', padding: '3px 0', alignItems: 'center' as 'center', }, caption: { marginLeft: 2, }, }, }; const DiffColorsLegend = (props: Props) => { const size = 20; const colorsRGBA = getDiffColorsRGBA(props.colors); const pos = colorsRGBA.positive.locationCircles; const neg = colorsRGBA.negative.locationCircles; return (
{props.positiveText || 'positive difference'}
{props.negativeText || 'negative difference'}
); }; export default DiffColorsLegend; ================================================ FILE: packages/react/src/legend/Disc.tsx ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { RGBA, rgbaAsString } from '@flowmap.gl/core'; import * as React from 'react'; export interface DiscProps { size: number; inner: RGBA; outer: RGBA; outline: RGBA; } const Disc: React.SFC = ({ size, inner, outer, outline }) => { return ( ); }; export default Disc; ================================================ FILE: packages/react/src/legend/LegendBox.tsx ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import React from 'react'; export interface LegendBoxProps { style?: React.CSSProperties; top?: number; left?: number; right?: number; bottom?: number; children: React.ReactNode; } const styles = { outer: { position: 'absolute' as 'absolute', background: '#fff', padding: 10, borderRadius: 4, border: '1px solid #ccc', }, }; const LegendBox: React.SFC = ({ style, top, left, right, bottom, children }) => (
{children}
); export default LegendBox; ================================================ FILE: packages/react/src/legend/LocationTotalsLegend.tsx ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { Colors, DiffColors, getColorsRGBA, getDiffColorsRGBA, isDiffColorsRGBA } from '@flowmap.gl/core'; import * as React from 'react'; import Disc from './Disc'; export interface Props { diff?: boolean; colors?: Colors | DiffColors; aboutEqualText?: string; moreOutgoingText?: string; moreIncomingText?: string; } const styles = { outer: { display: 'flex', flexDirection: 'column' as 'column', }, item: { outer: { display: 'flex', flexDirection: 'row' as 'row', padding: '3px 0', alignItems: 'center' as 'center', }, caption: { marginLeft: 2, }, }, }; const LocationTotalsLegend = ({ diff, colors, aboutEqualText, moreOutgoingText, moreIncomingText }: Props) => { const size = 20; const colorsRGBA = diff ? getDiffColorsRGBA(colors) : getColorsRGBA(colors); const pos = isDiffColorsRGBA(colorsRGBA) ? colorsRGBA.positive.locationCircles : colorsRGBA.locationCircles; const neg = isDiffColorsRGBA(colorsRGBA) ? colorsRGBA.negative.locationCircles : undefined; return (
{neg && }
{aboutEqualText || 'outgoing ≅ incoming'}
{neg && }
{moreOutgoingText || 'more outgoing'}
{neg && }
{moreIncomingText || 'more incoming'}
); }; export default LocationTotalsLegend; ================================================ FILE: packages/react/src/viewport.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { LocationProperties } from '@flowmap.gl/core'; import { BoundingBox, viewport } from '@mapbox/geo-viewport'; import { geoBounds } from 'd3-geo'; import { FeatureCollection, GeometryCollection, GeometryObject } from 'geojson'; import { ViewState } from '@flowmap.gl/core'; export function getViewStateForFeatures( featureCollection: FeatureCollection | GeometryCollection, size: [number, number], opts?: { pad?: number; tileSize?: number; minZoom?: number; maxZoom?: number; }, ): ViewState { const { pad = 0.05, tileSize = 512, minZoom = 0, maxZoom = 100 } = opts || {}; const [[x1, y1], [x2, y2]] = geoBounds(featureCollection as any); const bounds: BoundingBox = [x1 - pad * (x2 - x1), y1 - pad * (y2 - y1), x2 + pad * (x2 - x1), y2 + pad * (y2 - y1)]; const { center: [longitude, latitude], zoom, } = viewport(bounds, size, undefined, undefined, tileSize, true); return { longitude, latitude, zoom: Math.max(Math.min(maxZoom, zoom), minZoom), bearing: 0, pitch: 0, }; } export function getViewStateForLocations( locations: any[], getLocationCentroid: (location: any) => [number, number], size: [number, number], opts?: { pad?: number; tileSize?: number; minZoom?: number; maxZoom?: number; }, ): ViewState { return getViewStateForFeatures( { type: 'GeometryCollection', geometries: locations.map(location => ({ type: 'Point', coordinates: getLocationCentroid(location), })), } as any, size, opts, ); } ================================================ FILE: packages/react/tsconfig.build.esm.json ================================================ { "extends": "./tsconfig.build.json", "compilerOptions": { "target": "es2017", "module": "es2015", "outDir": "dist-esm", "moduleResolution": "node" } } ================================================ FILE: packages/react/tsconfig.build.json ================================================ { "extends": "./tsconfig.json", "include": [ "src/**/*", "../../typings/*" ] } ================================================ FILE: packages/react/tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "module": "commonjs", "esModuleInterop": true, "outDir": "dist", "declaration": true, "declarationMap": true, "sourceMap": true, "strict": true, "skipLibCheck": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, "jsx": "react", "lib": [ "es2015", "dom" ] }, "include": [ "src/**/*", "examples/**/*", "../../typings/*", "stories/**/*" ] } ================================================ FILE: tslint.json ================================================ { "defaultSeverity": "warning", "extends": [ "tslint:recommended", "tslint-react", "tslint-plugin-prettier", "tslint-config-prettier" ], "rules": { "prettier": [ true, { "printWidth": 120, "singleQuote": true, "trailingComma": "all", "arrowParens": "avoid" } ], "interface-name": [ true, "never-prefix" ], "no-console": [true, "log"], "object-literal-sort-keys": false, "member-access": [ true, "no-public" ], "no-namespace": [ true, "allow-declarations" ], "jsx-no-lambda": false } } ================================================ FILE: typings/deck.gl.d.ts ================================================ declare module '@deck.gl/core'; declare module '@deck.gl/layers'; declare module '@deck.gl/react'; ================================================ FILE: typings/kdbush.d.ts ================================================ /* * Copyright 2019 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ declare module 'kdbush'; ================================================ FILE: typings/luma.gl.d.ts ================================================ /* * Copyright 2018 Teralytics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ declare module '@luma.gl/core'; declare module '@luma.gl/constants'; ================================================ FILE: typings/react-map-gl.d.ts ================================================ declare module 'react-map-gl';