diff --git a/devtools/test_dashboard/devtools.js b/devtools/test_dashboard/devtools.js index 6a779e9656e..1b79674e91f 100644 --- a/devtools/test_dashboard/devtools.js +++ b/devtools/test_dashboard/devtools.js @@ -17,8 +17,7 @@ var Tabs = { Plotly.setPlotConfig({ // use local topojson files - topojsonURL: '../../node_modules/sane-topojson/dist/', - + topojsonURL: "../../dist/topojson/", // register mapbox access token // run `npm run preset` if you haven't yet mapboxAccessToken: credentials.MAPBOX_ACCESS_TOKEN, diff --git a/package-lock.json b/package-lock.json index dfd1f8f8f10..ad8272de02a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "css-loader": "^7.1.2", "d3-force": "^1.2.1", "d3-format": "^1.4.5", - "d3-geo": "^1.12.1", "d3-geo-projection": "^2.9.0", "d3-hierarchy": "^1.1.9", "d3-interpolate": "^3.0.1", @@ -67,6 +66,7 @@ "@biomejs/biome": "1.8.3", "@plotly/mathjax-v2": "npm:mathjax@2.7.5", "@plotly/mathjax-v3": "npm:mathjax@^3.2.2", + "@turf/simplify": "^7.2.0", "amdefine": "^1.0.1", "assert": "^2.1.0", "browserify-transform-tools": "^1.7.0", @@ -75,6 +75,7 @@ "canvas": "^2.11.2", "check-node-version": "^4.2.1", "chttps": "^1.0.6", + "d3-geo": "^3.1.1", "deep-equal": "^2.2.3", "ecstatic": "^4.1.4", "esbuild": "^0.23.1", @@ -104,6 +105,7 @@ "karma-viewport": "1.0.2", "lodash": "^4.17.21", "madge": "^8.0.0", + "mapshaper": "^0.6.102", "minify-stream": "^2.1.0", "npm-link-check": "^5.0.1", "open": "^8.4.2", @@ -1001,6 +1003,84 @@ "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==" }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1045,6 +1125,12 @@ "node": ">=14" } }, + "node_modules/@placemarkio/tokml": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@placemarkio/tokml/-/tokml-0.3.7.tgz", + "integrity": "sha512-pQaoQTBvDf7p7d/3ZHDaxWaU62guSYB9KQ6vvecshELunzpdN5tbgw0d+SVO1eYaTlrxX3Nvi7F9DI8FcoJePg==", + "dev": true + }, "node_modules/@plotly/d3": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/@plotly/d3/-/d3-3.8.2.tgz", @@ -1145,6 +1231,18 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, + "node_modules/@tmcw/togeojson": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@tmcw/togeojson/-/togeojson-5.8.1.tgz", + "integrity": "sha512-2YNrbis3l5kS0XrYwiHEZcGwiRp0MJ5CvwGwtMWp2z2tsVlskeec2qgvKHnF0RCwI5GnjrrBOoKsWfndEnd3LA==", + "dev": true, + "engines": { + "node": "*" + }, + "peerDependencies": { + "@types/geojson": "*" + } + }, "node_modules/@ts-graphviz/adapter": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.3.tgz", @@ -1276,38 +1374,105 @@ "url": "https://opencollective.com/turf" } }, + "node_modules/@turf/clean-coords": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-7.2.0.tgz", + "integrity": "sha512-+5+J1+D7wW7O/RDXn46IfCHuX1gIV1pIAQNSA7lcDbr3HQITZj334C4mOGZLEcGbsiXtlHWZiBtm785Vg8i+QQ==", + "dev": true, + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clone": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.2.0.tgz", + "integrity": "sha512-JlGUT+/5qoU5jqZmf6NMFIoLDY3O7jKd53Up+zbpJ2vzUp6QdwdNzwrsCeONhynWM13F0MVtPXH4AtdkrgFk4g==", + "dev": true, + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@turf/helpers": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.1.0.tgz", - "integrity": "sha512-dTeILEUVeNbaEeoZUOhxH5auv7WWlOShbx7QSd4s0T4Z0/iz90z9yaVCtZOLbU89umKotwKaJQltBNO9CzVgaQ==", - "license": "MIT", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", + "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", "dependencies": { "@types/geojson": "^7946.0.10", - "tslib": "^2.6.2" + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.2.0.tgz", + "integrity": "sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q==", + "dev": true, + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, "funding": { "url": "https://opencollective.com/turf" } }, "node_modules/@turf/meta": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.1.0.tgz", - "integrity": "sha512-ZgGpWWiKz797Fe8lfRj7HKCkGR+nSJ/5aKXMyofCvLSc2PuYJs/qyyifDPWjASQQCzseJ7AlF2Pc/XQ/3XkkuA==", - "license": "MIT", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.2.0.tgz", + "integrity": "sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==", "dependencies": { - "@turf/helpers": "^7.1.0", + "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10" }, "funding": { "url": "https://opencollective.com/turf" } }, + "node_modules/@turf/simplify": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-7.2.0.tgz", + "integrity": "sha512-9YHIfSc8BXQfi5IvEMbCeQYqNch0UawIGwbboJaoV8rodhtk6kKV2wrpXdGqk/6Thg6/RWvChJFKVVTjVrULyQ==", + "dev": true, + "dependencies": { + "@turf/clean-coords": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@types/bluebird": { "version": "3.5.36", "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz", "integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==", "dev": true }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -1329,6 +1494,15 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "peer": true }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/geojson": { "version": "7946.0.14", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", @@ -1387,6 +1561,12 @@ "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true + }, "node_modules/@types/sass": { "version": "1.45.0", "resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.45.0.tgz", @@ -1717,6 +1897,15 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -1783,6 +1972,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1974,6 +2172,12 @@ "node": ">=8" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -2349,6 +2553,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001651", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", @@ -2392,6 +2609,12 @@ "element-size": "^1.1.1" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -2886,6 +3109,19 @@ "node": ">= 0.6" } }, + "node_modules/cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "dev": true, + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -3116,11 +3352,15 @@ "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" }, "node_modules/d3-geo": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", - "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, "dependencies": { - "d3-array": "1" + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-geo-projection": { @@ -3141,6 +3381,26 @@ "geostitch": "bin/geostitch" } }, + "node_modules/d3-geo-projection/node_modules/d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-geo/node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-hierarchy": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", @@ -3167,6 +3427,19 @@ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-shape": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", @@ -3358,6 +3631,15 @@ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dev": true, + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3644,6 +3926,20 @@ "node": ">= 0.8.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/dup": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", @@ -3888,13 +4184,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -3940,6 +4233,33 @@ "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", "peer": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es5-ext": { "version": "0.10.63", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.63.tgz", @@ -4347,6 +4667,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "dev": true + }, "node_modules/filing-cabinet": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.0.2.tgz", @@ -4459,6 +4785,21 @@ "node": ">= 0.6.x" } }, + "node_modules/flatbush": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatbush/-/flatbush-3.3.1.tgz", + "integrity": "sha512-oKuPbtT+DS2CxH+9Vhbsq8HifmSCuOw+3Cy5zt/vCIrZl5KyengoTHDBLmtpZoBhcwa7/biNjgL1DwdLMJYm1A==", + "dev": true, + "dependencies": { + "flatqueue": "^1.2.1" + } + }, + "node_modules/flatqueue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/flatqueue/-/flatqueue-1.2.1.tgz", + "integrity": "sha512-X86TpWS1rGuY7m382HuA9vngLeDuWA9lJvhEG+GfgKMV5onSvx5a71cl7GMbXzhWtlN9dGfqOBrpfqeOtUfGYQ==", + "dev": true + }, "node_modules/flatted": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", @@ -4724,11 +5065,33 @@ "node": ">= 12.13.0" } }, + "node_modules/geographiclib": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/geographiclib/-/geographiclib-1.48.0.tgz", + "integrity": "sha512-WIpDKKj2Gu+E4jkF3f3fg7OPF6T1/35tQXKc4jLLQrwjNiTZ5KrrYF5Gp0dQ/YbJIoCjb9vs2/abWTRsCEoi2w==", + "deprecated": "This package has been split into geographic-geodesic + geographiclib-dms; use one or both of these instead of geographiclib.", + "dev": true + }, "node_modules/geojson-vt": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" }, + "node_modules/geokdbush": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/geokdbush/-/geokdbush-1.1.0.tgz", + "integrity": "sha512-pyyrGCMXZwWJaGt1YZIgGQg5ol3JjvEKESTon10HxdfWXlaqh5An8fNJkwOYUE+VkQfCdvYQKPVezTQU9AqWAA==", + "dev": true, + "dependencies": { + "tinyqueue": "^1.2.2" + } + }, + "node_modules/geokdbush/node_modules/tinyqueue": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-1.2.3.tgz", + "integrity": "sha512-Qz9RgWuO9l8lT+Y9xvbzhPT2efIUIFd69N7eF7tJ9lnQl0iLj1M7peK7IoUGZL9DJHw9XftqLreccfxcQgYLxA==", + "dev": true + }, "node_modules/get-amd-module-type": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-6.0.0.tgz", @@ -4757,16 +5120,21 @@ "integrity": "sha1-1ue1C8TkyGNXzTnyJkeoS3NgHpM=" }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4781,6 +5149,28 @@ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", "dev": true }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -5195,12 +5585,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5280,22 +5670,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "engines": { "node": ">= 0.4" @@ -5363,6 +5741,26 @@ "node": ">=18" } }, + "node_modules/http-basic": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-6.0.0.tgz", + "integrity": "sha512-7ScbVjuiReYe8S+OZOpNjoKGXrbhJHIrQQe7eq1TpLTJkxH8MPKvnTUzq/TNLjww1hdFQy8yUIC42wuLhCjYcQ==", + "dev": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/node": "^7.0.31", + "caseless": "~0.12.0", + "concat-stream": "^1.4.6", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + } + }, + "node_modules/http-basic/node_modules/@types/node": { + "version": "7.10.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.14.tgz", + "integrity": "sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA==", + "dev": true + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -5427,6 +5825,21 @@ "node": ">= 14" } }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, "node_modules/https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -5462,6 +5875,12 @@ "postcss": "^8.1.0" } }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", + "dev": true + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -5591,6 +6010,15 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/into-stream": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", @@ -6547,6 +6975,18 @@ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dev": true, + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -6993,6 +7433,74 @@ "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==" }, + "node_modules/mapshaper": { + "version": "0.6.102", + "resolved": "https://registry.npmjs.org/mapshaper/-/mapshaper-0.6.102.tgz", + "integrity": "sha512-LYflAzywcFskJ4aQVdctuymmOzB15MEjRY2nU+tkEH2hXNtsNSsM/IhOTF+NuoQziKxx7tHZObxnQSpcH5yaAA==", + "dev": true, + "dependencies": { + "@placemarkio/tokml": "^0.3.3", + "@tmcw/togeojson": "^5.6.0", + "@xmldom/xmldom": "^0.8.6", + "adm-zip": "^0.5.9", + "commander": "7.0.0", + "cookies": "^0.8.0", + "d3-color": "3.1.0", + "d3-interpolate": "^3.0.1", + "d3-scale-chromatic": "3.0.0", + "delaunator": "^5.0.0", + "fflate": "^0.7.4", + "flatbush": "^3.2.1", + "geokdbush": "^1.1.0", + "iconv-lite": "^0.6.3", + "idb-keyval": "^6.2.0", + "kdbush": "^3.0.0", + "mproj": "0.0.40", + "msgpackr": "^1.10.1", + "opn": "^5.3.0", + "rw": "~1.3.3", + "sync-request": "5.0.0", + "tinyqueue": "^2.0.3" + }, + "bin": { + "mapshaper": "bin/mapshaper", + "mapshaper-gui": "bin/mapshaper-gui", + "mapshaper-xl": "bin/mapshaper-xl" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/mapshaper/node_modules/commander": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz", + "integrity": "sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/mapshaper/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/math-log2": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz", @@ -7306,11 +7814,59 @@ "to-px": "^1.0.1" } }, + "node_modules/mproj": { + "version": "0.0.40", + "resolved": "https://registry.npmjs.org/mproj/-/mproj-0.0.40.tgz", + "integrity": "sha512-Fda92o5LkFUr0Tbz/5QsVhsIgbhnrnQf/eLkYRWGfKspbgvjA0VCk1FF5bT1TgpMHAP7000alBRkV7Q7hesnQg==", + "dev": true, + "dependencies": { + "geographiclib": "1.48.0", + "rw": "~1.3.2" + }, + "bin": { + "mcs2cs": "bin/mcs2cs", + "mproj": "bin/mproj" + }, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/msgpackr": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", + "dev": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, "node_modules/multipipe": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.3.1.tgz", @@ -7450,6 +8006,31 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -7725,6 +8306,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opn/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -7905,6 +8507,12 @@ "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.7.tgz", "integrity": "sha512-iMtu+HCbLXVrpf6Ys/4YKhcFxbux3xK4ZVB9r+a2kMSqeeQWQoDNYlXIsOjwlT2ldYXZ3k5PVeBnYn7fbAo/Bg==" }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, "node_modules/parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", @@ -8323,6 +8931,15 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "dependencies": { + "asap": "~2.0.6" + } + }, "node_modules/protocol-buffers-schema": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz", @@ -8866,6 +9483,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true + }, "node_modules/rrweb-cssom": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", @@ -9677,6 +10300,26 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/sync-request": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-5.0.0.tgz", + "integrity": "sha512-NKhEA4WacR3mRBIFz1niXrIUTrUVFtP2spzrLMINangebvJ/EFyVv+LMJKvVl6UIrJM4Fburnnj91lRnqb4WkA==", + "dev": true, + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.0", + "then-request": "^5.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "dependencies": { + "get-port": "^3.1.0" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -9817,6 +10460,47 @@ "node": ">=10" } }, + "node_modules/then-request": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-5.0.0.tgz", + "integrity": "sha512-A3uIVLD33SAvB10PfsxLuQBMV8GVC/6xKBMPOvkJchi6251e5AMJ+Yy+RVKsVsnj0iYNhN2E5SkNSi58H19wsw==", + "dev": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^6.0.0", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true + }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", + "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -10071,10 +10755,18 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "dev": true, + "engines": { + "node": ">=0.6.x" + } }, "node_modules/type": { "version": "1.2.0", diff --git a/package.json b/package.json index 0b863ad39ef..cf4a4b22b9d 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "use-draftlogs": "node tasks/use_draftlogs.js", "empty-draftlogs": "node tasks/empty_draftlogs.js", "empty-dist": "node tasks/empty_dist.js", - "build": "npm run empty-dist && npm run preprocess && npm run find-strings && npm run bundle && npm run extra-bundles && npm run locales && npm run schema dist && npm run stats", + "build": "npm run empty-dist && npm run preprocess && npm run build_topojson && npm run find-strings && npm run bundle && npm run extra-bundles && npm run locales && npm run schema dist && npm run stats", "regl-codegen": "node devtools/regl_codegen/server.mjs", "cibuild": "npm run empty-dist && npm run preprocess && node tasks/cibundle.mjs", "lint": "npx @biomejs/biome lint", @@ -60,7 +60,10 @@ "version": "npm run build && git add -A lib dist build src/version.js", "postversion": "node -e \"console.log('Version bumped and committed. If ok, run: git push && git push --tags')\"", "postpublish": "node tasks/sync_packages.js", - "postshrinkwrap": "chttps ." + "postshrinkwrap": "chttps .", + "get_geodata": "node tasks/topojson/get_geodata.mjs", + "process_geodata": "node tasks/topojson/process_geodata.mjs", + "build_topojson": "npm run get_geodata && npm run process_geodata" }, "browserify": { "transform": [ @@ -85,7 +88,6 @@ "css-loader": "^7.1.2", "d3-force": "^1.2.1", "d3-format": "^1.4.5", - "d3-geo": "^1.12.1", "d3-geo-projection": "^2.9.0", "d3-hierarchy": "^1.1.9", "d3-interpolate": "^3.0.1", @@ -124,6 +126,7 @@ }, "devDependencies": { "@biomejs/biome": "1.8.3", + "@turf/simplify": "^7.2.0", "amdefine": "^1.0.1", "assert": "^2.1.0", "browserify-transform-tools": "^1.7.0", @@ -132,6 +135,7 @@ "canvas": "^2.11.2", "check-node-version": "^4.2.1", "chttps": "^1.0.6", + "d3-geo": "^3.1.1", "deep-equal": "^2.2.3", "ecstatic": "^4.1.4", "esbuild": "^0.23.1", @@ -161,6 +165,7 @@ "karma-viewport": "1.0.2", "lodash": "^4.17.21", "madge": "^8.0.0", + "mapshaper": "^0.6.102", "@plotly/mathjax-v2": "npm:mathjax@2.7.5", "@plotly/mathjax-v3": "npm:mathjax@^3.2.2", "minify-stream": "^2.1.0", diff --git a/tasks/preprocess.js b/tasks/preprocess.js index 2436d8add38..e9dc2780bd7 100644 --- a/tasks/preprocess.js +++ b/tasks/preprocess.js @@ -11,7 +11,6 @@ var updateVersion = require('./util/update_version'); // main makeBuildCSS(); exposePartsInLib(); -copyTopojsonFiles(); updateVersion(constants.pathToPlotlyVersion); // convert scss to css to js and static css file @@ -72,13 +71,3 @@ function writeLibFiles(obj) { ); } } - -// copy topojson files from sane-topojson to dist/ -function copyTopojsonFiles() { - fs.copy( - constants.pathToTopojsonSrc, - constants.pathToTopojsonDist, - { clobber: true }, - common.throwOnError - ); -} diff --git a/tasks/topojson/config.mjs b/tasks/topojson/config.mjs new file mode 100644 index 00000000000..196cb6f13cb --- /dev/null +++ b/tasks/topojson/config.mjs @@ -0,0 +1,123 @@ +const config = { + resolutions: [50, 110], + scopes: [ + { + name: 'africa', + specs: { + filter: 'georeg === "AFR"', + bounds: [-30, -50, 60, 50] + } + }, + { + name: 'antarctica', + specs: { + filter: 'georeg === "ANT"', + bounds: [-180, -90, 180, -50] + } + }, + { + name: 'asia', + specs: { + filter: 'georeg === "ASI"', + bounds: [15, -90, 180, 85] + } + }, + { + name: 'europe', + specs: { + filter: 'georeg === "EUR"', + bounds: [-30, 0, 60, 90] + } + }, + { + name: 'north-america', + specs: { + filter: 'subreg === "Northern America" || ["Central America", "Caribbean"].includes(intreg)', + bounds: [-180, 0, -45, 85] + } + }, + { + name: 'oceania', + specs: { + filter: 'georeg === "OCE"', + bounds: [-180, -50, 180, 25] + } + }, + { + name: 'south-america', + specs: { + filter: 'intreg === "South America"', + bounds: [-100, -70, -30, 25] + } + }, + { + name: 'usa', + specs: { + filter: 'iso3cd === "USA" && ![4, undefined].includes(stscod)', + bounds: [-180, 0, -45, 85] + } + }, + { + name: 'world', + specs: { + filter: '', + bounds: [] + } + } + ], + outputDirGeojson: './build/geodata/geojson', + outputDirTopojson: './dist/topojson', + inputDir: './build/geodata', + vectors: { + // 'coastlines', 'countries', and 'land' are derived from UN geodata + ocean: { + source: 'ocean', + type: 'physical' + }, + lakes: { + source: 'lakes', + type: 'physical' + }, + rivers: { + source: 'rivers_lake_centerlines', + type: 'physical' + }, + subunits: { + source: 'admin_1_states_provinces_lakes', + type: 'cultural' + } + }, + layers: { + coastlines: 'land', + countries: 'countries', + ocean: 'land', + lakes: 'lakes', + land: 'land', + rivers: 'rivers_lake_centerlines', + subunits: 'admin_1_states_provinces_lakes' + }, + unFilename: 'un_geodata_simplified', + unDownloadUrl: 'https://geoportal.un.org/arcgis/sharing/rest/content/items/d7caaff3ef4b4f7c82689b7c4694ad92/data', + filters: { + countries: 'stscod !== undefined', + land: [ + '{839C9589-44D9-4BD5-A681-13E10ED03C5E}', // AME + '{2EE1B4A5-9C3F-445C-A1AB-399715463785}', // ANT + '{3D11547B-94D9-42C9-B849-14B389FE5F7F}', // OCE + '{32DB79BE-0D53-46BD-995F-EBE7C30ED6B6}', // AFR + '{3F3547E7-C7FB-4347-9D80-575C6485FD2E}', // EUR + '{4351AA38-B383-44BF-8341-720DD74872B4}' // ASI + ] + .map((id) => `globalid === "${id}"`) + .join(' || '), + subunits: ['AUS', 'BRA', 'CAN', 'USA'].map((id) => `adm0_a3 === "${id}"`).join(' || ') + } +}; + +export const getNEFilename = ({ resolution, source }) => `ne_${resolution}m_${source}`; + +export function getNEDownloadUrl({ resolution, vector: { source, type } }) { + return `https://naciscdn.org/naturalearth/${resolution}m/${type}/${getNEFilename({ resolution, source })}.zip`; +} + +export default config; diff --git a/tasks/topojson/get_geodata.mjs b/tasks/topojson/get_geodata.mjs new file mode 100644 index 00000000000..3cb8f0fe16d --- /dev/null +++ b/tasks/topojson/get_geodata.mjs @@ -0,0 +1,76 @@ +import { exec } from 'child_process'; +import fs from 'fs'; +import { Readable } from 'stream'; +import { pipeline } from 'stream/promises'; +import config, { getNEDownloadUrl, getNEFilename } from './config.mjs'; + +const { resolutions, unDownloadUrl, unFilename, vectors } = config; + +const tasksPath = './tasks/topojson'; +const outputPath = './build/geodata'; + +// Download Natural Earth vectors +for (const vector of Object.values(vectors)) { + for (const resolution of resolutions) { + const url = getNEDownloadUrl({ resolution, vector }); + const filename = getNEFilename({ resolution, source: vector.source }); + const archivePath = `${outputPath}/${filename}.zip`; + + if (fs.existsSync(archivePath)) { + console.log(`File ${archivePath} already exists. Skipping download.`); + } else { + try { + console.log(`Downloading data from ${url}`); + + const response = await fetch(url); + if (!response.ok || !response.body) throw new Error(`Bad response: ${response.status}`); + + if (!fs.existsSync(outputPath)) fs.mkdirSync(outputPath, { recursive: true }); + const file = fs.createWriteStream(archivePath); + await pipeline(Readable.fromWeb(response.body), file); + + console.log('Decompressing NE shapefile'); + // Use the shell to handle decompressing + if (!fs.existsSync(outputPath)) fs.mkdirSync(outputPath, { recursive: true }); + exec(`unzip -o ${archivePath} -d ${outputPath}`); + + console.log(`NE Shapefile decompressed to ${outputPath}`); + } catch (error) { + console.error(`Error when downloading file '${archivePath}': ${error}`); + continue; + } + } + } +} + +// Download UN GeoJSON file +const url = unDownloadUrl; +const archivePath = `${tasksPath}/${unFilename}.zip`; +const geojsonPath = `${outputPath}`; +const geojsonFilePath = `${geojsonPath}/${unFilename}.geojson`; + +if (fs.existsSync(archivePath)) { + console.log(`File ${archivePath} already exists. Skipping download.`); + if (fs.existsSync(geojsonFilePath)) console.log(`File ${geojsonFilePath} already exists. Skipping decompression.`); + else exec(`unzip -o ${archivePath} -d ${geojsonPath}`); +} else { + try { + console.log(`Downloading data from ${url}`); + + const response = await fetch(url); + if (!response.ok || !response.body) throw new Error(`Bad response: ${response.status}`); + + // if (!fs.existsSync(outputPath)) fs.mkdirSync(outputPath, { recursive: true }); + const file = fs.createWriteStream(geojsonFilePath); + await pipeline(Readable.fromWeb(response.body), file); + console.log(`UN GeoJSON file saved to ${geojsonFilePath}`); + + console.log('Compressing UN GeoJSON for future use'); + // Use the shell to handle compression + exec(`zip -j ${archivePath} ${geojsonFilePath}`); + + console.log(`UN GeoJSON archive saved to ${archivePath}`); + } catch (error) { + console.error(`Error when downloading file '${geojsonFilePath}': ${error}`); + } +} diff --git a/tasks/topojson/process_geodata.mjs b/tasks/topojson/process_geodata.mjs new file mode 100644 index 00000000000..124cd5298e4 --- /dev/null +++ b/tasks/topojson/process_geodata.mjs @@ -0,0 +1,283 @@ +import simplify from '@turf/simplify'; +import { geoIdentity, geoPath } from 'd3-geo'; +import fs from 'fs'; +import mapshaper from 'mapshaper'; +import path from 'path'; +import config, { getNEFilename } from './config.mjs'; + +const { filters, inputDir, layers, resolutions, scopes, unFilename, vectors } = config; + +// Create output directories +const outputDirGeojson = path.resolve(config.outputDirGeojson); +if (!fs.existsSync(outputDirGeojson)) fs.mkdirSync(outputDirGeojson, { recursive: true }); +const outputDirTopojson = path.resolve(config.outputDirTopojson); +if (!fs.existsSync(outputDirTopojson)) fs.mkdirSync(outputDirTopojson, { recursive: true }); + +async function convertShpToGeo(filename) { + const inputFilePath = `${inputDir}/${filename}.shp`; + const outputFilePath = `${outputDirGeojson}/${filename}.geojson`; + const commands = [inputFilePath, `-proj wgs84`, `-o format=geojson ${outputFilePath}`].join(' '); + await mapshaper.runCommands(commands); + + console.log(`GeoJSON saved to ${outputFilePath}`); +} + +function getJsonFile(filename) { + try { + return JSON.parse(fs.readFileSync(filename, 'utf8')); + } catch (err) { + console.error(`❌ Failed to load JSON input file '${filename}':`, err.message); + process.exit(1); + } +} + +async function createCountriesLayer({ bounds, filter, name, resolution, source }) { + console.log(`Building ${resolution}m countries layer for '${name}'`); + const inputFilePath = `${outputDirGeojson}/${unFilename}_${resolution}m/${source}.geojson`; + const outputFilePath = `${outputDirGeojson}/${name}_${resolution}m/countries.geojson`; + const commands = [ + inputFilePath, + bounds.length ? `-clip bbox=${bounds.join(',')}` : '', + filter ? `-filter '${filter}'` : '', + `-o ${outputFilePath}` + ].join(' '); + await mapshaper.runCommands(commands); + addCentroidsToGeojson(outputFilePath); + // TODO: Add simplification command if on 110m resolution? Or take care of somewhere else? +} + +function addCentroidsToGeojson(geojsonPath) { + const geojson = getJsonFile(geojsonPath); + if (!geojson.features) return; + + const features = geojson.features.map((feature) => { + const centroid = getCentroid(feature); + feature.properties.ct = centroid; + + return feature; + }); + + fs.writeFileSync(geojsonPath, JSON.stringify({ ...geojson, features })); +} + +async function createLandLayer({ bounds, name, resolution, source }) { + // TODO: Figure out way to only include North and Central America via filter, dissolve + console.log(`Building ${resolution}m land layer for '${name}'`); + const inputFilePath = `${outputDirGeojson}/${unFilename}_${resolution}m/${source}.geojson`; + const outputFilePath = `${outputDirGeojson}/${name}_${resolution}m/land.geojson`; + const commands = [ + inputFilePath, + '-dissolve', + bounds.length ? `-clip bbox=${bounds.join(',')}` : '', + `-o ${outputFilePath}` + ].join(' '); + await mapshaper.runCommands(commands); +} + +async function createCoastlinesLayer({ bounds, name, resolution, source }) { + console.log(`Building ${resolution}m coastlines layer for '${name}'`); + // TODO: Update source to be a path? + const inputFilePath = `${outputDirGeojson}/${unFilename}_${resolution}m/${source}.geojson`; + const outputFilePath = `${outputDirGeojson}/${name}_${resolution}m/coastlines.geojson`; + const commands = [ + inputFilePath, + '-dissolve', + '-lines', + bounds.length ? `-clip bbox=${bounds.join(',')}` : '', + `-o ${outputFilePath}` + ].join(' '); + await mapshaper.runCommands(commands); +} + +async function createOceanLayer({ bounds, name, resolution, source }) { + console.log(`Building ${resolution}m ocean layer for '${name}'`); + const inputFilePath = `./tasks/topojson/world_rectangle.geojson`; + const outputFilePath = `${outputDirGeojson}/${name}_${resolution}m/ocean.geojson`; + const eraseFilePath = `${outputDirGeojson}/${unFilename}_${resolution}m/${source}.geojson`; + const commands = [ + inputFilePath, + bounds.length ? `-clip bbox=${bounds.join(',')}` : '', + `-erase ${eraseFilePath}`, + `-o ${outputFilePath}` + ].join(' '); + await mapshaper.runCommands(commands); +} + +async function createRiversLayer({ name, resolution, source }) { + console.log(`Building ${resolution}m rivers layer for '${name}'`); + const inputFilePath = `${outputDirGeojson}/${getNEFilename({ resolution, source })}.geojson`; + const outputFilePath = `${outputDirGeojson}/${name}_${resolution}m/rivers.geojson`; + const commands = [ + inputFilePath, + `-clip ${outputDirGeojson}/${name}_${resolution}m/countries.geojson`, // Clip to the continent + `-o ${outputFilePath}` + ].join(' '); + await mapshaper.runCommands(commands); +} + +async function createLakesLayer({ name, resolution, source }) { + console.log(`Building ${resolution}m lakes layer for '${name}'`); + const inputFilePath = `${outputDirGeojson}/${getNEFilename({ resolution, source })}.geojson`; + const outputFilePath = `${outputDirGeojson}/${name}_${resolution}m/lakes.geojson`; + const commands = [ + inputFilePath, + `-clip ${outputDirGeojson}/${name}_${resolution}m/countries.geojson`, // Clip to the continent + `-o ${outputFilePath}` + ].join(' '); + await mapshaper.runCommands(commands); +} + +async function createSubunitsLayer({ name, resolution, source }) { + console.log(`Building ${resolution}m subunits layer for '${name}'`); + const filter = ['AUS', 'BRA', 'CAN', 'USA'].map((id) => `adm0_a3 === "${id}"`).join(' || '); + const inputFilePath = `${outputDirGeojson}/${getNEFilename({ resolution, source })}.geojson`; + const outputFilePath = `${outputDirGeojson}/${name}_${resolution}m/subunits.geojson`; + const commands = [ + inputFilePath, + `-filter "${filter}"`, + `-clip ${outputDirGeojson}/${name}_${resolution}m/countries.geojson`, // Clip to the continent + `-o ${outputFilePath}` + ].join(' '); + await mapshaper.runCommands(commands); + addCentroidsToGeojson(outputFilePath); +} + +function pruneProperties(topojson) { + for (const layer in topojson.objects) { + switch (layer) { + case 'countries': + topojson.objects[layer].geometries = topojson.objects[layer].geometries.map((geometry) => { + const { properties } = geometry; + if (properties) { + geometry.id = properties.iso3cd; + geometry.properties = { + ct: properties.ct + }; + } + + return geometry; + }); + break; + case 'subunits': + topojson.objects[layer].geometries = topojson.objects[layer].geometries.map((geometry) => { + const { properties } = geometry; + if (properties) { + geometry.id = properties.postal; + geometry.properties = { + ct: properties.ct, + gu: properties.gu_a3 + }; + } + + return geometry; + }); + + break; + default: + topojson.objects[layer].geometries = topojson.objects[layer].geometries.map((geometry) => { + delete geometry.id; + delete geometry.properties; + + return geometry; + }); + + break; + } + } + + return topojson; +} + +function getCentroid(feature) { + const { type } = feature.geometry; + const projection = geoIdentity(); + const path = geoPath(projection); + + if (type === 'MultiPolygon') { + let maxArea = -Infinity; + + for (const coordinates of feature.geometry.coordinates) { + const polygon = { type: 'Polygon', coordinates }; + const area = path.area(polygon); + if (area > maxArea) { + maxArea = area; + feature = polygon; + } + } + } + + return path.centroid(feature).map((coord) => +coord.toFixed(2)); +} + +async function convertLayersToTopojson({ name, resolution }) { + const regionDir = path.join(outputDirGeojson, `${name}_${resolution}m`); + if (!fs.existsSync(regionDir)) { + console.log(`Couldn't find ${regionDir}`); + return; + } + + const outputFile = `${outputDirTopojson}/${name}_${resolution}m.json`; + // Layer names default to file names + const commands = [`${regionDir}/*.geojson combine-files`, `-o format=topojson ${outputFile}`].join(' '); + await mapshaper.runCommands(commands); + + // Remove extra information from features + const topojson = getJsonFile(outputFile); + const prunedTopojson = pruneProperties(topojson); + fs.writeFileSync(outputFile, JSON.stringify(prunedTopojson)); + + console.log(`Topojson saved to: ${outputFile}`); +} + +// Get polygon features from UN GeoJSON +const inputFilePath = `${inputDir}/${unFilename}.geojson`; +const outputFilePath50m = `${outputDirGeojson}/${unFilename}_50m/all_features.geojson`; +const outputPath110m = `${outputDirGeojson}/${unFilename}_110m`; +const commandsAllFeatures = [inputFilePath, `-o target=1 ${outputFilePath50m}`].join(' '); +await mapshaper.runCommands(commandsAllFeatures); + +const geojson = getJsonFile(outputFilePath50m); +const simplifiedGeojson = { + ...geojson, + features: geojson.features.map((f) => simplify(f, { tolerance: 0.01, highQuality: true })) +}; +if (!fs.existsSync(outputPath110m)) fs.mkdirSync(outputPath110m, { recursive: true }); +fs.writeFileSync(`${outputPath110m}/all_features.geojson`, JSON.stringify(simplifiedGeojson)); + +for (const resolution of resolutions) { + for (const { source } of Object.values(vectors)) { + await convertShpToGeo(getNEFilename({ resolution, source })); + } + + // Get countries from all polygon features + const inputFilePathCountries = `${outputDirGeojson}/${unFilename}_${resolution}m/all_features.geojson`; + const outputFilePathCountries = `${outputDirGeojson}/${unFilename}_${resolution}m/countries.geojson`; + const commandsCountries = [ + inputFilePathCountries, + `-filter '${filters.countries}'`, + `-o ${outputFilePathCountries}` + ].join(' '); + await mapshaper.runCommands(commandsCountries); + + // Get land from all polygon features + const inputFilePathLand = `${outputDirGeojson}/${unFilename}_${resolution}m/all_features.geojson`; + const outputFilePathLand = `${outputDirGeojson}/${unFilename}_${resolution}m/land.geojson`; + const commandsLand = [inputFilePathLand, `-filter '${filters.land}'`, `-clean -o ${outputFilePathLand}`].join(' '); + await mapshaper.runCommands(commandsLand); +} + +for (const resolution of resolutions) { + for (const { + name, + specs: { bounds, filter } + } of scopes) { + await createCountriesLayer({ bounds, filter, name, resolution, source: layers.countries }); + await createLandLayer({ bounds, name, resolution, source: layers.land }); + await createCoastlinesLayer({ bounds, name, resolution, source: layers.coastlines }); + await createOceanLayer({ bounds, name, resolution, source: layers.ocean }); + await createRiversLayer({ bounds, name, resolution, source: layers.rivers }); + await createLakesLayer({ bounds, name, resolution, source: layers.lakes }); + await createSubunitsLayer({ bounds, name, resolution, source: layers.subunits }); + await convertLayersToTopojson({ name, resolution }); + } +} diff --git a/tasks/topojson/world_rectangle.geojson b/tasks/topojson/world_rectangle.geojson new file mode 100644 index 00000000000..efb6119b4b5 --- /dev/null +++ b/tasks/topojson/world_rectangle.geojson @@ -0,0 +1,21 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-180, -90], + [180, -90], + [180, 90], + [-180, 90], + [-180,-90] + ] + ] + } + } + ] +} diff --git a/tasks/util/constants.js b/tasks/util/constants.js index 6442501d9aa..1f6bb0a4856 100644 --- a/tasks/util/constants.js +++ b/tasks/util/constants.js @@ -24,20 +24,7 @@ var strictIndex = fs.readFileSync(pathToPlotlyStrict, 'utf-8'); var allTraces = fs.readdirSync(path.join(pathToSrc, 'traces')) .filter(startsWithLowerCase); -var pathToTopojsonSrc; -try { - pathToTopojsonSrc = path.join(path.dirname(require.resolve('sane-topojson')), 'dist/'); -} catch(e) { - console.log([ - '', - 'WARN: Cannot resolve path to *sane-topojson* package.', - ' This can happen when one `npm link sane-topojson`', - ' and runs a command in a Docker container.', - ' There is nothing to worry, if you see this warning while running', - ' `npm run test-image`, `npm run test-export` or `npm run baseline` ;)', - '' - ].join('\n')); -} +var pathToTopojsonSrc = path.join(pathToDist, 'topojson/'); var partialBundleNames = [ 'basic', 'cartesian', 'geo', 'gl3d', 'gl2d', 'mapbox', 'finance', 'strict' diff --git a/test/jasmine/karma.conf.js b/test/jasmine/karma.conf.js index f66235d7593..92369e80eb9 100644 --- a/test/jasmine/karma.conf.js +++ b/test/jasmine/karma.conf.js @@ -118,7 +118,7 @@ if(isFullSuite) { } var pathToCustomMatchers = path.join(__dirname, 'assets', 'custom_matchers.js'); -var pathToSaneTopojsonDist = path.join(__dirname, '..', '..', 'node_modules', 'sane-topojson', 'dist'); +var pathToTopojsonDist = path.join(__dirname, '..', '..', 'dist', 'topojson'); var pathToMathJax2 = path.join(__dirname, '..', '..', 'node_modules', '@plotly/mathjax-v2'); var pathToMathJax3 = path.join(__dirname, '..', '..', 'node_modules', '@plotly/mathjax-v3'); var pathToVirtualWebgl = path.join(__dirname, '..', '..', 'node_modules', 'virtual-webgl', 'src', 'virtual-webgl.js'); @@ -193,7 +193,7 @@ func.defaultConfig = { {pattern: pathToMathJax2 + '/**', included: false, watched: false, served: true}, {pattern: pathToMathJax3 + '/**', included: false, watched: false, served: true}, // available to fetch from /base/node_modules/sane-topojson/dist/ - {pattern: pathToSaneTopojsonDist + '/**', included: false, watched: false, served: true} + {pattern: pathToTopojsonDist + '/**', included: false, watched: false, served: true} ], // list of files / pattern to exclude diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 16ff8e10138..66ee8a65324 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -23,9 +23,7 @@ var DBLCLICKDELAY = require('../../../src/plot_api/plot_config').dfltConfig.doub var HOVERMINTIME = require('../../../src/components/fx').constants.HOVERMINTIME; // use local topojson files -Plotly.setPlotConfig({ - topojsonURL: '/base/node_modules/sane-topojson/dist/' -}); +Plotly.setPlotConfig({ topojsonURL: '../../../dist/topojson' }); function move(fromX, fromY, toX, toY, delay) { return new Promise(function(resolve) {