From 77b2f41ee08b585bd8d48f1e40fd256b1776fdbd Mon Sep 17 00:00:00 2001 From: Babayaga Date: Sun, 22 Mar 2026 20:28:41 +0100 Subject: [PATCH] Maintenance Love :) --- packages/ui/shared/package-lock.json | 2382 ++++++++++++++++- packages/ui/shared/package.json | 3 +- packages/ui/shared/src/index.ts | 2 +- .../src/products/places}/grid-generator.ts | 10 +- .../src/modules/places/CompetitorsMapView.tsx | 132 +- .../modules/places/GridSearchPlayground.tsx | 10 +- .../places/components/GridSearchMap.tsx | 280 +- .../places/components/GridSearchSimulator.tsx | 914 ------- .../places/components/MapLayerToggles.tsx | 41 + .../components/map-layers/RegionLayers.tsx | 211 ++ .../components/map-layers/SimulatorLayers.tsx | 152 ++ .../modules/places/gridsearch/GridSearch.tsx | 8 + .../simulator/GridSearchSimulator.tsx | 82 + .../simulator/components/DeferredInputs.tsx | 48 + .../components/SimulatorControls.tsx | 102 + .../components/SimulatorSettingsPanel.tsx | 366 +++ .../simulator/components/SimulatorStats.tsx | 56 + .../simulator/hooks/useGridSimulatorState.ts | 396 +++ .../places/gridsearch/simulator/types.ts | 19 + .../places/hooks/useGridSearchState.ts | 56 +- 20 files changed, 4037 insertions(+), 1233 deletions(-) rename packages/ui/{src/modules/places/utils => shared/src/products/places}/grid-generator.ts (98%) delete mode 100644 packages/ui/src/modules/places/components/GridSearchSimulator.tsx create mode 100644 packages/ui/src/modules/places/components/MapLayerToggles.tsx create mode 100644 packages/ui/src/modules/places/components/map-layers/RegionLayers.tsx create mode 100644 packages/ui/src/modules/places/components/map-layers/SimulatorLayers.tsx create mode 100644 packages/ui/src/modules/places/gridsearch/simulator/GridSearchSimulator.tsx create mode 100644 packages/ui/src/modules/places/gridsearch/simulator/components/DeferredInputs.tsx create mode 100644 packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorControls.tsx create mode 100644 packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorSettingsPanel.tsx create mode 100644 packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorStats.tsx create mode 100644 packages/ui/src/modules/places/gridsearch/simulator/hooks/useGridSimulatorState.ts create mode 100644 packages/ui/src/modules/places/gridsearch/simulator/types.ts diff --git a/packages/ui/shared/package-lock.json b/packages/ui/shared/package-lock.json index e50635c2..7dca3afe 100644 --- a/packages/ui/shared/package-lock.json +++ b/packages/ui/shared/package-lock.json @@ -9,50 +9,17 @@ "version": "1.0.0", "dependencies": { "@hono/zod-openapi": "^1.1.5", + "@turf/turf": "^7.3.4", "zod": "^4.3.6" }, "devDependencies": { "typescript": "^5.0.0" } }, - "../../../polymech-mono/packages/commons": { - "name": "@polymech/commons", - "version": "0.2.6", - "extraneous": true, - "license": "BSD", - "dependencies": { - "@polymech/core": "file:../core", - "@polymech/fs": "file:../fs", - "@repo/typescript-config": "file:../typescript-config", - "@schemastore/package": "^0.0.10", - "ansi-regex": "^6.2.2", - "env-var": "^7.5.0", - "glob": "^10.4.5", - "js-yaml": "^4.1.0", - "jsonpath-plus": "^10.3.0", - "normalize-url": "^8.0.1", - "p-map": "^7.0.3", - "p-throttle": "^4.1.1", - "regedit": "^5.1.4", - "tslog": "^3.3.3", - "tsup": "^2.0.3", - "yargs": "^17.7.2", - "zod": "^3.24.3", - "zod-to-json-schema": "^3.24.5", - "zod-to-ts": "^1.2.0" - }, - "bin": { - "pm-cli": "dist/main.js" - }, - "devDependencies": { - "@types/node": "^22.12.0", - "typescript": "^5.7.3" - } - }, "node_modules/@asteasolutions/zod-to-openapi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-8.1.0.tgz", - "integrity": "sha512-tQFxVs05J/6QXXqIzj6rTRk3nj1HFs4pe+uThwE95jL5II2JfpVXkK+CqkO7aT0Do5AYqO6LDrKpleLUFXgY+g==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-8.5.0.tgz", + "integrity": "sha512-SABbKiObg5dLRiTFnqiW1WWwGcg1BJfmHtT2asIBnBHg6Smy/Ms2KHc650+JI4Hw7lSkdiNebEGXpwoxfben8Q==", "license": "MIT", "dependencies": { "openapi3-ts": "^4.1.2" @@ -62,13 +29,13 @@ } }, "node_modules/@hono/zod-openapi": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@hono/zod-openapi/-/zod-openapi-1.1.5.tgz", - "integrity": "sha512-EAnY6ad4yt/MUKHx716BEGGOXSl5d0/FOLozOYB/pmSEFq07qrzefKFtBEMAgd3hlpJXjH+4lwgTtlAo+BGBgQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@hono/zod-openapi/-/zod-openapi-1.2.3.tgz", + "integrity": "sha512-zAviC3ApRAYGUiGWZiW/mrK/UBqkTi9BKQxn1IvwYyp2kS+k1tlnwut59DfEInpcsq1347zAYOqYI+obMy4vzQ==", "license": "MIT", "dependencies": { - "@asteasolutions/zod-to-openapi": "^8.1.0", - "@hono/zod-validator": "^0.7.4", + "@asteasolutions/zod-to-openapi": "^8.5.0", + "@hono/zod-validator": "^0.7.6", "openapi3-ts": "^4.5.0" }, "engines": { @@ -80,25 +47,2224 @@ } }, "node_modules/@hono/zod-validator": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@hono/zod-validator/-/zod-validator-0.7.5.tgz", - "integrity": "sha512-n4l4hutkfYU07PzRUHBOVzUEn38VSfrh+UVE5d0w4lyfWDOEhzxIupqo5iakRiJL44c3vTuFJBvcmUl8b9agIA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@hono/zod-validator/-/zod-validator-0.7.6.tgz", + "integrity": "sha512-Io1B6d011Gj1KknV4rXYz4le5+5EubcWEU/speUjuw9XMMIaP3n78yXLhjd2A3PXaXaUwEAluOiAyLqhBEJgsw==", "license": "MIT", "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, + "node_modules/@turf/along": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-7.3.4.tgz", + "integrity": "sha512-PvIoXin0I1t3nRwJz7uqR6fsxDMqdGwJq90qGOeqkNwlZqlF+5o2wKHPwYwi0RXZhLvxRP5qlbNIvV8ADdbWxw==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/angle": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-7.3.4.tgz", + "integrity": "sha512-235JAfbrNMjHQXQfd/p+fYnlfCHsQsKHda5Eeyc+/jIY0s5mKvhcxgFaOEnigA2q1n+PrVOExs3BViGTKnWhAg==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/area": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.3.4.tgz", + "integrity": "sha512-UEQQFw2XwHpozSBAMEtZI3jDsAad4NnHL/poF7/S6zeDCjEBCkt3MYd6DSGH/cvgcOozxH/ky3/rIVSMZdx4vA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.3.4.tgz", + "integrity": "sha512-D5ErVWtfQbEPh11yzI69uxqrcJmbPU/9Y59f1uTapgwAwQHQztDWgsYpnL3ns8r1GmPWLP8sGJLVTIk2TZSiYA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-clip": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-7.3.4.tgz", + "integrity": "sha512-HCn0q/WPVEE9Dztg7tCvClOPrrh9MoxNUk73byHvcZLBcvziN6F84f/ZbFcbQSh8hgOeVMs/keeqWMqsICcNLg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-7.3.4.tgz", + "integrity": "sha512-XCDYQwCA41Bum3R1xX0Na1nR4ozoe/pCYy5bxqrzyMs87kPJUIfBrD5IWxjnZyLqFpfEpolMHJz5ed1uA2PanQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bearing": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-7.3.4.tgz", + "integrity": "sha512-zvFjapyFaOrM8nBtAND7f4yb0BJV0jyj6cyoXyTYqLY+3Hn0eHgL0M8lwxDLbTom5KfqYDHDVDQC3+VSfypoEA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bezier-spline": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-7.3.4.tgz", + "integrity": "sha512-+iDUeiBKByIs/6K5WW8pG6IDxrRLJHFLM80zSpzk2xBtgy3mq36NZwwt67Pu7EJAkc9GUXKIm9SkspoKue9aYQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-clockwise": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-7.3.4.tgz", + "integrity": "sha512-X/O+u/OsoJ99mujhlqviuB7HX0tdJ5931TBjNSseps43XtROVuB5PwBDgwKfu5lY1B4DSGAxbbxJ795RmPnguQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-concave": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-concave/-/boolean-concave-7.3.4.tgz", + "integrity": "sha512-SHuAzjqaAes6ELDZcN/FKZWCQZsqwYv3gMosoLRFWTwKyBQe8i29e4y6XnXakDr1uklVUeRRcdhZ5oKtX9ABPQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-contains": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-7.3.4.tgz", + "integrity": "sha512-AJMGbtC6HiXgHvq0RNlTfsDB58Qf9Js45MP/APbhGTH4AiLZ8VMDISywVFNd7qN6oppNlDd3xApVR28+ti8bNg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-split": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-crosses": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-7.3.4.tgz", + "integrity": "sha512-v/U3SuGdkexfLTMhho6Vj0OjqPUeYdThxp8zggGJ1VHow27fvLLez0DjUR3AftHjjHM6bRzZoNsu2qUlEe5hjw==", + "license": "MIT", + "dependencies": { + "@turf/boolean-equal": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-disjoint": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-7.3.4.tgz", + "integrity": "sha512-Dl4O27ygi2NqskGQuvSlDLJYlJ2SPkHb3A9T/v6eAudjlMiKdEY6bMxKUfU5y+Px1WiCZxd+9rXGXJgGC3WiQg==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-equal": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-7.3.4.tgz", + "integrity": "sha512-AhWqe7D1o0wp3d3QQRSqgWDI8s1JfTFKFe9rU5mrSxYPGlmaQsJC07RCaYfFiGym9lACd1lxBJiPidCbLaPOfw==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "geojson-equality-ts": "^1.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-intersects": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-7.3.4.tgz", + "integrity": "sha512-sxi41NXkb5hrJgOvpm32hyBLhW8fem0vn2XxR4+jyRg1rM/v3ziF10/VqC9KDZuDNZkt9JjL9B0825Cf7AN6Lg==", + "license": "MIT", + "dependencies": { + "@turf/boolean-disjoint": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-overlap": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-7.3.4.tgz", + "integrity": "sha512-Q3dlswIuqffSiMfln7xa36YDnN1TWtERMF/155rzjglm4NTUG/6S+gNsb8s6qpLjc+hN6btCq1ZjxAWurPf8Vg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/line-overlap": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "geojson-equality-ts": "^1.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-parallel": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-7.3.4.tgz", + "integrity": "sha512-sTNMqsUkLPnSJEqc2IZ5ig3nHRoubyOH2HW1LILqOybCJI630FEM9UoYP1pZniF5nwTyCjQWnXA1FxusVILuFQ==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.3.4.tgz", + "integrity": "sha512-v/4hfyY90Vz9cDgs2GwjQf+Lft8o7mNCLJOTz/iv8SHAIgMMX0czEoIaNVOJr7tBqPqwin1CGwsncrkf5C9n8Q==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "point-in-polygon-hao": "^1.1.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-on-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-7.3.4.tgz", + "integrity": "sha512-70gm5x6YQOZKcw0b/O4jjMwVWnFj+Zb6TXozLgZFDZShc8pgTQtZku7K+HKZ7Eya+7usHIB4IimZauomOMa+iw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-touches": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-touches/-/boolean-touches-7.3.4.tgz", + "integrity": "sha512-XOwhjc0oCWhnBUB+l4drpXcg7mkNXPX3SuSz/Xv7gvLH/yRrBwzVGllzK1AHlGU9BVkGVBJIZGYX7jgTM681NQ==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-valid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-valid/-/boolean-valid-7.3.4.tgz", + "integrity": "sha512-P6M9BtRvzFF2N5g+1/DTIbYGpEbwQ2sv/Pw+uj11P3NYAA9VE8mvrxFYf+CowFdSfY6bY4ejhuqKhrTmAMv7wA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-crosses": "7.3.4", + "@turf/boolean-disjoint": "7.3.4", + "@turf/boolean-overlap": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@types/geojson": "^7946.0.10", + "geojson-polygon-self-intersections": "^1.2.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-within": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-7.3.4.tgz", + "integrity": "sha512-eLgi803gz0KcYkyxnnqnz9Vd6tw2/0eAExe/Rq8sO0dqypaSiomSumxjqu89d/yo24Qz8gW7c0kJ6YihNbMYxA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-split": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/buffer": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-7.3.4.tgz", + "integrity": "sha512-MVOCBDuOl3KGDsh2stW12RmiFaFeSkVjeUbZ+ADUtIVnv+jlFsmjBpFtsEw8s9YQn5g0667QppOshm0FBHA57Q==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/center": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/jsts": "^2.7.1", + "@turf/meta": "7.3.4", + "@turf/projection": "7.3.4", + "@types/geojson": "^7946.0.10", + "d3-geo": "1.7.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-7.3.4.tgz", + "integrity": "sha512-4SsLMDHWthXbyIHsczgFCo4fx+8tC8w2+B5HdEuY+P+cSOOL4T+6QQzd7WWjuN/Y3ndowFssUmwRrvXuwVRxQA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-mean": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-7.3.4.tgz", + "integrity": "sha512-6foVk5HLjlSPr48EI686Eis6/bYrJiHjKQlwY/7YlJc1uDitsIjPw2LjUCGIUZDEd6PdNUgg1+LgI7klXYvW3A==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-median": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-7.3.4.tgz", + "integrity": "sha512-Bz6rDr0plQOGSXgT3X3t941pYd44a5vIY8OEt4Y11H1BsgpmzFc6g7L5mr7FXW/uiYGxOewAfNcVUYUdJf9kMg==", + "license": "MIT", + "dependencies": { + "@turf/center-mean": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-of-mass": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-7.3.4.tgz", + "integrity": "sha512-mOSupDF5qxQTA/kOWYletHcBJQ3S2gVl/IRgrBH/YY9yiFq6UGRpZ0sNcIML4H06u/1DY/jqqG+d1nc/1yIA6Q==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "7.3.4", + "@turf/convex": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.3.4.tgz", + "integrity": "sha512-6c3kyTSKBrmiPMe75UkHw6MgedroZ6eR5usEvdlDhXgA3MudFPXIZkMFmMd1h9XeJ9xFfkmq+HPCdF0cOzvztA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/circle": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-7.3.4.tgz", + "integrity": "sha512-6ccr5iT51/XONF+pbpkqoRxKX4ZVWLubXb1frGCnClv2suo1UIY9SIlINNctVDupXd2P9PpqZCbrXATrcrokPg==", + "license": "MIT", + "dependencies": { + "@turf/destination": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clean-coords": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-7.3.4.tgz", + "integrity": "sha512-S61aJXLvPN/uZHtjzmJbLv7xhi28Sq3PshCIZSvno4Mo45bvl79Vg4aZskrG05AaSSbipplqfH+MZrkW9Xboeg==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clone": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.3.4.tgz", + "integrity": "sha512-pwQ+RyQw986uu7IulY/18NRAebwZZScb084bvVqVkTrllwLSv4oVBqUxmUMiwtp+PNdiRGRFOvNyZqtRsiD+Jw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-7.3.4.tgz", + "integrity": "sha512-+zoSyiF0LilXy4Tr0/lC7IgqbTMZZ2wwP3iSrqre58b61pUtdhCnBcjA2r8FkcW7z3GMbGf5XkIWhO+b+vDSsw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-dbscan": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-7.3.4.tgz", + "integrity": "sha512-RkuXf767Shk0AfY+fh0PASVw8YR4H8zYR7XQrCgWd/bCuh6CXs7rWZ6UTLu/PiA6y6WsIhyAQv4LhNH5kCzpbA==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "@types/geokdbush": "^1.1.5", + "geokdbush": "^2.0.1", + "kdbush": "^4.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-kmeans": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-7.3.4.tgz", + "integrity": "sha512-89mlwhcb+vyZAKX0eBa3LQ8VyIKLayrzJpKGb90sEkIu0hDua9JCE+zlbaPoUAvAqflEiX+poFFuh7pngtsBMg==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "skmeans": "0.9.7", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/collect": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-7.3.4.tgz", + "integrity": "sha512-fG28oDZK4HCXC/AhF0pmHKLtI9DWwdJr/ktuWolrqzA5b1G7eawrXwDu8B5I3sXhdWonNRMcuLbIuz+XQscHKw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/combine": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-7.3.4.tgz", + "integrity": "sha512-wNp9ar4FfpTfQXLZWXQ/jUBBoUFOwRN/mmlv5xrhoYFpP/F5SNy7GVDMZXaBfHdUUplfJUPF5hIKQlCUR8+k3A==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/concave": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-7.3.4.tgz", + "integrity": "sha512-HZa1CV2pv4Xpcoe3t5S3ZW6j9jVbc27exzKwZWF7MlFxSz4BKRirWiME8Fku8nvQcGafpfLc+Lwpma+nGvg06w==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/tin": "7.3.4", + "@types/geojson": "^7946.0.10", + "topojson-client": "3.x", + "topojson-server": "3.x", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/convex": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-7.3.4.tgz", + "integrity": "sha512-zeNv0fFdOoHuOQB7nl6OLb0DyjvzDvm0e3zlFkph50GF9pEKOmkCSmlniw681aWL2aRBdWZBnON3rRzOS+9C7Q==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "concaveman": "^1.2.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/destination": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-7.3.4.tgz", + "integrity": "sha512-YxoUJwkKmTHiRFQxMQOP0tz8Vy+ga5EXl+C+F/WubjDLwT1AJu5y8CNIjLvWyjPWckj/vZG4u/1js5bx6MLADA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/difference": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-7.3.4.tgz", + "integrity": "sha512-kIxizNQrYLO2rtqUIeed0tPycicrXoipy/g9d4mjv91kzBEbwpyojz9zi8U9G1ISBfCEgA7wsViQD0r+8qzxXw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/dissolve": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-7.3.4.tgz", + "integrity": "sha512-xjGY1gQ4icWhDgsW0YfU2KQtij1+ru34AfvtkVMQEgI86O9EwjW2r9Jq5DJY2PMKPbor3kz9yM/RTOiDP7f3Jg==", + "license": "MIT", + "dependencies": { + "@turf/flatten": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-7.3.4.tgz", + "integrity": "sha512-9drWgd46uHPPyzgrcRQLgSvdS/SjVlQ6ZIBoRQagS5P2kSjUbcOXHIMeOSPwfxwlKhEtobLyr+IiR2ns1TfF8w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance-weight": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-7.3.4.tgz", + "integrity": "sha512-dVMNEmIluKgn7iQTmzJJOe0UASRNmmSdFX1boAev5MISaW3AvPiURCCOV+lTIeoaQbWRpEAESbAp6JIimXFr8Q==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/ellipse": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-7.3.4.tgz", + "integrity": "sha512-SMgbERZl12j7H8YaIofmnf0NwAvdF5Wly4tjI/eUhj/sFOKrKXOS1lvCSBJ6uSV9tFijl3ecGOVOlTpURdZ30g==", + "license": "MIT", + "dependencies": { + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/transform-rotate": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/envelope": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-7.3.4.tgz", + "integrity": "sha512-anXSjYMXGAyXT7rpO74VyRI0q/rPAbKE/MYvou+QvG0U/Oa7el0yF4JNNi9wKEAxXg/10aWm9kHp8s2caeLg6A==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/explode": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-7.3.4.tgz", + "integrity": "sha512-7QWhp3f8jhrWjvArhJ74hXBFHMaiJr/2Y1PzHCWue2/pC5MbbTV0o7peehwrrrJC/1uD6CVb3hlcb77IxtMQkw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flatten": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-7.3.4.tgz", + "integrity": "sha512-Yt3HCh/qeNaXS4LYhXczFhBfTeaKlTBoxEw1OICb9RT3SiGU0XCxuK7H0W26OLo7XxB0qP7GPs2L3FZbiri6wQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flip": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-7.3.4.tgz", + "integrity": "sha512-HME+kVMTyvcsYVY6dC6DTvuzq8vvDpw+C7PviEqpuT3KcVlBCoGPAqlWRdyWYOb9MDciOqNxvvJF/okpb/GQcg==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/geojson-rbush": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/geojson-rbush/-/geojson-rbush-7.3.4.tgz", + "integrity": "sha512-aDG/5mMCgKduqBwZ3XpLOdlE2hizV3fM+5dHCWyrBepCQLeM/QRvvpBDCdQKDWKpoIBmrGGYDNiOofnf3QmGhg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/great-circle": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-7.3.4.tgz", + "integrity": "sha512-JvfzWFL9efP+xKtOnKzGvwEIXfaN0CLZoPPxNnWa/cVisLs9FVMlC9PWnuL3/3aqH5VhBHPddmU8ipzNE6KIIA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "arc": "^0.2.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.4.tgz", + "integrity": "sha512-U/S5qyqgx3WTvg4twaH0WxF3EixoTCfDsmk98g1E3/5e2YKp7JKYZdz0vivsS5/UZLJeZDEElOSFH4pUgp+l7g==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/hex-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-7.3.4.tgz", + "integrity": "sha512-TDCgBykFdsrP3IOOfToiiLpYkbUb3eEEhM9riIqWht0ubKUY61LN7qVs9bxZD83hG6XaDB6uY7SWkxK1zIEopQ==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/intersect": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/interpolate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-7.3.4.tgz", + "integrity": "sha512-lwYSMbHxsXYEWObv0tyBCjwTLXyfsTvOLn/NFhlsGrNCYEXn8I1VPtLGwuxbSdF3hVRgurn8qftkB1npHrNs6Q==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/hex-grid": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/point-grid": "7.3.4", + "@turf/square-grid": "7.3.4", + "@turf/triangle-grid": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/intersect": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-7.3.4.tgz", + "integrity": "sha512-VsqMEMeRWWs2mjwI7sTlUgH1cEfugTGhQ0nF8ncHG7YKd9HUUTzIKpn9FJeoguPWIYITcy1ar4yJEOU/hteBVw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.3.4.tgz", + "integrity": "sha512-88Eo4va4rce9sNZs6XiMJowWkikM3cS2TBhaCKlU+GFHdNf8PFEpiU42VDU8q5tOF6/fu21Rvlke5odgOGW4AQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isobands": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-7.3.4.tgz", + "integrity": "sha512-SFYefwjQdQfF0MV0zfaSwNg9J1wD7mfPP8scGcScKGM3admbwS2A3V8rqPADBfYLD2eCPBDFnySxcl9SHbPung==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isolines": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-7.3.4.tgz", + "integrity": "sha512-UFRIULkIgkZOmrhLxExWvguixbzfoCgVcXIqo2Cp68do4v+nwc3pTM7MTt4DBVFloIdX0Usrn4K44LQ/V05gxg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/jsts": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@turf/jsts/-/jsts-2.7.2.tgz", + "integrity": "sha512-zAezGlwWHPyU0zxwcX2wQY3RkRpwuoBmhhNE9HY9kWhFDkCxZ3aWK5URKwa/SWKJbj9aztO+8vtdiBA28KVJFg==", + "license": "(EDL-1.0 OR EPL-1.0)", + "dependencies": { + "jsts": "2.7.1" + } + }, + "node_modules/@turf/kinks": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-7.3.4.tgz", + "integrity": "sha512-LZTKELWxvXl0vc9ZxVgi0v07fO9+2FrZOam2B10fz/eGjy3oKNazU5gjggbnc499wEIcJS4hN+VyjQZrmsJAdQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/length": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-7.3.4.tgz", + "integrity": "sha512-Dg1GnQ/B2go5NIWXt91N4L7XTjIgIWCftBSYIXkrpIM7QGjItzglek0Z5caytvb8ZRWXzZOGs8//+Q5we91WuQ==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-arc": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-7.3.4.tgz", + "integrity": "sha512-nqZ+JKjDVIrvREFHgtJIP9Ps4WbWw3eStqdIzAPolrzoXyAZnpIKquyfRTxpJFYUUjDmf+uQ/SFWsPP4SOWAqQ==", + "license": "MIT", + "dependencies": { + "@turf/circle": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-chunk": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-7.3.4.tgz", + "integrity": "sha512-xWEHR99EpUO5ZPEZhMfa0QvnFZC0W+QLxB1GcJcSeJAQ5ZMXUXY8doKF1Nztk0eppawMprEEO3nQWLvQoR4z2g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/length": "7.3.4", + "@turf/line-slice-along": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-intersect": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-7.3.4.tgz", + "integrity": "sha512-XygbTvHa6A+v6l2ZKYtS8AAWxwmrPxKxfBbdH75uED1JvdytSLWYTKGlcU3soxd9sYb4x/g9sDvRIVyU6Lucrg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "sweepline-intersections": "^1.5.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-offset": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-7.3.4.tgz", + "integrity": "sha512-CSrg3njde9Tx+C0oL+BHUpZYpgD+PEmzp0ldDNis5ZQiTe5tUrwiIyG7A/QXf9eDnGhtV1WhCAycX0Wjged4pg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-overlap": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-7.3.4.tgz", + "integrity": "sha512-3GBECiwNAQ2MmSwiqAHMweIl+EiePK0Jx4fXxF1KFE+NGCDv/MbGcEYfAbmsTg8mg6oRI9D8fJZzrT44DHpHXA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-on-line": "7.3.4", + "@turf/geojson-rbush": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "fast-deep-equal": "^3.1.3", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-segment": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-7.3.4.tgz", + "integrity": "sha512-UeISzf/JHoWEY5yeoyvKwA5epWcvJMCpCwbIMolvfTC5pp+IVozjHPVCRvRWuzmbmAvetcW0unL5bjqi0ADmuQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-7.3.4.tgz", + "integrity": "sha512-6Vt4Eptdr2C5T+jtpbo8D4v8b6X7KqYonPPyMB6huv+Kcg3nz4JRI9OQCDCaon9rWvU3ffWwjsjcbJCQS9o0sA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice-along": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-7.3.4.tgz", + "integrity": "sha512-RT5HydNy8+m9Y3u39USeYZauG2EyMqCYoLnTpWcAxbZGdq9WjIwdzAwYir3d8eJkOzjlR6Khz071VM4Ufqs0Kg==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-split": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-7.3.4.tgz", + "integrity": "sha512-l1zmCSUnGsiN4gf22Aw91a2VnYs5DZS67FdkYqKgr+wPEAL/gpQgIBBWSTmhwY8zb3NEqty+f/gMEe8EJAWYng==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/geojson-rbush": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@turf/truncate": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-to-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-7.3.4.tgz", + "integrity": "sha512-vRnDHjzwOroC74/fsJEU+dUeGhiR/B2bG0/HeEWRBplAjmwVPptRBmDGtXKTz8sbA6or17/XtOITp3zTU0lBZw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/mask": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-7.3.4.tgz", + "integrity": "sha512-FJIlSk8m0AiqzNoLSMdYuhDRif6aeOYVdW/WxjEjpUoMalwy2w5MMlZqJB9zxt/xSrMq6lvTWJgZfZfGL2s4ZQ==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.3.4.tgz", + "integrity": "sha512-tlmw9/Hs1p2n0uoHVm1w3ugw1I6L8jv9YZrcdQa4SH5FX5UY0ATrKeIvfA55FlL//PGuYppJp+eyg/0eb4goqw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/midpoint": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-7.3.4.tgz", + "integrity": "sha512-/XAeGvsz8l5HaqcP7TUlexzGfibqXozQgBZ8rH7az6op2Dfm3pL/Z7bKLHoVavM0ccBg0Pt7g6j9NM54kZWdKA==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/moran-index": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-7.3.4.tgz", + "integrity": "sha512-SNb16szwEG0OiyNn3z9zvSnk3M3tfwvvN8i//9UIC32APEApI+MRXCl93H/qZkKMhhh/cHA0pF0pjYZwl5z8Ow==", + "license": "MIT", + "dependencies": { + "@turf/distance-weight": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-neighbor-analysis": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-neighbor-analysis/-/nearest-neighbor-analysis-7.3.4.tgz", + "integrity": "sha512-8EZlDy5poU0t7BDy8KTzOmfiGsAs2kWuB3/kgI4sMdbThKVk2P4hHKuToCSGvqAzwSy3B2qKYM1N6JeVWytu+w==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-7.3.4.tgz", + "integrity": "sha512-WfI09f2bX0nKx/jkO7zCt3tUrJulyAlUYQtZHP7lWYMCOmZ6Pq26D6lKWjpfs2it0OHbhlx1XF/UupEUaz830w==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-on-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-7.3.4.tgz", + "integrity": "sha512-DQrP3lRju83rIXFN68tUEpc7ki/eRwdwBkK2CTT4RAcyCxbcH2NGJPQv8dYiww/Ar77u1WLVn+aINXZH904dWw==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-to-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-7.3.4.tgz", + "integrity": "sha512-Nzp3ojQt0gDACNYG+oNWymRXAUCey0LzdiSezYtRwdA0/+FQCtuxP8Lbc8FftV10JL8D78/CRlmt7omaXLLXCg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/point-to-line-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/planepoint": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-7.3.4.tgz", + "integrity": "sha512-KAhMAnddbuWIEZuk2bK//g+xTeKn8aV9N2AaE27x6JMJyV/wqvatIuVVqEIXI3SkAFbhiVBpVuarvPYhrJ+fhg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-7.3.4.tgz", + "integrity": "sha512-9CL3OJ4dEt266+fxYlOQeRFqAY3XtsAuak2Gpk+K8k+Y3yGv8pvyn3QaAQ6P2npbiKt0zfG8Md/+HBAPOMPQ0A==", + "license": "MIT", + "dependencies": { + "@turf/boolean-within": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-on-feature": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-7.3.4.tgz", + "integrity": "sha512-tQfIxsJUxZqyO7OeJC25y3DqN9i4fmrAt4TBrPvZcIIwymgN7aMrElJKlg/dfi7JDihKp3h/CkWMjtMQA14Vwg==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/center": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-line-distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-7.3.4.tgz", + "integrity": "sha512-IdPAxlAQZj7FCZg+ObyVHlNdqwLL/oxYoQjpxMNJ511gNxokCtEv0aeRZQjYOYIxr9Ss97v3yo3ILJaF9V2kPw==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@turf/projection": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-polygon-distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-to-polygon-distance/-/point-to-polygon-distance-7.3.4.tgz", + "integrity": "sha512-VxbkgHyzCkYWSxirqSUqw+lzbYmTf2qFhVZ/T5dprhwyXWcgalpupvgRzmZmjKkgsoJ017vrvCNKZRaCCn+Z7Q==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/point-to-line-distance": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/points-within-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-7.3.4.tgz", + "integrity": "sha512-HfT83Iw99zywDfCp+nJwS+JDzH+GdNug0sut9WDjGEznHKoZyAcOk+hGKL/ja8TeCLx9VsZHOiVCQFm+NTgvgA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-smooth": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-7.3.4.tgz", + "integrity": "sha512-AnpaGgNYVvP/dfz10id3AotDrUh9O+4unXCk3es1ff51VrpUhVgH3H+zyTSbVL4zAXN/ejPb8UnKCxDvNOQs4g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-tangents": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-7.3.4.tgz", + "integrity": "sha512-D1IFocXJYF8PUMZ+BmnOstyRrzklqC86FgakYVk9O61F9Ki8LhMGaRfF+6reKMD473KvHvEf1M2EgmGt+OHDRw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-within": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-to-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-7.3.4.tgz", + "integrity": "sha512-xhmOZ5rHZAKLUDLeYKWMsX84ip8CCGOcGLBHtPPYOjdIDHddMV6Sxt5kVgkmlZpK6NEWEmOD6lYR4obxHcHlGA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygonize": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-7.3.4.tgz", + "integrity": "sha512-kmj05rkJ4tE8LvbQ4GVsL5GOrRiX/F5W4RIdxo8gPGTw1Y5oLG/1vFk6Hg6x63L1WcdNtF0sq6AdEI0G9BXWXA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/envelope": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/projection": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-7.3.4.tgz", + "integrity": "sha512-p91zOaLmzoBHzU/2H6Ot1tOhTmAom85n1P7I4Oo0V9xU8hmJXWfNnomLFf/6rnkKDIFZkncLQIBz4iIecZ61sA==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/quadrat-analysis": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/quadrat-analysis/-/quadrat-analysis-7.3.4.tgz", + "integrity": "sha512-Yxqq8wgrDiXIX+s0uOZ2exmYfRwTIcUX8J7j4P+sbyLVbyN8W3AjN2s5ZX21P0aFf3v24FBd2fNWlm5VmMUAdg==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/point-grid": "7.3.4", + "@turf/random": "7.3.4", + "@turf/square-grid": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/random": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-7.3.4.tgz", + "integrity": "sha512-CXMS5XDoI5x0zc1aCYbn3t603k8hjaFHNsSOvGBW20z68cwP0UwMQQr0KLqFPqI4J1O7dMX+urn8IHH27RXFYg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rectangle-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-7.3.4.tgz", + "integrity": "sha512-qM7vujJ4wndB4MKZlEcnUSawgvs5wXpSEFf4f+LWRIfmGhtv6serzDqFzWcmy8kF8hg5J465PMktRmAFWq/a+w==", + "license": "MIT", + "dependencies": { + "@turf/boolean-intersects": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rewind": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-7.3.4.tgz", + "integrity": "sha512-4BZ8MHMujl4NAT7XnIs7JoOuDhpR96oDTB0RtqTeIP4onioIedVnw1ZA3Uq08sILGpR0qKLuDsvdz4x9jtbptg==", + "license": "MIT", + "dependencies": { + "@turf/boolean-clockwise": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-bearing": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-7.3.4.tgz", + "integrity": "sha512-tvX1toSo80q0iL0cUMMXpSKsCCfOjRqDGCmOdR6B9shhk6xP1ZM2PLQDr+MFPBFeGyQuyY4CNFkV2+3DF49vYw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-destination": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-7.3.4.tgz", + "integrity": "sha512-6HikEb5nm2A18FQWk6vVLMQkc099I/7c69j47RYM27xQK8J8uBCNk1zLYyMPcZTh24xcNSbZ1iPHDsDOqw6wWQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-7.3.4.tgz", + "integrity": "sha512-phwskeijdgYMsR3qDQmytfsg2iZcp3uWK7UFc76wKTEpxozbDGFI4enX5gXvZPpyI1iD7gsktGqHsO33AjnFDA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sample": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-7.3.4.tgz", + "integrity": "sha512-XzAATg09c2XYAXkIBbg8lktSrU1tXNjJYXtbVwF6jLp1q2wTRpwb+mZpTEPAwzZwVF81uR5c0CsdQyr5UHINVw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sector": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-7.3.4.tgz", + "integrity": "sha512-x2tNAXl21HRcF302ghU5ohE/vmmfDcXpQKgoWHyi7o5Q9kDRBwy7kbvr5YxbT3vwW/kAWUDYM7FoXNH42bXgCw==", + "license": "MIT", + "dependencies": { + "@turf/circle": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-arc": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/shortest-path": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-7.3.4.tgz", + "integrity": "sha512-xbK/oM+JRL+lJCHkAdZ3QPgoivT40J9WKJ0d1Ddt8LXTpzX2YeJVgcwOZaBPG9ncZUzHfHIWS1rUjc54clnZcg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/clean-coords": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/transform-scale": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/simplify": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-7.3.4.tgz", + "integrity": "sha512-OoSwu3vI0H9P+GzLDaOJIL9v0V8ubeP8wQjM8GeMEZrq6U2uh9JWQnAU+jviT3ODcKF5H+88snpiMik585L0wA==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-7.3.4.tgz", + "integrity": "sha512-vJ+NeiEaOVsb8YiUExtyIgvH+ZybthHszl2TASZn5q340ioKHPb2JeHGlbgrB2x8pEMh3MVhoqxAbXDuND/cnw==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-7.3.4.tgz", + "integrity": "sha512-MgjlVRklQYFfQm9yJNha9kXothLPliVdeycNdmn4lWLH3SOZe1rqJPB5Z9+dhmJELT3BJraDq3W5ik5taEpKyQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/rectangle-grid": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/standard-deviational-ellipse": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-7.3.4.tgz", + "integrity": "sha512-+BaetOKN8zA2mQCVTcRWMcfidNR3JkjmYj0r5iGRncK0J+pdxIjX2q6sF6yBMOOxMoEMy393P7j07HdBIPbibw==", + "license": "MIT", + "dependencies": { + "@turf/center-mean": "7.3.4", + "@turf/ellipse": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/points-within-polygon": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tag": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-7.3.4.tgz", + "integrity": "sha512-ienLhLzBLeChtKhbJMmU3/vGg0hWzi6Wh/q0n39W4CmdNb+yAoGQhlYjcCbPOJT4IcdFlWE3OhbP9EmH/xPgfg==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tesselate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-7.3.4.tgz", + "integrity": "sha512-NnDgVb5ZchJEhEpq1je2hktS5UhnHMfeeumxZQgnIoMeGILpJtcOL//b/1biBBUVSJ0ZZg5zxiHdQc1PgK2gxA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "earcut": "^2.2.4", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tin": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-7.3.4.tgz", + "integrity": "sha512-tuegrGlbKPp6Dm8r5SuYDtQ2EVzdXVVxelqI1agnzj9N+l8oTBIKLRxRbBkLsizeVIDnlmVHCQB6cRc3v+u8JQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-rotate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-7.3.4.tgz", + "integrity": "sha512-pbUG6QLwyJvvitq4aAq4IQH79X8T0NmEPUGDUEEP69yW7t4+UZjDBAVbCKwpOc8gtsK0K5yvxlZ0e2CdtpNmEw==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-scale": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-7.3.4.tgz", + "integrity": "sha512-7gUIFFHaU3Ewj3rCzIu5Yo7Zjfv4R2ypjh6UWiMJnDavb7RQ8fn0AKKcNMA/vF/yxuncp2l3zoa2gygv4AKM8A==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/center": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-translate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-7.3.4.tgz", + "integrity": "sha512-qbSIEueOR8mNB7p4EB88vHvUAyuSBM8zxP68UiiTNV3Gh+OZF2VXTFiu3EFYMTaD9sE6Lxmzvv3fjW8N2q82pw==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/triangle-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-7.3.4.tgz", + "integrity": "sha512-0bki10XwYvNcPzDcSs5kUh3niOogdVeFtawJEz5FdlyTAUohbNlC+Vb40K//OqEyTrGII+q1/dE4q+1J6ZCmDA==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/intersect": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/truncate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-7.3.4.tgz", + "integrity": "sha512-VPXdae9+RLLM19FMrJgt7QANBikm7DxPbfp/dXgzE4Ca7v+mJ4T1fYc7gCZDaqOrWMccHKbvv4iSuW7YZWdIIA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/turf": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-7.3.4.tgz", + "integrity": "sha512-uMAKLYt2tWJj8xIepq4vExF1r8fzJviP/5l/elDHuRyauI2mASy/Gox6kSFlrN0t0p8AT4Cs8o//4GuJTXyC+Q==", + "license": "MIT", + "dependencies": { + "@turf/along": "7.3.4", + "@turf/angle": "7.3.4", + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/bbox-clip": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/bearing": "7.3.4", + "@turf/bezier-spline": "7.3.4", + "@turf/boolean-clockwise": "7.3.4", + "@turf/boolean-concave": "7.3.4", + "@turf/boolean-contains": "7.3.4", + "@turf/boolean-crosses": "7.3.4", + "@turf/boolean-disjoint": "7.3.4", + "@turf/boolean-equal": "7.3.4", + "@turf/boolean-intersects": "7.3.4", + "@turf/boolean-overlap": "7.3.4", + "@turf/boolean-parallel": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/boolean-touches": "7.3.4", + "@turf/boolean-valid": "7.3.4", + "@turf/boolean-within": "7.3.4", + "@turf/buffer": "7.3.4", + "@turf/center": "7.3.4", + "@turf/center-mean": "7.3.4", + "@turf/center-median": "7.3.4", + "@turf/center-of-mass": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/circle": "7.3.4", + "@turf/clean-coords": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/clusters": "7.3.4", + "@turf/clusters-dbscan": "7.3.4", + "@turf/clusters-kmeans": "7.3.4", + "@turf/collect": "7.3.4", + "@turf/combine": "7.3.4", + "@turf/concave": "7.3.4", + "@turf/convex": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/difference": "7.3.4", + "@turf/dissolve": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/distance-weight": "7.3.4", + "@turf/ellipse": "7.3.4", + "@turf/envelope": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/flatten": "7.3.4", + "@turf/flip": "7.3.4", + "@turf/geojson-rbush": "7.3.4", + "@turf/great-circle": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/hex-grid": "7.3.4", + "@turf/interpolate": "7.3.4", + "@turf/intersect": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/isobands": "7.3.4", + "@turf/isolines": "7.3.4", + "@turf/kinks": "7.3.4", + "@turf/length": "7.3.4", + "@turf/line-arc": "7.3.4", + "@turf/line-chunk": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/line-offset": "7.3.4", + "@turf/line-overlap": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/line-slice": "7.3.4", + "@turf/line-slice-along": "7.3.4", + "@turf/line-split": "7.3.4", + "@turf/line-to-polygon": "7.3.4", + "@turf/mask": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/midpoint": "7.3.4", + "@turf/moran-index": "7.3.4", + "@turf/nearest-neighbor-analysis": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@turf/nearest-point-to-line": "7.3.4", + "@turf/planepoint": "7.3.4", + "@turf/point-grid": "7.3.4", + "@turf/point-on-feature": "7.3.4", + "@turf/point-to-line-distance": "7.3.4", + "@turf/point-to-polygon-distance": "7.3.4", + "@turf/points-within-polygon": "7.3.4", + "@turf/polygon-smooth": "7.3.4", + "@turf/polygon-tangents": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@turf/polygonize": "7.3.4", + "@turf/projection": "7.3.4", + "@turf/quadrat-analysis": "7.3.4", + "@turf/random": "7.3.4", + "@turf/rectangle-grid": "7.3.4", + "@turf/rewind": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@turf/sample": "7.3.4", + "@turf/sector": "7.3.4", + "@turf/shortest-path": "7.3.4", + "@turf/simplify": "7.3.4", + "@turf/square": "7.3.4", + "@turf/square-grid": "7.3.4", + "@turf/standard-deviational-ellipse": "7.3.4", + "@turf/tag": "7.3.4", + "@turf/tesselate": "7.3.4", + "@turf/tin": "7.3.4", + "@turf/transform-rotate": "7.3.4", + "@turf/transform-scale": "7.3.4", + "@turf/transform-translate": "7.3.4", + "@turf/triangle-grid": "7.3.4", + "@turf/truncate": "7.3.4", + "@turf/union": "7.3.4", + "@turf/unkink-polygon": "7.3.4", + "@turf/voronoi": "7.3.4", + "@types/geojson": "^7946.0.10", + "@types/kdbush": "^3.0.5", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/union": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-7.3.4.tgz", + "integrity": "sha512-JJYyPMmGcrTa9sPv2ief2QU9Hb//cEAU1zgKu/OfoCMa9a8Imp5QVm9UTAkhGlc+4qm/N/X16iJ+cvVWaxPjkg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/unkink-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-7.3.4.tgz", + "integrity": "sha512-dFIqTLAnLL5D3OANPJtRb5OvmOM81GlNCjwgjlLQy0xdpYgKwGdE+gNXjygDrPUUXNc22xnaj3EfAfC3Pq7W4Q==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/voronoi": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-7.3.4.tgz", + "integrity": "sha512-cwKSiDzDHRnA7yafQ1zOhWxRuMzp+fYFFzadCdByBAG1jAD7UlFwKhS1fjNPBNs67Fl5X3LL5ahCLW5gEdFgmg==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/d3-voronoi": "^1.1.12", + "@types/geojson": "^7946.0.10", + "d3-voronoi": "1.1.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@types/d3-voronoi": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.12.tgz", + "integrity": "sha512-DauBl25PKZZ0WVJr42a6CNvI6efsdzofl9sajqZr2Gf5Gu733WkDdUGiPkUHXiUvYGzNNlFQde2wdZdfQPG+yw==", + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/geokdbush": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/geokdbush/-/geokdbush-1.1.5.tgz", + "integrity": "sha512-jIsYnXY+RQ/YCyBqeEHxYN9mh+7PqKJUJUp84wLfZ7T2kqyVPNaXwZuvf1A2uQUkrvVqEbsG94ff8jH32AlLvA==", + "license": "MIT", + "dependencies": { + "@types/kdbush": "^1" + } + }, + "node_modules/@types/geokdbush/node_modules/@types/kdbush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/kdbush/-/kdbush-1.0.7.tgz", + "integrity": "sha512-QM5iB8m/0mnGOjUKshErIZQ0LseyTieRSYc3yaOpmrRM0xbWiOuJUWlduJx+TPNK7/VFMWphUGwx3nus7eT1Wg==", + "license": "MIT" + }, + "node_modules/@types/kdbush": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/kdbush/-/kdbush-3.0.5.tgz", + "integrity": "sha512-tdJz7jaWFu4nR+8b2B+CdPZ6811ighYylWsu2hpsivapzW058yP0KdfZuNY89IiRe5jbKvBGXN3LQdN2KPXVdQ==", + "license": "MIT" + }, + "node_modules/arc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/arc/-/arc-0.2.0.tgz", + "integrity": "sha512-8NFOo126uYKQJyXNSLY/jSklgfLQL+XWAcPXGo876JwEQ8nSOPXWNI3TV2jLZMN8QEw8uksJ1ZwS4npjBca8MA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/concaveman": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.1.tgz", + "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==", + "license": "ISC", + "dependencies": { + "point-in-polygon": "^1.1.0", + "rbush": "^3.0.1", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-geo": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz", + "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw==", + "license": "BSD-3-Clause" + }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "license": "ISC" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/geojson-equality-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/geojson-equality-ts/-/geojson-equality-ts-1.0.2.tgz", + "integrity": "sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.14" + } + }, + "node_modules/geojson-polygon-self-intersections": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/geojson-polygon-self-intersections/-/geojson-polygon-self-intersections-1.2.2.tgz", + "integrity": "sha512-6XRNF4CsRHYmR9z5YuIk5f/aOototnDf0dgMqYGcS7y1l57ttt6MAIAxl3rXyas6lq1HEbTuLMh4PgvO+OV42w==", + "license": "MIT", + "dependencies": { + "rbush": "^2.0.1" + } + }, + "node_modules/geojson-polygon-self-intersections/node_modules/quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==", + "license": "ISC" + }, + "node_modules/geojson-polygon-self-intersections/node_modules/rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "license": "MIT", + "dependencies": { + "quickselect": "^1.0.1" + } + }, + "node_modules/geokdbush": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/geokdbush/-/geokdbush-2.0.1.tgz", + "integrity": "sha512-0M8so1Qx6+jJ1xpirpCNrgUsWAzIcQ3LrLmh0KJPBYI3gH7vy70nY5zEEjSp9Tn0nBt6Q2Fh922oL08lfib4Zg==", + "license": "ISC", + "dependencies": { + "tinyqueue": "^2.0.3" + } + }, "node_modules/hono": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", - "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==", + "version": "4.12.8", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.8.tgz", + "integrity": "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==", "license": "MIT", "peer": true, "engines": { "node": ">=16.9.0" } }, + "node_modules/jsts": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/jsts/-/jsts-2.7.1.tgz", + "integrity": "sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==", + "license": "(EDL-1.0 OR EPL-1.0)", + "engines": { + "node": ">= 12" + } + }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", + "license": "ISC" + }, "node_modules/openapi3-ts": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.5.0.tgz", @@ -108,6 +2274,117 @@ "yaml": "^2.8.0" } }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==", + "license": "MIT" + }, + "node_modules/point-in-polygon-hao": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.2.4.tgz", + "integrity": "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==", + "license": "MIT", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/point-in-polygon-hao/node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/polyclip-ts": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/polyclip-ts/-/polyclip-ts-0.16.8.tgz", + "integrity": "sha512-JPtKbDRuPEuAjuTdhR62Gph7Is2BS1Szx69CFOO3g71lpJDFo78k4tFyi+qFOMVPePEzdSKkpGU3NBXPHHjvKQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.1.0", + "splaytree-ts": "^1.0.2" + } + }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "license": "ISC" + }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "license": "MIT", + "dependencies": { + "quickselect": "^2.0.0" + } + }, + "node_modules/robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==", + "license": "Unlicense" + }, + "node_modules/skmeans": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", + "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==", + "license": "MIT" + }, + "node_modules/splaytree-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/splaytree-ts/-/splaytree-ts-1.0.2.tgz", + "integrity": "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA==", + "license": "BDS-3-Clause" + }, + "node_modules/sweepline-intersections": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sweepline-intersections/-/sweepline-intersections-1.5.0.tgz", + "integrity": "sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==", + "license": "MIT", + "dependencies": { + "tinyqueue": "^2.0.0" + } + }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", + "license": "ISC" + }, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "license": "ISC", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson-server": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.1.tgz", + "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==", + "license": "ISC", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -123,15 +2400,18 @@ } }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/zod": { diff --git a/packages/ui/shared/package.json b/packages/ui/shared/package.json index d7dc6153..a99bcbef 100644 --- a/packages/ui/shared/package.json +++ b/packages/ui/shared/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@hono/zod-openapi": "^1.1.5", + "@turf/turf": "^7.3.4", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/packages/ui/shared/src/index.ts b/packages/ui/shared/src/index.ts index 05ae79f7..678106a1 100644 --- a/packages/ui/shared/src/index.ts +++ b/packages/ui/shared/src/index.ts @@ -2,4 +2,4 @@ export * from './ui/schemas.js'; export * from './ui/page-iterator.js'; export * from './competitors/schemas.js'; export * from './config/config.schema.js'; - +export * from './products/places/grid-generator.js'; diff --git a/packages/ui/src/modules/places/utils/grid-generator.ts b/packages/ui/shared/src/products/places/grid-generator.ts similarity index 98% rename from packages/ui/src/modules/places/utils/grid-generator.ts rename to packages/ui/shared/src/products/places/grid-generator.ts index e5073297..793cb4b4 100644 --- a/packages/ui/src/modules/places/utils/grid-generator.ts +++ b/packages/ui/shared/src/products/places/grid-generator.ts @@ -244,20 +244,20 @@ export async function generateGridSearchCells( if (cell) { // Add parent properties cell.properties = { ...props, is_center_cell: true }; - + if (onFilterCell && !onFilterCell(cell) && allowed) { allowed = false; reason = `custom filter`; } - cell.properties.sim_region_idx = i; + cell.properties!.sim_region_idx = i; if (allowed) { - cell.properties.sim_status = 'pending'; + cell.properties!.sim_status = 'pending'; validCells.push(cell); acceptedCenters.push(pt); } else { - cell.properties.sim_status = 'skipped'; - cell.properties.sim_skip_reason = reason; + cell.properties!.sim_status = 'skipped'; + cell.properties!.sim_skip_reason = reason; skippedCells.push(cell); } } diff --git a/packages/ui/src/modules/places/CompetitorsMapView.tsx b/packages/ui/src/modules/places/CompetitorsMapView.tsx index 4724459f..70842c93 100644 --- a/packages/ui/src/modules/places/CompetitorsMapView.tsx +++ b/packages/ui/src/modules/places/CompetitorsMapView.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react'; -import { Map as MapIcon, Sun, Moon, GripVertical, Maximize, Locate, Loader2 } from 'lucide-react'; +import { Map as MapIcon, Sun, Moon, GripVertical, Maximize, Locate, Loader2, LayoutGrid } from 'lucide-react'; import { type CompetitorFull } from '@polymech/shared'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; @@ -7,12 +7,32 @@ import { useTheme } from 'next-themes'; import { LocationDetailView } from './LocationDetail'; import { InfoPanel } from './InfoPanel'; import { GadmPicker } from './gadm-picker'; +import { GridSearchSimulator } from './gridsearch/simulator/GridSearchSimulator'; import { Info, Sparkles, Crosshair } from 'lucide-react'; import { useMapControls } from './hooks/useMapControls'; import { MapFooter } from './components/MapFooter'; import { MAP_STYLES, type MapStyleKey } from './components/map-styles'; +import { SimulatorLayers } from './components/map-layers/SimulatorLayers'; +import { RegionLayers } from './components/map-layers/RegionLayers'; +import { MapLayerToggles } from './components/MapLayerToggles'; // import { useLocationEnrichment } from './hooks/useEnrichment'; +const safeSetStyle = (m: maplibregl.Map, style: any) => { + const terrain = m.getTerrain(); + if (terrain) { + m.setTerrain(null); + } + m.setStyle(style); + if (terrain) { + m.once('style.load', () => { + setTimeout(() => { + try { m.setTerrain(terrain); } catch(e) {} + }, 50); + }); + } +}; + + interface CompetitorsMapViewProps { competitors: CompetitorFull[]; @@ -108,6 +128,19 @@ export const CompetitorsMapView: React.FC = ({ competit // Selection and Sidebar State const [selectedLocation, setSelectedLocation] = useState(null); const [gadmPickerActive, setGadmPickerActive] = useState(false); + + // Grid Search Simulator State + const [simulatorActive, setSimulatorActive] = useState(false); + const [pickerRegions, setPickerRegions] = useState([]); + const [pickerPolygons, setPickerPolygons] = useState([]); + const [simulatorData, setSimulatorData] = useState(null); + const [simulatorPath, setSimulatorPath] = useState(null); + const [simulatorScanner, setSimulatorScanner] = useState(null); + + // Layer Toggles + const [showDensity, setShowDensity] = useState(false); + const [showCenters, setShowCenters] = useState(false); + const [sidebarWidth, setSidebarWidth] = useState(400); const [isResizing, setIsResizing] = useState(false); @@ -253,12 +286,14 @@ export const CompetitorsMapView: React.FC = ({ competit // Sync Theme/Style useEffect(() => { if (!map.current) return; - map.current.setStyle(MAP_STYLES[mapStyleKey]); + safeSetStyle(map.current, MAP_STYLES[mapStyleKey]); // Note: Re-adding sources/layers after style switch would be needed here for production resilience, // but for now we assume style switching might reset them. // A robust solution would re-initialize layers on 'style.load'. }, [mapStyleKey]); + + // Handle Layout Resize useEffect(() => { if (map.current) { @@ -327,10 +362,13 @@ export const CompetitorsMapView: React.FC = ({ competit
+ {/* Grid Search Toggle */} + + {/* Info Button */} +
+
+ {(pickerRegions && pickerRegions.length > 0) ? ( + true} + /> + ) : ( +
+ Please select a region using the Area Selector first to define the search bounds. +
+ )} +
)} @@ -386,6 +486,12 @@ export const CompetitorsMapView: React.FC = ({ competit activeStyleKey={mapStyleKey} onStyleChange={setMapStyleKey} > + - + + - - - - - -
- - {[0.5, 1, 5, 10, 50].map(s => ( - - ))} -
- - - {/* Progress Stats */} -
-
- Scanning... - {progressIndex} / {gridCells.length} -
-
-
-
- -
-
-
- Target Calls - {validCount} -
- {gridCells.length > 0 && ( -
- - -
- )} -
-
- Skipped Calls - {skippedCount} -
-
- Simulated Processed - {processedCount} -
-
-
- - -
-
-

- Settings -

-
- - - - -
-
- -
-
- -
- - - - -
-
- -
- -
- - - - - -
- - - -
-
- -
- setMaxElevation(num)} - /> - Drop > {maxElevation}m -
-
-
- -
- setMinDensity(num)} - /> - Drop < {minDensity} -
-
- -
-
-

GHS Data Thresholds

-

Logic when filters are active.

-
-
- - -
setGhsFilterMode(ghsFilterMode === 'AND' ? 'OR' : 'AND')}> - AND - - OR -
-
-
- -
- -
- {ghsBounds.minPop > 0 ? ghsBounds.minPop.toLocaleString() : '0'} - setMinGhsPop(num)} - className="flex-1" - /> - {ghsBounds.maxPop > 0 ? ghsBounds.maxPop.toLocaleString() : 'Max'} -
-
-
- -
- {ghsBounds.minBuilt > 0 ? ghsBounds.minBuilt.toLocaleString() : '0'} - setMinGhsBuilt(num)} - className="flex-1" - /> - {ghsBounds.maxBuilt > 0 ? ghsBounds.maxBuilt.toLocaleString() : 'Max'} -
-
-
-
- -
-
-
- - setCellSize(num)} - min={0.5} - step={0.5} - disabled={gridMode === 'admin'} - /> -
- {gridMode === 'centers' ? ( -
- - setCentroidOverlap(num)} - min={0} - max={100} - step={1} - title="0% means centroids must be at least 1 cell-size apart. 100% allows exact duplicates." - /> -
- No Overlap (Dist) - Full Overlap -
-
- ) : ( -
- - setCellOverlap(num)} - min={0} - max={100} - step={1} - title="Increase to balloon the geometric cells and ensure no places are missed at the seams." - disabled={gridMode === 'admin'} - /> -
- )} -
-
- - setMaxCellsLimit(num)} - step={500} - disabled={gridMode === 'admin'} - /> -
-
-
-
- - {isCalculating && ( -
-
-
- - - Calculating... - {calcStats.total > 0 && {calcStats.current} / {calcStats.total} cells} - -
- -
-
- )} - - ); -} diff --git a/packages/ui/src/modules/places/components/MapLayerToggles.tsx b/packages/ui/src/modules/places/components/MapLayerToggles.tsx new file mode 100644 index 00000000..4a0a46f0 --- /dev/null +++ b/packages/ui/src/modules/places/components/MapLayerToggles.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Users, MapPin } from 'lucide-react'; + +export interface MapLayerTogglesProps { + showDensity?: boolean; + onToggleDensity?: (visible: boolean) => void; + showCenters?: boolean; + onToggleCenters?: (visible: boolean) => void; +} + +export function MapLayerToggles({ + showDensity, + onToggleDensity, + showCenters, + onToggleCenters +}: MapLayerTogglesProps) { + return ( + <> + {onToggleDensity && ( + + )} + {onToggleCenters && ( + + )} + + ); +} diff --git a/packages/ui/src/modules/places/components/map-layers/RegionLayers.tsx b/packages/ui/src/modules/places/components/map-layers/RegionLayers.tsx new file mode 100644 index 00000000..0d6427d8 --- /dev/null +++ b/packages/ui/src/modules/places/components/map-layers/RegionLayers.tsx @@ -0,0 +1,211 @@ +import { useEffect, useRef } from 'react'; +import maplibregl from 'maplibre-gl'; + +export interface RegionLayersProps { + map: maplibregl.Map | null; + isDarkStyle: boolean; + bboxesFeatureCollection?: any; + polygonsFeatureCollection?: any; + pickerPolygons?: any[]; + showDensity?: boolean; + showCenters?: boolean; +} + +const emptyFc = { type: 'FeatureCollection', features: [] }; + +export function RegionLayers({ + map, + isDarkStyle, + bboxesFeatureCollection, + polygonsFeatureCollection, + pickerPolygons, + showDensity = false, + showCenters = false +}: RegionLayersProps) { + + const isDarkStyleRef = useRef(isDarkStyle); + isDarkStyleRef.current = isDarkStyle; + + const showDensityRef = useRef(showDensity); + showDensityRef.current = showDensity; + + const bboxesCollectionRef = useRef(bboxesFeatureCollection); + bboxesCollectionRef.current = bboxesFeatureCollection; + + const polygonsCollectionRef = useRef(polygonsFeatureCollection); + polygonsCollectionRef.current = polygonsFeatureCollection; + + const ghsCentersDataRef = useRef(emptyFc); + + // Add Sources and Layers + useEffect(() => { + if (!map) return; + + const setupMapLayers = () => { + if (!map.getStyle()) return; + + const currentIsDark = isDarkStyleRef.current; + const currentShowDensity = showDensityRef.current; + + // Add Sources + if (!map.getSource('grid-bboxes')) map.addSource('grid-bboxes', { type: 'geojson', data: bboxesCollectionRef.current || emptyFc as any }); + if (!map.getSource('grid-polygons')) map.addSource('grid-polygons', { type: 'geojson', data: polygonsCollectionRef.current || emptyFc as any }); + if (!map.getSource('ghs-centers')) map.addSource('ghs-centers', { type: 'geojson', data: ghsCentersDataRef.current || emptyFc as any }); + + // Add Layers + if (!map.getLayer('polygons-fill')) { + map.addLayer({ + id: 'polygons-fill', type: 'fill', source: 'grid-polygons', + paint: { 'fill-color': currentIsDark ? '#3b82f6' : '#2563eb', 'fill-opacity': currentIsDark ? 0.3 : 0.5 }, + layout: { visibility: currentShowDensity ? 'none' : 'visible' } + }); + } + + if (!map.getLayer('polygons-line')) { + map.addLayer({ + id: 'polygons-line', type: 'line', source: 'grid-polygons', + paint: { 'line-color': currentIsDark ? '#2563eb' : '#1d4ed8', 'line-width': currentIsDark ? 2 : 3 }, + layout: { visibility: currentShowDensity ? 'none' : 'visible' } + }); + } + + if (!map.getLayer('bboxes-fill')) { + map.addLayer({ + id: 'bboxes-fill', type: 'fill', source: 'grid-bboxes', + paint: { 'fill-color': '#f59e0b', 'fill-opacity': 0 } + }); + } + + if (!map.getLayer('bboxes-line')) { + map.addLayer({ + id: 'bboxes-line', type: 'line', source: 'grid-bboxes', + paint: { 'line-color': '#d97706', 'line-width': 2, 'line-dasharray': [2, 2] } + }); + } + + if (!map.getLayer('density-fill')) { + map.addLayer({ + id: 'density-fill', type: 'fill', source: 'grid-bboxes', + layout: { visibility: currentShowDensity ? 'visible' : 'none' }, + paint: { + 'fill-color': [ + 'case', + ['all', ['has', 'population'], ['has', 'areaSqKm'], ['>', ['get', 'areaSqKm'], 0]], + ['interpolate', ['linear'], ['/', ['get', 'population'], ['get', 'areaSqKm']], + 0, '#f7fcf5', 50, '#c7e9c0', 200, '#74c476', 500, '#31a354', 1000, '#006d2c', 5000, '#00441b'], + '#cccccc' + ], + 'fill-opacity': 0.65 + } + }); + } + + if (!map.getLayer('ghs-centers-points')) { + map.addLayer({ + id: 'ghs-centers-points', + type: 'circle', + source: 'ghs-centers', + paint: { + 'circle-radius': ['match', ['get', 'type'], 'pop', 5, 'built', 4, 3], + 'circle-color': ['match', ['get', 'type'], 'pop', '#facc15', 'built', '#f87171', '#aaaaaa'], + 'circle-stroke-width': 1, + 'circle-stroke-color': '#000000', + 'circle-opacity': 0.8 + } + }); + } + }; + + if (map.getStyle()) setupMapLayers(); + map.on('styledata', setupMapLayers); + map.on('style.load', setupMapLayers); + + return () => { + map.off('styledata', setupMapLayers); + map.off('style.load', setupMapLayers); + if (map.getStyle()) { + if (map.getLayer('polygons-fill')) map.removeLayer('polygons-fill'); + if (map.getLayer('polygons-line')) map.removeLayer('polygons-line'); + if (map.getLayer('bboxes-fill')) map.removeLayer('bboxes-fill'); + if (map.getLayer('bboxes-line')) map.removeLayer('bboxes-line'); + if (map.getLayer('density-fill')) map.removeLayer('density-fill'); + if (map.getLayer('ghs-centers-points')) map.removeLayer('ghs-centers-points'); + if (map.getSource('grid-bboxes')) map.removeSource('grid-bboxes'); + if (map.getSource('grid-polygons')) map.removeSource('grid-polygons'); + if (map.getSource('ghs-centers')) map.removeSource('ghs-centers'); + } + }; + }, [map]); + + // Update Styles + useEffect(() => { + if (!map || !map.getStyle()) return; + + if (map.getLayer('polygons-fill')) { + map.setPaintProperty('polygons-fill', 'fill-color', isDarkStyle ? '#3b82f6' : '#2563eb'); + map.setPaintProperty('polygons-fill', 'fill-opacity', isDarkStyle ? 0.3 : 0.5); + map.setLayoutProperty('polygons-fill', 'visibility', showDensity ? 'none' : 'visible'); + } + + if (map.getLayer('polygons-line')) { + map.setPaintProperty('polygons-line', 'line-color', isDarkStyle ? '#2563eb' : '#1d4ed8'); + map.setPaintProperty('polygons-line', 'line-width', isDarkStyle ? 2 : 3); + map.setLayoutProperty('polygons-line', 'visibility', showDensity ? 'none' : 'visible'); + } + + if (map.getLayer('density-fill')) { + map.setLayoutProperty('density-fill', 'visibility', showDensity ? 'visible' : 'none'); + } + }, [map, isDarkStyle, showDensity]); + + // Update GHS Centers Source + useEffect(() => { + if (!map) return; + try { + if (showCenters && pickerPolygons && pickerPolygons.length > 0) { + const features: any[] = []; + pickerPolygons.forEach(fc => { + if (fc && fc.features) { + fc.features.forEach((f: any) => { + if (f.properties?.ghsBuiltCenter) { + features.push({ + type: 'Feature', + geometry: { type: 'Point', coordinates: f.properties.ghsBuiltCenter }, + properties: { type: 'built' } + }); + } + if (f.properties?.ghsPopCenter) { + features.push({ + type: 'Feature', + geometry: { type: 'Point', coordinates: f.properties.ghsPopCenter }, + properties: { type: 'pop' } + }); + } + }); + } + }); + const newData = { type: 'FeatureCollection', features }; + ghsCentersDataRef.current = newData; + if (map.getSource('ghs-centers')) (map.getSource('ghs-centers') as maplibregl.GeoJSONSource).setData(newData as any); + } else { + ghsCentersDataRef.current = emptyFc; + if (map.getSource('ghs-centers')) (map.getSource('ghs-centers') as maplibregl.GeoJSONSource).setData(emptyFc as any); + } + } catch (e) { + console.warn("Could not set ghs-centers", e); + } + }, [map, showCenters, pickerPolygons]); + + // Update Bboxes and Polygons Features + useEffect(() => { + if (!map) return; + try { + if (map.getSource('grid-bboxes')) (map.getSource('grid-bboxes') as maplibregl.GeoJSONSource).setData(bboxesFeatureCollection || emptyFc as any); + if (map.getSource('grid-polygons')) (map.getSource('grid-polygons') as maplibregl.GeoJSONSource).setData(polygonsFeatureCollection || emptyFc as any); + } catch (e) { + console.warn("Could not update grid-bboxes or grid-polygons", e); + } + }, [map, bboxesFeatureCollection, polygonsFeatureCollection]); + + return null; +} diff --git a/packages/ui/src/modules/places/components/map-layers/SimulatorLayers.tsx b/packages/ui/src/modules/places/components/map-layers/SimulatorLayers.tsx new file mode 100644 index 00000000..106af909 --- /dev/null +++ b/packages/ui/src/modules/places/components/map-layers/SimulatorLayers.tsx @@ -0,0 +1,152 @@ +import { useEffect, useRef } from 'react'; +import maplibregl from 'maplibre-gl'; + +export interface SimulatorLayersProps { + map: maplibregl.Map | null; + isDarkStyle: boolean; + simulatorData?: any; + simulatorPath?: any; + simulatorScanner?: any; +} + +const emptyFc = { type: 'FeatureCollection', features: [] }; + +const pacmanOpenSvg = ``; +const pacmanClosedSvg = ``; + +export function SimulatorLayers({ map, isDarkStyle, simulatorData, simulatorPath, simulatorScanner }: SimulatorLayersProps) { + + const isDarkStyleRef = useRef(isDarkStyle); + isDarkStyleRef.current = isDarkStyle; + + const simulatorDataRef = useRef(simulatorData); + simulatorDataRef.current = simulatorData; + + const simulatorPathRef = useRef(simulatorPath); + simulatorPathRef.current = simulatorPath; + + const simulatorScannerRef = useRef(simulatorScanner); + simulatorScannerRef.current = simulatorScanner; + + // Add Sources and Layers (Mount/Unmount only!) + useEffect(() => { + if (!map) return; + + const setupMapLayers = () => { + if (!map.getStyle()) return; + + const currentIsDark = isDarkStyleRef.current; + + // Load Pacman Icons + const imgOpen = new Image(30, 30); + imgOpen.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(pacmanOpenSvg); + imgOpen.onload = () => { if (!map.hasImage('pacman-open')) map.addImage('pacman-open', imgOpen); }; + + const imgClosed = new Image(30, 30); + imgClosed.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(pacmanClosedSvg); + imgClosed.onload = () => { if (!map.hasImage('pacman-closed')) map.addImage('pacman-closed', imgClosed); }; + + if (!map.getSource('simulator-grid')) { + map.addSource('simulator-grid', { type: 'geojson', data: simulatorDataRef.current || emptyFc as any }); + } + if (!map.getLayer('simulator-grid-fill')) { + map.addLayer({ + id: 'simulator-grid-fill', + type: 'fill', + source: 'simulator-grid', + paint: { + 'fill-color': [ + 'match', ['get', 'sim_status'], + 'pending', currentIsDark ? 'rgba(150, 150, 150, 0.2)' : 'rgba(0, 0, 0, 0.25)', + 'skipped', currentIsDark ? 'rgba(239, 68, 68, 0.3)' : 'rgba(220, 38, 38, 0.6)', + 'processed', currentIsDark ? 'rgba(34, 197, 94, 0.4)' : 'rgba(22, 163, 74, 0.7)', + 'rgba(0,0,0,0)' + ], + 'fill-outline-color': currentIsDark ? 'rgba(150, 150, 150, 0.5)' : 'rgba(0, 0, 0, 0.6)' + } + }); + } + + if (!map.getSource('simulator-path')) { + map.addSource('simulator-path', { type: 'geojson', data: simulatorPathRef.current || emptyFc as any }); + } + if (!map.getLayer('simulator-path-line')) { + map.addLayer({ + id: 'simulator-path-line', + type: 'line', + source: 'simulator-path', + paint: { + 'line-color': 'rgba(59, 130, 246, 0.4)', + 'line-width': 1.5, + 'line-dasharray': [3, 3] + } + }); + } + + if (!map.getSource('simulator-scanner')) { + map.addSource('simulator-scanner', { type: 'geojson', data: simulatorScannerRef.current || emptyFc as any }); + } + if (!map.getLayer('simulator-scanner-pacman')) { + map.addLayer({ + id: 'simulator-scanner-pacman', + type: 'symbol', + source: 'simulator-scanner', + layout: { + 'icon-image': ['coalesce', ['get', 'icon_state'], 'pacman-open'], + 'icon-size': 1.0, + 'icon-rotate': ['-', ['get', 'bearing'], 90], + 'icon-rotation-alignment': 'map', + 'icon-allow-overlap': true, + 'icon-ignore-placement': true + } + }); + } + }; + + if (map.getStyle()) setupMapLayers(); + map.on('styledata', setupMapLayers); + map.on('style.load', setupMapLayers); + + return () => { + map.off('styledata', setupMapLayers); + map.off('style.load', setupMapLayers); + if (map.getStyle()) { + if (map.getLayer('simulator-grid-fill')) map.removeLayer('simulator-grid-fill'); + if (map.getLayer('simulator-path-line')) map.removeLayer('simulator-path-line'); + if (map.getLayer('simulator-scanner-pacman')) map.removeLayer('simulator-scanner-pacman'); + if (map.getSource('simulator-grid')) map.removeSource('simulator-grid'); + if (map.getSource('simulator-path')) map.removeSource('simulator-path'); + if (map.getSource('simulator-scanner')) map.removeSource('simulator-scanner'); + } + }; + }, [map]); + + // Update Styles when theme changes + useEffect(() => { + if (!map || !map.getStyle()) return; + if (map.getLayer('simulator-grid-fill')) { + map.setPaintProperty('simulator-grid-fill', 'fill-color', [ + 'match', ['get', 'sim_status'], + 'pending', isDarkStyle ? 'rgba(150, 150, 150, 0.2)' : 'rgba(0, 0, 0, 0.25)', + 'skipped', isDarkStyle ? 'rgba(239, 68, 68, 0.3)' : 'rgba(220, 38, 38, 0.6)', + 'processed', isDarkStyle ? 'rgba(34, 197, 94, 0.4)' : 'rgba(22, 163, 74, 0.7)', + 'rgba(0,0,0,0)' + ]); + map.setPaintProperty('simulator-grid-fill', 'fill-outline-color', isDarkStyle ? 'rgba(150, 150, 150, 0.5)' : 'rgba(0, 0, 0, 0.6)'); + } + }, [map, isDarkStyle]); + + // Update Data + useEffect(() => { + if (!map) return; + try { + if (map.getSource('simulator-grid')) (map.getSource('simulator-grid') as maplibregl.GeoJSONSource).setData(simulatorData || emptyFc as any); + if (map.getSource('simulator-path')) (map.getSource('simulator-path') as maplibregl.GeoJSONSource).setData(simulatorPath || emptyFc as any); + if (map.getSource('simulator-scanner')) (map.getSource('simulator-scanner') as maplibregl.GeoJSONSource).setData(simulatorScanner || emptyFc as any); + } catch (e) { + console.warn("Could not update simulator data", e); + } + }, [map, simulatorData, simulatorPath, simulatorScanner]); + + return null; +} diff --git a/packages/ui/src/modules/places/gridsearch/GridSearch.tsx b/packages/ui/src/modules/places/gridsearch/GridSearch.tsx index f757dec9..51425006 100644 --- a/packages/ui/src/modules/places/gridsearch/GridSearch.tsx +++ b/packages/ui/src/modules/places/gridsearch/GridSearch.tsx @@ -4,6 +4,7 @@ import { PlusCircle } from 'lucide-react'; import { OngoingSearches } from './OngoingSearches'; import { GridSearchWizard } from './GridSearchWizard'; import { JobViewer } from './JobViewer'; +import { useAppStore } from '@/store/appStore'; export default function GridSearch() { const [sidebarWidth, setSidebarWidth] = useState(() => { @@ -11,6 +12,13 @@ export default function GridSearch() { return saved ? parseInt(saved, 10) : 320; }); const [isResizing, setIsResizing] = useState(false); + + const { setShowGlobalFooter } = useAppStore(); + + useEffect(() => { + setShowGlobalFooter(false); + return () => setShowGlobalFooter(true); + }, [setShowGlobalFooter]); const navigate = useNavigate(); const location = useLocation(); diff --git a/packages/ui/src/modules/places/gridsearch/simulator/GridSearchSimulator.tsx b/packages/ui/src/modules/places/gridsearch/simulator/GridSearchSimulator.tsx new file mode 100644 index 00000000..7a07b859 --- /dev/null +++ b/packages/ui/src/modules/places/gridsearch/simulator/GridSearchSimulator.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { Loader2 } from 'lucide-react'; +import { useGridSimulatorState } from './hooks/useGridSimulatorState'; +import { SimulatorControls } from './components/SimulatorControls'; +import { SimulatorStats } from './components/SimulatorStats'; +import { SimulatorSettingsPanel } from './components/SimulatorSettingsPanel'; +import { GridSearchSimulatorProps } from './types'; +import { T, translate } from '@/i18n'; + +export function GridSearchSimulator(props: GridSearchSimulatorProps) { + const state = useGridSimulatorState(props); + + if (!props.pickerRegions || props.pickerRegions.length === 0) return null; + + const handleCopyWaypoints = () => { + const hops = state.getFinalHopList(); + navigator.clipboard.writeText(JSON.stringify(hops, null, 2)) + .then(() => alert(translate("Waypoints copied to clipboard!"))) + .catch(err => console.error(translate("Failed to copy"), err)); + }; + + const handleExportWaypoints = () => { + const hops = state.getFinalHopList(); + const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(hops, null, 2)); + const anchor = document.createElement('a'); + anchor.href = dataStr; + anchor.download = "grid-search-waypoints.json"; + anchor.click(); + }; + + return ( +
+
+ + +
+ + + + {state.isCalculating && ( +
+
+
+ + + Calculating... + {state.calcStats.total > 0 && {state.calcStats.current} / {state.calcStats.total} cells} + +
+ +
+
+ )} +
+ ); +} diff --git a/packages/ui/src/modules/places/gridsearch/simulator/components/DeferredInputs.tsx b/packages/ui/src/modules/places/gridsearch/simulator/components/DeferredInputs.tsx new file mode 100644 index 00000000..a09c4917 --- /dev/null +++ b/packages/ui/src/modules/places/gridsearch/simulator/components/DeferredInputs.tsx @@ -0,0 +1,48 @@ +import React, { useState, useEffect } from 'react'; + +export function DeferredNumberInput({ value, onChange, ...props }: any) { + const [local, setLocal] = useState(value); + useEffect(() => { setLocal(value); }, [value]); + + const handleCommit = () => { + const num = Number(local); + if (!isNaN(num) && num !== value) onChange(num); + }; + + return ( + setLocal(e.target.value)} + onBlur={handleCommit} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.currentTarget.blur(); + } + }} + /> + ) +} + +export function DeferredRangeSlider({ value, onChange, ...props }: any) { + const [local, setLocal] = useState(value); + useEffect(() => { setLocal(value); }, [value]); + + const handleCommit = () => { + const num = Number(local); + if (!isNaN(num) && num !== value) onChange(num); + }; + + return ( + setLocal(Number(e.target.value))} + onMouseUp={handleCommit} + onTouchEnd={handleCommit} + onBlur={handleCommit} + /> + ) +} diff --git a/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorControls.tsx b/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorControls.tsx new file mode 100644 index 00000000..029b33f6 --- /dev/null +++ b/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorControls.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import { Play, Pause, Square, FastForward, Eye, Bug } from 'lucide-react'; +import { T, translate } from '@/i18n'; + +interface SimulatorControlsProps { + gridCells: any[]; + progressIndex: number; + isPlaying: boolean; + speed: number; + isCalculating: boolean; + pickerPolygons: any[]; + skippedCellsRef: React.MutableRefObject; + setIsPlaying: (p: boolean) => void; + setProgressIndex: (i: number | ((prev: number) => number)) => void; + setSpeed: (s: number) => void; + computeGrid: (autoplay: boolean | 'preview') => void; + handleClear: () => void; +} + +export function SimulatorControls({ + gridCells, progressIndex, isPlaying, speed, isCalculating, pickerPolygons, skippedCellsRef, + setIsPlaying, setProgressIndex, setSpeed, computeGrid, handleClear +}: SimulatorControlsProps) { + return ( +
+
+ + + + +
+ +
+ + {[0.5, 1, 5, 10, 50].map(s => ( + + ))} +
+
+ ); +} diff --git a/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorSettingsPanel.tsx b/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorSettingsPanel.tsx new file mode 100644 index 00000000..6e37d37a --- /dev/null +++ b/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorSettingsPanel.tsx @@ -0,0 +1,366 @@ +import React, { useRef } from 'react'; +import { Copy, Download, Upload } from 'lucide-react'; +import { DeferredNumberInput, DeferredRangeSlider } from './DeferredInputs'; +import { GridSimulatorSettings } from '../types'; +import { T, translate } from '@/i18n'; + +interface SimulatorSettingsPanelProps { + // Current State + gridMode: string; + pathOrder: string; + groupByRegion: boolean; + cellSize: number; + cellOverlap: number; + centroidOverlap: number; + ghsFilterMode: string; + maxCellsLimit: number; + maxElevation: number; + minDensity: number; + minGhsPop: number; + minGhsBuilt: number; + enableElevation: boolean; + enableDensity: boolean; + enableGhsPop: boolean; + enableGhsBuilt: boolean; + allowMissingGhs: boolean; + bypassFilters: boolean; + ghsBounds: { minPop: number; maxPop: number; minBuilt: number; maxBuilt: number; }; + + // Setters + setGridMode: (v: any) => void; + setPathOrder: (v: any) => void; + setGroupByRegion: (v: any) => void; + setCellSize: (v: any) => void; + setCellOverlap: (v: any) => void; + setCentroidOverlap: (v: any) => void; + setGhsFilterMode: (v: any) => void; + setMaxCellsLimit: (v: any) => void; + setMaxElevation: (v: any) => void; + setMinDensity: (v: any) => void; + setMinGhsPop: (v: any) => void; + setMinGhsBuilt: (v: any) => void; + setEnableElevation: (v: any) => void; + setEnableDensity: (v: any) => void; + setEnableGhsPop: (v: any) => void; + setEnableGhsBuilt: (v: any) => void; + setAllowMissingGhs: (v: any) => void; + setBypassFilters: (v: any) => void; + + // Actions + getCurrentSettings: () => GridSimulatorSettings; +} + +export function SimulatorSettingsPanel(props: SimulatorSettingsPanelProps) { + const fileInputRef = useRef(null); + + const handleCopySettings = () => { + navigator.clipboard.writeText(JSON.stringify(props.getCurrentSettings(), null, 2)) + .then(() => alert(translate("Settings copied to clipboard!"))) + .catch(err => console.error(translate("Failed to copy"), err)); + }; + + const handleExportSettings = () => { + const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(props.getCurrentSettings(), null, 2)); + const anchor = document.createElement('a'); + anchor.href = dataStr; + anchor.download = "grid-simulator-settings.json"; + anchor.click(); + }; + + const handleImportSettings = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = (evt) => { + try { + const json = JSON.parse(evt.target?.result as string); + if (json.gridMode) props.setGridMode(json.gridMode); + if (json.pathOrder) props.setPathOrder(json.pathOrder); + if (json.groupByRegion !== undefined) props.setGroupByRegion(json.groupByRegion); + if (json.cellSize !== undefined) props.setCellSize(json.cellSize); + if (json.cellOverlap !== undefined) props.setCellOverlap(json.cellOverlap); + if (json.centroidOverlap !== undefined) props.setCentroidOverlap(json.centroidOverlap); + if (json.ghsFilterMode) props.setGhsFilterMode(json.ghsFilterMode); + if (json.maxCellsLimit !== undefined) props.setMaxCellsLimit(json.maxCellsLimit); + if (json.maxElevation !== undefined) props.setMaxElevation(json.maxElevation); + if (json.minDensity !== undefined) props.setMinDensity(json.minDensity); + if (json.minGhsPop !== undefined) props.setMinGhsPop(json.minGhsPop); + if (json.minGhsBuilt !== undefined) props.setMinGhsBuilt(json.minGhsBuilt); + if (json.enableElevation !== undefined) props.setEnableElevation(json.enableElevation); + if (json.enableDensity !== undefined) props.setEnableDensity(json.enableDensity); + if (json.enableGhsPop !== undefined) props.setEnableGhsPop(json.enableGhsPop); + if (json.enableGhsBuilt !== undefined) props.setEnableGhsBuilt(json.enableGhsBuilt); + if (json.allowMissingGhs !== undefined) props.setAllowMissingGhs(json.allowMissingGhs); + if (json.bypassFilters !== undefined) props.setBypassFilters(json.bypassFilters); + } catch (err) { + alert(translate("Invalid JSON file")); + console.error(err); + } + }; + reader.readAsText(file); + + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + + return ( +
+
+

+ Settings +

+
+ + + + +
+
+ +
+
+ +
+ + + + +
+
+ +
+ +
+ + + + + +
+ + + +
+
+ +
+ props.setMaxElevation(num)} + /> + Drop > {props.maxElevation}m +
+
+
+ +
+ props.setMinDensity(num)} + /> + Drop < {props.minDensity} +
+
+ +
+
+

GHS Data Thresholds

+

Logic when filters are active.

+
+
+ + +
props.setGhsFilterMode(props.ghsFilterMode === 'AND' ? 'OR' : 'AND')}> + AND + + OR +
+
+
+ +
+ +
+ {props.ghsBounds.minPop > 0 ? props.ghsBounds.minPop.toLocaleString() : '0'} + props.setMinGhsPop(num)} + className="flex-1" + /> + {props.ghsBounds.maxPop > 0 ? props.ghsBounds.maxPop.toLocaleString() : translate('Max')} +
+
+
+ +
+ {props.ghsBounds.minBuilt > 0 ? props.ghsBounds.minBuilt.toLocaleString() : '0'} + props.setMinGhsBuilt(num)} + className="flex-1" + /> + {props.ghsBounds.maxBuilt > 0 ? props.ghsBounds.maxBuilt.toLocaleString() : translate('Max')} +
+
+
+
+ +
+
+
+ + props.setCellSize(num)} + min={0.5} + step={0.5} + disabled={props.gridMode === 'admin'} + /> +
+ {props.gridMode === 'centers' ? ( +
+ + props.setCentroidOverlap(num)} + min={0} + max={100} + step={1} + title={translate("0% means centroids must be at least 1 cell-size apart. 100% allows exact duplicates.")} + /> +
+ No Overlap (Dist) + Full Overlap +
+
+ ) : ( +
+ + props.setCellOverlap(num)} + min={0} + max={100} + step={1} + title={translate("Increase to balloon the geometric cells and ensure no places are missed at the seams.")} + disabled={props.gridMode === 'admin'} + /> +
+ )} +
+
+ + props.setMaxCellsLimit(num)} + step={500} + disabled={props.gridMode === 'admin'} + /> +
+
+
+
+ ); +} diff --git a/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorStats.tsx b/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorStats.tsx new file mode 100644 index 00000000..4b7ded0d --- /dev/null +++ b/packages/ui/src/modules/places/gridsearch/simulator/components/SimulatorStats.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { Copy, Download } from 'lucide-react'; +import { T, translate } from '@/i18n'; + +interface SimulatorStatsProps { + progressIndex: number; + validCount: number; + skippedCount: number; + processedCount: number; + totalCells: number; + handleCopyWaypoints: () => void; + handleExportWaypoints: () => void; +} + +export function SimulatorStats({ + progressIndex, validCount, skippedCount, processedCount, totalCells, handleCopyWaypoints, handleExportWaypoints +}: SimulatorStatsProps) { + return ( +
+
+ Scanning... + {progressIndex} / {totalCells} +
+
+
0 ? `${(progressIndex / totalCells) * 100}%` : '0%' }}>
+
+ +
+
+
+ Target Calls + {validCount} +
+ {totalCells > 0 && ( +
+ + +
+ )} +
+
+ Skipped Calls + {skippedCount} +
+
+ Simulated Processed + {processedCount} +
+
+
+ ); +} diff --git a/packages/ui/src/modules/places/gridsearch/simulator/hooks/useGridSimulatorState.ts b/packages/ui/src/modules/places/gridsearch/simulator/hooks/useGridSimulatorState.ts new file mode 100644 index 00000000..994a558c --- /dev/null +++ b/packages/ui/src/modules/places/gridsearch/simulator/hooks/useGridSimulatorState.ts @@ -0,0 +1,396 @@ +import { useState, useEffect, useCallback, useRef } from 'react'; +import * as turf from '@turf/turf'; +import { generateGridSearchCells } from '@polymech/shared'; +import { GridSimulatorSettings, GridSearchSimulatorProps } from '../types'; + +function useLocalStorage(key: string, initialValue: T) { + const [storedValue, setStoredValue] = useState(() => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + } catch (error) { + console.error(error); + return initialValue; + } + }); + + const setValue = (value: T | ((val: T) => T)) => { + try { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } catch (error) { + console.error(error); + } + }; + + return [storedValue, setValue] as const; +} + +export function useGridSimulatorState({ + pickerRegions, + pickerPolygons, + onFilterCell, + setSimulatorData, + setSimulatorPath, + setSimulatorScanner +}: GridSearchSimulatorProps) { + + const [gridCells, setGridCells] = useState([]); + const [progressIndex, setProgressIndex] = useState(0); + const [isPlaying, setIsPlaying] = useState(false); + const [speed, setSpeed] = useState(0.5); + const [skippedTotal, setSkippedTotal] = useState(0); + + // Config + const [gridMode, setGridMode] = useLocalStorage<'hex' | 'square' | 'admin' | 'centers'>('pm_gridMode', 'hex'); + const [pathOrder, setPathOrder] = useLocalStorage<'zigzag' | 'snake' | 'spiral-out' | 'spiral-in' | 'shortest'>('pm_pathOrder', 'snake'); + const [groupByRegion, setGroupByRegion] = useLocalStorage('pm_groupByRegion', true); + const [cellSize, setCellSize] = useLocalStorage('pm_cellSize', 2.5); + const [cellOverlap, setCellOverlap] = useLocalStorage('pm_cellOverlap', 0); + const [centroidOverlap, setCentroidOverlap] = useLocalStorage('pm_centroidOverlap', 50); + const [ghsFilterMode, setGhsFilterMode] = useLocalStorage<'AND' | 'OR'>('pm_ghsFilterMode', 'AND'); + const [maxCellsLimit, setMaxCellsLimit] = useLocalStorage('pm_maxCellsLimit', 15000); + const [maxElevation, setMaxElevation] = useLocalStorage('pm_maxElevation', 700); + const [minDensity, setMinDensity] = useLocalStorage('pm_minDensity', 10); + const [minGhsPop, setMinGhsPop] = useLocalStorage('pm_minGhsPop', 0); + const [minGhsBuilt, setMinGhsBuilt] = useLocalStorage('pm_minGhsBuilt', 0); + const [enableElevation, setEnableElevation] = useLocalStorage('pm_enElev', false); + const [enableDensity, setEnableDensity] = useLocalStorage('pm_enDens', false); + const [enableGhsPop, setEnableGhsPop] = useLocalStorage('pm_enPop', false); + const [enableGhsBuilt, setEnableGhsBuilt] = useLocalStorage('pm_enBuilt', false); + const [allowMissingGhs, setAllowMissingGhs] = useLocalStorage('pm_allowMissGhs', false); + const [bypassFilters, setBypassFilters] = useLocalStorage('pm_bypassFilters', false); + + const [isCalculating, setIsCalculating] = useState(false); + const [calcStats, setCalcStats] = useState({ current: 0, total: 0, valid: 0 }); + const skippedCellsRef = useRef([]); + + const stopRequestedRef = useRef(false); + const reqRef = useRef(); + const lastTickRef = useRef(0); + const globalProcessedHopsRef = useRef([]); + + const [ghsBounds, setGhsBounds] = useState({ minPop: 0, maxPop: 1000000, minBuilt: 0, maxBuilt: 1000000 }); + + useEffect(() => { + if (!pickerPolygons || pickerPolygons.length === 0) return; + let minPop = Infinity; + let maxPop = -Infinity; + let minBuilt = Infinity; + let maxBuilt = -Infinity; + + pickerPolygons.forEach(fc => { + if (fc && fc.features) { + fc.features.forEach((f: any) => { + const raw = f.properties || {}; + const pop = raw.ghsPopulation; + const built = raw.ghsBuiltWeight; + if (typeof pop === 'number') { + if (pop < minPop) minPop = pop; + if (pop > maxPop) maxPop = pop; + } + if (typeof built === 'number') { + if (built < minBuilt) minBuilt = built; + if (built > maxBuilt) maxBuilt = built; + } + }); + } + }); + + if (minPop !== Infinity) { + setGhsBounds({ + minPop, maxPop, minBuilt: minBuilt === Infinity ? 0 : minBuilt, maxBuilt: maxBuilt === -Infinity ? 1000000 : maxBuilt + }); + } + }, [pickerPolygons]); + + const getFinalHopList = () => { + return gridCells + .filter(c => c.properties.sim_status !== 'skipped') + .map((c, i) => { + const pt = turf.centroid(c).geometry.coordinates; + return { + step: i + 1, + lng: Number(pt[0].toFixed(6)), + lat: Number(pt[1].toFixed(6)), + radius_km: c.properties.search_radius_km ? Number(c.properties.search_radius_km.toFixed(2)) : undefined + }; + }); + }; + + const getCurrentSettings = (): GridSimulatorSettings => ({ + gridMode, pathOrder, groupByRegion, cellSize, cellOverlap, centroidOverlap, + ghsFilterMode, maxCellsLimit, maxElevation, minDensity, minGhsPop, minGhsBuilt, + enableElevation, enableDensity, enableGhsPop, enableGhsBuilt, allowMissingGhs, bypassFilters + }); + + const computeGrid = useCallback(async (autoPlay: boolean | 'preview' = false) => { + if (!pickerRegions || pickerRegions.length === 0 || !pickerPolygons) { + setGridCells([]); + setSimulatorData(turf.featureCollection([])); + setSimulatorPath(turf.featureCollection([])); + setSimulatorScanner(turf.featureCollection([])); + return; + } + + const features: any[] = []; + pickerPolygons.forEach(fc => { + if (fc && fc.features) { + features.push(...fc.features); + } + }); + + if (features.length === 0) return; + setIsCalculating(true); + stopRequestedRef.current = false; + setCalcStats({ current: 0, total: 0, valid: 0 }); + + await new Promise(r => setTimeout(r, 10)); + + try { + const result = await generateGridSearchCells({ + features, gridMode, cellSize, pathOrder, groupByRegion, onFilterCell, + maxElevation: enableElevation ? maxElevation : 0, + minDensity: enableDensity ? minDensity : 0, + minGhsPop: enableGhsPop ? minGhsPop : 0, + minGhsBuilt: enableGhsBuilt ? minGhsBuilt : 0, + allowMissingGhs, + bypassFilters, + cellOverlap: cellOverlap / 100, + centroidOverlap: centroidOverlap / 100, + ghsFilterMode, + maxCellsLimit, + skipPolygons: globalProcessedHopsRef.current + }, async (stats) => { + setCalcStats({ current: stats.current, total: stats.total, valid: stats.validCells.length }); + if (stopRequestedRef.current) return false; + await new Promise(r => setTimeout(r, 0)); + return true; + }); + + if (result.error) { + alert(result.error); + setIsCalculating(false); + return; + } + + setGridCells(result.validCells); + skippedCellsRef.current = result.skippedCells || []; + setSkippedTotal(result.skippedCells ? result.skippedCells.length : 0); + + if (autoPlay === 'preview') { + setProgressIndex(result.validCells.length); + setIsPlaying(false); + } else { + setProgressIndex(0); + setIsPlaying(stopRequestedRef.current ? false : autoPlay); + } + + setSimulatorData(turf.featureCollection(result.validCells)); + setSimulatorPath(turf.featureCollection([])); + setSimulatorScanner(turf.featureCollection([])); + setIsCalculating(false); + } catch (err) { + console.error("Turf Error:", err); + alert("An error occurred during grid generation."); + setIsCalculating(false); + } + }, [pickerRegions, pickerPolygons, gridMode, pathOrder, groupByRegion, cellSize, cellOverlap, centroidOverlap, maxCellsLimit, maxElevation, minDensity, minGhsPop, minGhsBuilt, ghsFilterMode, onFilterCell, enableElevation, enableDensity, enableGhsPop, enableGhsBuilt, allowMissingGhs, bypassFilters, setSimulatorData, setSimulatorPath, setSimulatorScanner]); + + const activeSettingsRef = useRef({ gridMode, cellSize, cellOverlap, centroidOverlap, maxElevation, minDensity, minGhsPop, minGhsBuilt, ghsFilterMode, pathOrder, groupByRegion, maxCellsLimit, enableElevation, enableDensity, enableGhsPop, enableGhsBuilt, allowMissingGhs, bypassFilters }); + + useEffect(() => { + const prev = activeSettingsRef.current; + let changed = false; + + if (prev.gridMode !== gridMode) changed = true; + if (prev.cellSize !== cellSize) changed = true; + if (prev.cellOverlap !== cellOverlap) changed = true; + if (prev.centroidOverlap !== centroidOverlap) changed = true; + if (prev.maxElevation !== maxElevation) changed = true; + if (prev.minDensity !== minDensity) changed = true; + if (prev.minGhsPop !== minGhsPop) changed = true; + if (prev.minGhsBuilt !== minGhsBuilt) changed = true; + if (prev.ghsFilterMode !== ghsFilterMode) changed = true; + if (prev.pathOrder !== pathOrder) changed = true; + if (prev.groupByRegion !== groupByRegion) changed = true; + if (prev.maxCellsLimit !== maxCellsLimit) changed = true; + if (prev.enableElevation !== enableElevation) changed = true; + if (prev.enableDensity !== enableDensity) changed = true; + if (prev.enableGhsPop !== enableGhsPop) changed = true; + if (prev.enableGhsBuilt !== enableGhsBuilt) changed = true; + if (prev.allowMissingGhs !== allowMissingGhs) changed = true; + if (prev.bypassFilters !== bypassFilters) changed = true; + + if (changed) { + globalProcessedHopsRef.current = []; + activeSettingsRef.current = { gridMode, cellSize, cellOverlap, centroidOverlap, maxElevation, minDensity, minGhsPop, minGhsBuilt, ghsFilterMode, pathOrder, groupByRegion, maxCellsLimit, enableElevation, enableDensity, enableGhsPop, enableGhsBuilt, allowMissingGhs, bypassFilters }; + if (pickerPolygons && pickerPolygons.length > 0) { + setTimeout(() => computeGrid('preview'), 0); + } + } + }, [gridMode, cellSize, cellOverlap, centroidOverlap, maxElevation, minDensity, minGhsPop, minGhsBuilt, ghsFilterMode, pathOrder, groupByRegion, maxCellsLimit, pickerPolygons, computeGrid, enableElevation, enableDensity, enableGhsPop, enableGhsBuilt, allowMissingGhs, bypassFilters]); + + const prevRegionsRef = useRef(''); + useEffect(() => { + const currentRegions = (pickerRegions || []).map(r => r.gid).sort().join(','); + if (prevRegionsRef.current !== currentRegions) { + prevRegionsRef.current = currentRegions; + setGridCells([]); + setIsPlaying(false); + setProgressIndex(0); + setSimulatorData(turf.featureCollection([])); + setSimulatorPath(turf.featureCollection([])); + setSimulatorScanner(turf.featureCollection([])); + } + }, [pickerRegions, setSimulatorData, setSimulatorPath, setSimulatorScanner]); + + const handleClear = useCallback(() => { + setGridCells([]); + setIsPlaying(false); + setProgressIndex(0); + setSimulatorData(turf.featureCollection([])); + setSimulatorPath(turf.featureCollection([])); + setSimulatorScanner(turf.featureCollection([])); + globalProcessedHopsRef.current = []; + }, [setSimulatorData, setSimulatorPath, setSimulatorScanner]); + + // Animation Loop + useEffect(() => { + if (!isPlaying || progressIndex >= gridCells.length) { + if (progressIndex >= gridCells.length && isPlaying) setIsPlaying(false); + return; + } + + const tick = (time: number) => { + if (time - lastTickRef.current > (1000 / (10 * speed))) { + setProgressIndex(prev => { + if (prev < gridCells.length) { + const cell = gridCells[prev]; + if (cell.properties.sim_status !== 'skipped') { + globalProcessedHopsRef.current.push(cell); + } + } + const next = prev + 1; + return next > gridCells.length ? gridCells.length : next; + }); + lastTickRef.current = time; + } + reqRef.current = requestAnimationFrame(tick); + }; + + reqRef.current = requestAnimationFrame(tick); + return () => { + if (reqRef.current) cancelAnimationFrame(reqRef.current); + }; + }, [isPlaying, progressIndex, gridCells.length, speed]); + + // Sync State to GeoJSON + useEffect(() => { + if (gridCells.length === 0) return; + + const updatedCells = gridCells.map((cell, i) => { + if (cell.properties.sim_status === 'skipped') return cell; + return { + ...cell, + properties: { + ...cell.properties, + sim_status: i < progressIndex ? 'processed' : 'pending' + } + }; + }); + setSimulatorData(turf.featureCollection(updatedCells)); + + const pathCoords = gridCells + .slice(0, progressIndex) + .filter(c => c.properties.sim_status !== 'skipped') + .map(c => turf.centroid(c).geometry.coordinates); + + if (pathCoords.length > 1) { + setSimulatorPath(turf.featureCollection([turf.lineString(pathCoords)])); + } else { + setSimulatorPath(turf.featureCollection([])); + } + + const activeCells = gridCells.filter(c => c.properties.sim_status !== 'skipped'); + const currentIndex = gridCells.slice(0, progressIndex).filter(c => c.properties.sim_status !== 'skipped').length; + + if (currentIndex > 0 && currentIndex <= activeCells.length) { + const currentCell = activeCells[currentIndex - 1]; + const centroid = turf.centroid(currentCell); + let angle = 0; + if (currentIndex > 1) { + const prevCell = activeCells[currentIndex - 2]; + angle = turf.bearing(turf.centroid(prevCell), centroid); + } else if (currentIndex === 1 && activeCells.length > 1) { + const nextCell = activeCells[1]; + angle = turf.bearing(centroid, turf.centroid(nextCell)); + } + centroid.properties = { + bearing: angle, + icon_state: (progressIndex % 2 === 0) ? 'pacman-open' : 'pacman-closed' + }; + setSimulatorScanner(turf.featureCollection([centroid])); + } else { + setSimulatorScanner(turf.featureCollection([])); + } + + }, [progressIndex, gridCells, setSimulatorData, setSimulatorPath, setSimulatorScanner]); + + const processedCount = progressIndex; + const skippedCount = skippedTotal; + const validCount = gridCells.length; + + return { + // State + gridCells, + progressIndex, + isPlaying, + speed, + isCalculating, + calcStats, + skippedCellsRef, + stopRequestedRef, + + // Metrics + processedCount, + skippedCount, + validCount, + ghsBounds, + + // Configuration actions + setGridMode, + setPathOrder, + setGroupByRegion, + setCellSize, + setCellOverlap, + setCentroidOverlap, + setGhsFilterMode, + setMaxCellsLimit, + setMaxElevation, + setMinDensity, + setMinGhsPop, + setMinGhsBuilt, + setEnableElevation, + setEnableDensity, + setEnableGhsPop, + setEnableGhsBuilt, + setAllowMissingGhs, + setBypassFilters, + + // Core Actions + setIsPlaying, + setProgressIndex, + setSpeed, + computeGrid, + handleClear, + getFinalHopList, + getCurrentSettings, + + // Config variables + gridMode, pathOrder, groupByRegion, cellSize, cellOverlap, centroidOverlap, + ghsFilterMode, maxCellsLimit, maxElevation, minDensity, minGhsPop, minGhsBuilt, + enableElevation, enableDensity, enableGhsPop, enableGhsBuilt, allowMissingGhs, bypassFilters + }; +} diff --git a/packages/ui/src/modules/places/gridsearch/simulator/types.ts b/packages/ui/src/modules/places/gridsearch/simulator/types.ts new file mode 100644 index 00000000..a09a1357 --- /dev/null +++ b/packages/ui/src/modules/places/gridsearch/simulator/types.ts @@ -0,0 +1,19 @@ +import { GridGeneratorOptions } from '@polymech/shared'; + +type SimulatorBaseConfig = Required>; + +export interface GridSimulatorSettings extends SimulatorBaseConfig { + enableElevation: boolean; + enableDensity: boolean; + enableGhsPop: boolean; + enableGhsBuilt: boolean; +} + +export interface GridSearchSimulatorProps { + pickerRegions: any[]; + pickerPolygons: any[]; + onFilterCell?: (cell: any) => boolean; + setSimulatorData: (data: any) => void; + setSimulatorPath: (data: any) => void; + setSimulatorScanner: (data: any) => void; +} diff --git a/packages/ui/src/modules/places/hooks/useGridSearchState.ts b/packages/ui/src/modules/places/hooks/useGridSearchState.ts index aa8c4c20..ccb3e762 100644 --- a/packages/ui/src/modules/places/hooks/useGridSearchState.ts +++ b/packages/ui/src/modules/places/hooks/useGridSearchState.ts @@ -45,21 +45,49 @@ export function useGridSearchState() { updateParams({ search: val, polygons: null }); }, [updateParams]); - const includeStats = searchParams.get('stats') !== '0'; - const showDensity = searchParams.get('density') === '1'; - const showCenters = searchParams.get('centers') === '1'; - const gadmPickerActive = searchParams.get('picker') === '1'; - const urlStyle = searchParams.get('style'); - const posterMode = searchParams.get('poster') === '1'; - const posterTheme = searchParams.get('theme') || 'terracotta'; + const getPersistentToggle = useCallback((key: string, urlValue: string | null, defaultValue: boolean): boolean => { + if (typeof window === 'undefined') return defaultValue; + if (urlValue !== null) { + localStorage.setItem(`gridsearch_${key}`, urlValue); + return urlValue === '1'; + } + const stored = localStorage.getItem(`gridsearch_${key}`); + if (stored !== null) return stored === '1'; + return defaultValue; + }, []); - const setIncludeStats = useCallback((val: boolean) => updateParam('stats', val ? null : '0'), [updateParam]); - const setShowDensity = useCallback((val: boolean) => updateParam('density', val ? '1' : null), [updateParam]); - const setShowCenters = useCallback((val: boolean) => updateParam('centers', val ? '1' : null), [updateParam]); - const setGadmPickerActive = useCallback((val: boolean) => updateParam('picker', val ? '1' : null), [updateParam]); - const setUrlStyle = useCallback((style: string) => updateParam('style', style), [updateParam]); - const setPosterMode = useCallback((val: boolean) => updateParam('poster', val ? '1' : null), [updateParam]); - const setPosterTheme = useCallback((theme: string) => updateParam('theme', theme), [updateParam]); + const getPersistentString = useCallback((key: string, urlValue: string | null): string | null => { + if (typeof window === 'undefined') return null; + if (urlValue !== null) { + localStorage.setItem(`gridsearch_${key}`, urlValue); + return urlValue; + } + return localStorage.getItem(`gridsearch_${key}`); + }, []); + + const includeStats = getPersistentToggle('stats', searchParams.get('stats'), true); + const showDensity = getPersistentToggle('density', searchParams.get('density'), false); + const showCenters = getPersistentToggle('centers', searchParams.get('centers'), false); + const gadmPickerActive = getPersistentToggle('picker', searchParams.get('picker'), false); + const urlStyle = getPersistentString('style', searchParams.get('style')); + const posterMode = getPersistentToggle('poster', searchParams.get('poster'), false); + const posterTheme = getPersistentString('theme', searchParams.get('theme')) || 'terracotta'; + + const setPersistentParam = useCallback((key: string, val: string | null) => { + if (typeof window !== 'undefined') { + if (val === null) localStorage.removeItem(`gridsearch_${key}`); + else localStorage.setItem(`gridsearch_${key}`, val); + } + updateParam(key, val); + }, [updateParam]); + + const setIncludeStats = useCallback((val: boolean) => setPersistentParam('stats', val ? '1' : '0'), [setPersistentParam]); + const setShowDensity = useCallback((val: boolean) => setPersistentParam('density', val ? '1' : '0'), [setPersistentParam]); + const setShowCenters = useCallback((val: boolean) => setPersistentParam('centers', val ? '1' : '0'), [setPersistentParam]); + const setGadmPickerActive = useCallback((val: boolean) => setPersistentParam('picker', val ? '1' : '0'), [setPersistentParam]); + const setUrlStyle = useCallback((style: string) => setPersistentParam('style', style), [setPersistentParam]); + const setPosterMode = useCallback((val: boolean) => setPersistentParam('poster', val ? '1' : '0'), [setPersistentParam]); + const setPosterTheme = useCallback((theme: string) => setPersistentParam('theme', theme), [setPersistentParam]); const [pickerRegions, setPickerRegions] = useState([]); const [pickerPolygons, setPickerPolygons] = useState([]);