From 6c4923967506fa037f112d61c7a8a2f49b0b23bb Mon Sep 17 00:00:00 2001 From: Dan Hansen Date: Fri, 30 Jan 2026 19:02:31 +0000 Subject: [PATCH] Fix add widget behavior, widget types and view settings button menu item... --- client/package-lock.json | 2505 ++++++++++++++++- client/package.json | 2 + client/public/images/NVIS_Logo_Black.png | Bin 0 -> 32979 bytes .../public/images/NVIS_Logo_transparent.png | Bin 0 -> 26531 bytes .../CanvasComponents/AlertLevelWidget.jsx | 18 +- .../Canvas/CanvasComponents/CancelButton.jsx | 2 +- .../Canvas/CanvasComponents/DialogPopup.jsx | 2 +- .../CanvasComponents/FirstWidgetButton.jsx | 9 +- .../Canvas/CanvasComponents/GasFilter.jsx | 232 ++ .../Canvas/CanvasComponents/GasWidget.jsx | 264 ++ .../CanvasComponents/MapButtonControls.jsx | 2 +- .../Canvas/CanvasComponents/MapFilter.jsx | 130 + .../Canvas/CanvasComponents/MapLegend.jsx | 49 + .../CanvasComponents/MapSettingsButton.jsx | 2 +- .../CanvasComponents/PlaceholderWidget.jsx | 33 + .../CanvasComponents/SettingsButton.jsx | 16 +- .../CanvasComponents/WidgetInspector.jsx | 529 ++++ .../Canvas/CanvasComponents/WidgetMenu.jsx | 22 +- client/src/components/Canvas/views/Admin.jsx | 32 +- .../Canvas/views/AdminTabs/MEDataEntry.jsx | 4 +- .../Canvas/views/AdminTabs/NVEWS.jsx | 236 ++ .../src/components/Canvas/views/GlobalMap.jsx | 18 +- client/src/components/Canvas/views/Home.jsx | 130 +- .../Canvas/views/HomeTabs/Deformation.jsx | 22 + .../Canvas/views/HomeTabs/MyDashboard.jsx | 45 +- .../components/Canvas/views/NVEWSLegend.jsx | 60 + .../src/components/Canvas/views/NVEWSMap.jsx | 168 ++ client/src/components/Dashboard.jsx | 151 +- client/src/components/Header/Header.jsx | 23 +- client/src/components/Sidebar/Sidebar.jsx | 2 +- client/src/components/ui/color-mode.jsx | 7 + client/src/constants/viewKeys.js | 21 +- .../src/constants/widgetInspectorRegistry.js | 97 + client/src/constants/widgetRegistry.js | 106 + client/src/theme.js | 7 +- .../utils/map-utils/functions/mapHelpers.js | 57 + .../map-utils/functions/markerHelpers.js | 63 + .../utils/map-utils/icons/black-triangle.svg | 3 + client/src/utils/map-utils/icons/camera.svg | 4 + client/src/utils/map-utils/icons/cloud.svg | 2 + .../src/utils/map-utils/icons/gps-solid.svg | 17 + .../utils/map-utils/icons/green-triangle.svg | 3 + .../utils/map-utils/icons/line-segment.svg | 4 + .../utils/map-utils/icons/red-triangle.svg | 3 + .../src/utils/map-utils/icons/sound-black.svg | 2 + .../utils/map-utils/icons/triangle-black.svg | 2 + .../utils/map-utils/icons/volcano-smoke.svg | 8 + .../utils/map-utils/icons/volcano-solid.svg | 5 + .../utils/map-utils/icons/yellow-triangle.svg | 3 + .../map-configs/nvewsMapFilterConfig.js | 44 + client/src/utils/pctStyles.js | 7 + 51 files changed, 4999 insertions(+), 174 deletions(-) create mode 100644 client/public/images/NVIS_Logo_Black.png create mode 100644 client/public/images/NVIS_Logo_transparent.png create mode 100644 client/src/components/Canvas/CanvasComponents/GasFilter.jsx create mode 100644 client/src/components/Canvas/CanvasComponents/GasWidget.jsx create mode 100644 client/src/components/Canvas/CanvasComponents/MapFilter.jsx create mode 100644 client/src/components/Canvas/CanvasComponents/MapLegend.jsx create mode 100644 client/src/components/Canvas/CanvasComponents/PlaceholderWidget.jsx create mode 100644 client/src/components/Canvas/CanvasComponents/WidgetInspector.jsx create mode 100644 client/src/components/Canvas/views/AdminTabs/NVEWS.jsx create mode 100644 client/src/components/Canvas/views/HomeTabs/Deformation.jsx create mode 100644 client/src/components/Canvas/views/NVEWSLegend.jsx create mode 100644 client/src/components/Canvas/views/NVEWSMap.jsx create mode 100644 client/src/constants/widgetInspectorRegistry.js create mode 100644 client/src/constants/widgetRegistry.js create mode 100644 client/src/utils/map-utils/functions/mapHelpers.js create mode 100644 client/src/utils/map-utils/functions/markerHelpers.js create mode 100644 client/src/utils/map-utils/icons/black-triangle.svg create mode 100644 client/src/utils/map-utils/icons/camera.svg create mode 100644 client/src/utils/map-utils/icons/cloud.svg create mode 100644 client/src/utils/map-utils/icons/gps-solid.svg create mode 100644 client/src/utils/map-utils/icons/green-triangle.svg create mode 100644 client/src/utils/map-utils/icons/line-segment.svg create mode 100644 client/src/utils/map-utils/icons/red-triangle.svg create mode 100644 client/src/utils/map-utils/icons/sound-black.svg create mode 100644 client/src/utils/map-utils/icons/triangle-black.svg create mode 100644 client/src/utils/map-utils/icons/volcano-smoke.svg create mode 100644 client/src/utils/map-utils/icons/volcano-solid.svg create mode 100644 client/src/utils/map-utils/icons/yellow-triangle.svg create mode 100644 client/src/utils/map-utils/map-configs/nvewsMapFilterConfig.js create mode 100644 client/src/utils/pctStyles.js diff --git a/client/package-lock.json b/client/package-lock.json index 794430d..2e73659 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,12 +16,14 @@ "leaflet": "^1.9.4", "lucide-react": "^0.539.0", "next-themes": "^0.4.6", + "plotly.js": "^3.3.1", "react": "^19.1.0", "react-dom": "^19.1.0", "react-grid-layout": "^1.5.2", "react-hook-form": "^7.62.0", "react-icons": "^5.5.0", "react-leaflet": "^5.0.0", + "react-plotly.js": "^2.6.0", "react-resizable": "^3.0.5", "tailwindcss": "^4.1.11", "uuid": "^11.1.0" @@ -268,6 +270,24 @@ "react-dom": ">=18" } }, + "node_modules/@choojs/findup": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz", + "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==", + "license": "MIT", + "dependencies": { + "commander": "^2.15.1" + }, + "bin": { + "findup": "bin/findup.js" + } + }, + "node_modules/@choojs/findup/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/@cush/relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@cush/relative/-/relative-1.0.0.tgz", @@ -326,6 +346,7 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -913,12 +934,12 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -926,9 +947,9 @@ } }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.15" @@ -1024,6 +1045,7 @@ "version": "3.8.2", "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz", "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==", + "peer": true, "dependencies": { "@swc/helpers": "^0.5.0" } @@ -1095,6 +1117,110 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "license": "ISC", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/geojson-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", + "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==", + "license": "ISC" + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", + "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==", + "license": "BSD-3-Clause", + "peerDependencies": { + "mapbox-gl": ">=0.32.1 <2.0.0" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz", + "integrity": "sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz", + "integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "tinyqueue": "^3.0.0" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, "node_modules/@pandacss/is-valid-prop": { "version": "0.54.0", "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-0.54.0.tgz", @@ -1110,6 +1236,92 @@ "node": ">=14" } }, + "node_modules/@plotly/d3": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@plotly/d3/-/d3-3.8.2.tgz", + "integrity": "sha512-wvsNmh1GYjyJfyEBPKJLTMzgf2c2bEbSIL50lmqVUi+o1NHaLPi1Lb4v7VxXXJn043BhNyrxUrWI85Q+zmjOVA==", + "license": "BSD-3-Clause" + }, + "node_modules/@plotly/d3-sankey": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.7.2.tgz", + "integrity": "sha512-2jdVos1N3mMp3QW0k2q1ph7Gd6j5PY1YihBrwpkFnKqO+cqtZq3AdEYUeSGXMeLsBDQYiqTVcihYfk8vr5tqhw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1", + "d3-collection": "1", + "d3-shape": "^1.2.0" + } + }, + "node_modules/@plotly/d3-sankey-circular": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey-circular/-/d3-sankey-circular-0.33.1.tgz", + "integrity": "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==", + "license": "MIT", + "dependencies": { + "d3-array": "^1.2.1", + "d3-collection": "^1.0.4", + "d3-shape": "^1.2.0", + "elementary-circuits-directed-graph": "^1.0.4" + } + }, + "node_modules/@plotly/mapbox-gl": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@plotly/mapbox-gl/-/mapbox-gl-1.13.4.tgz", + "integrity": "sha512-sR3/Pe5LqT/fhYgp4rT4aSFf1rTsxMbGiH6Hojc7PH36ny5Bn17iVFUjpzycafETURuFbLZUfjODO8LvSI+5zQ==", + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.5.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^1.1.1", + "@mapbox/unitbezier": "^0.0.0", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.2", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.2.1", + "grid-index": "^1.1.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^7.1.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.1" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/@plotly/point-cluster": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@plotly/point-cluster/-/point-cluster-3.1.9.tgz", + "integrity": "sha512-MwaI6g9scKf68Orpr1pHZ597pYx9uP8UEFXLPbsCmuw3a84obwz6pnMXGc90VhgDNeNiLEdlmuK7CPo+5PIxXw==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "binary-search-bounds": "^2.0.4", + "clamp": "^1.0.1", + "defined": "^1.0.0", + "dtype": "^2.0.0", + "flatten-vertex-data": "^1.0.2", + "is-obj": "^1.0.1", + "math-log2": "^1.0.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0" + } + }, + "node_modules/@plotly/regl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@plotly/regl/-/regl-2.1.2.tgz", + "integrity": "sha512-Mdk+vUACbQvjd0m/1JJjOOafmkp/EpmHjISsopEz5Av44CBq7rPC05HHNbYGKVyNUF2zmEoBS/TT0pd0SPFFyw==", + "license": "MIT" + }, "node_modules/@react-leaflet/core": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", @@ -1835,11 +2047,97 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@turf/area": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.3.1.tgz", + "integrity": "sha512-9nSiwt4zB5QDMcSoTxF28WpK1f741MNKcpUJDiHVRX08CZ4qfGWGV9ZIPQ8TVEn5RE4LyYkFuQ47Z9pdEUZE9Q==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@turf/meta": "7.3.1", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.3.1.tgz", + "integrity": "sha512-/IyMKoS7P9B0ch5PIlQ6gMfoE8gRr48+cSbzlyexvEjuDuaAV1VURjH1jAthS0ipFG8RrFxFJKnp7TLL1Skong==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@turf/meta": "7.3.1", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.3.1.tgz", + "integrity": "sha512-hRnsDdVBH4pX9mAjYympb2q5W8TCMUMNEjcRrAF7HTCyjIuRmjJf8vUtlzf7TTn9RXbsvPc1vtm3kLw20Jm8DQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@turf/meta": "7.3.1", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.1.tgz", + "integrity": "sha512-zkL34JVhi5XhsuMEO0MUTIIFEJ8yiW1InMu4hu/oRqamlY4mMoZql0viEmH6Dafh/p+zOl8OYvMJ3Vm3rFshgg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.3.1.tgz", + "integrity": "sha512-NWsfOE5RVtWpLQNkfOF/RrYvLRPwwruxhZUV0UFIzHqfiRJ50aO9Y6uLY4bwCUe2TumLJQSR4yaoA72Rmr2mnQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" }, + "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/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1852,16 +2150,40 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", + "license": "MIT" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "dev": true, + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1875,6 +2197,15 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@vitejs/plugin-react-swc": { "version": "3.10.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.10.2.tgz", @@ -2655,11 +2986,18 @@ "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.18.2.tgz", "integrity": "sha512-tGrG2Qnm5qf95VJEBHunrEDHO0OJZGU81FoZU2VNC+YkRmD4C1phcvyxVLklDFT569rNxIHmFn/6gr9D7HmPrQ==" }, + "node_modules/abs-svg-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", + "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==", + "license": "MIT" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2731,6 +3069,42 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-bounds": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-bounds/-/array-bounds-1.0.1.tgz", + "integrity": "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ==", + "license": "MIT" + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-normalize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.4.tgz", + "integrity": "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.0" + } + }, + "node_modules/array-range": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-range/-/array-range-1.0.1.tgz", + "integrity": "sha512-shdaI1zT3CVNL2hnx9c0JMc0ZogGaxDs5e85akgHWKYa0yVbIyp06Ind3dVkTj/uuFrzaHBOyqFzo+VV6aXgtA==", + "license": "MIT" + }, + "node_modules/array-rearrange": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/array-rearrange/-/array-rearrange-2.2.2.tgz", + "integrity": "sha512-UfobP5N12Qm4Qu4fwLDIi2v6+wZsSf6snYSxAMeKhrh37YGnNWZPRmVEKc/2wfms53TLQnzfpG8wCx2Y/6NG1w==", + "license": "MIT" + }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -2751,6 +3125,43 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/binary-search-bounds": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz", + "integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==", + "license": "MIT" + }, + "node_modules/bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==", + "license": "MIT" + }, + "node_modules/bitmap-sdf": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bitmap-sdf/-/bitmap-sdf-1.0.4.tgz", + "integrity": "sha512-1G3U4n5JE6RAiALMxu0p1XmeZkTeCwGKykzsLTCqVzfSDaN6S7fKnkIkfejogz+iwqBWc0UYAIKnKHNN7pSfDg==", + "license": "MIT" + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2761,6 +3172,12 @@ "concat-map": "0.0.1" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2769,6 +3186,15 @@ "node": ">=6" } }, + "node_modules/canvas-fit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/canvas-fit/-/canvas-fit-1.5.0.tgz", + "integrity": "sha512-onIcjRpz69/Hx5bB5HGbYKUF2uC6QT6Gp+pfpGm3A7mPfcluSLV5v4Zu+oflDUwLdUw0rLIBhUbi0v8hM4FJQQ==", + "license": "MIT", + "dependencies": { + "element-size": "^1.1.1" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2793,6 +3219,12 @@ "node": ">=18" } }, + "node_modules/clamp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", + "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==", + "license": "MIT" + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2802,6 +3234,24 @@ "node": ">=6" } }, + "node_modules/color-alpha": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/color-alpha/-/color-alpha-1.0.4.tgz", + "integrity": "sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A==", + "license": "MIT", + "dependencies": { + "color-parse": "^1.3.8" + } + }, + "node_modules/color-alpha/node_modules/color-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.4.3.tgz", + "integrity": "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2814,11 +3264,74 @@ "node": ">=7.0.0" } }, + "node_modules/color-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/color-id/-/color-id-1.1.0.tgz", + "integrity": "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g==", + "license": "MIT", + "dependencies": { + "clamp": "^1.0.1" + } + }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-normalize": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/color-normalize/-/color-normalize-1.5.0.tgz", + "integrity": "sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw==", + "license": "MIT", + "dependencies": { + "clamp": "^1.0.1", + "color-rgba": "^2.1.1", + "dtype": "^2.0.0" + } + }, + "node_modules/color-normalize/node_modules/color-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.4.3.tgz", + "integrity": "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/color-normalize/node_modules/color-rgba": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.4.0.tgz", + "integrity": "sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q==", + "license": "MIT", + "dependencies": { + "color-parse": "^1.4.2", + "color-space": "^2.0.0" + } + }, + "node_modules/color-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-2.0.0.tgz", + "integrity": "sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/color-rgba": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-3.0.0.tgz", + "integrity": "sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg==", + "license": "MIT", + "dependencies": { + "color-parse": "^2.0.0", + "color-space": "^2.0.0" + } + }, + "node_modules/color-space": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.3.2.tgz", + "integrity": "sha512-BcKnbOEsOarCwyoLstcoEztwT0IJxqqQkNwDuA3a65sICvvHL2yoeV13psoDFh5IuiOMnIOKdQDwB4Mk3BypiA==", + "license": "Unlicense" }, "node_modules/commander": { "version": "4.1.1", @@ -2835,11 +3348,32 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -2863,6 +3397,12 @@ "node": ">= 6" } }, + "node_modules/country-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz", + "integrity": "sha512-iSPlClZP8vX7MC3/u6s3lrDuoQyhQukh5LyABJ3hvfzbQ3Yyayd4fp04zjLnfi267B/B2FkumcWWgrbban7sSA==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2877,11 +3417,222 @@ "node": ">= 8" } }, + "node_modules/css-font": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-font/-/css-font-1.2.0.tgz", + "integrity": "sha512-V4U4Wps4dPDACJ4WpgofJ2RT5Yqwe1lEH6wlOOaIxMi0gTjdIijsc5FmxQlZ7ZZyKQkkutqqvULOp07l9c7ssA==", + "license": "MIT", + "dependencies": { + "css-font-size-keywords": "^1.0.0", + "css-font-stretch-keywords": "^1.0.1", + "css-font-style-keywords": "^1.0.1", + "css-font-weight-keywords": "^1.0.0", + "css-global-keywords": "^1.0.1", + "css-system-font-keywords": "^1.0.0", + "pick-by-alias": "^1.2.0", + "string-split-by": "^1.0.0", + "unquote": "^1.1.0" + } + }, + "node_modules/css-font-size-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz", + "integrity": "sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==", + "license": "MIT" + }, + "node_modules/css-font-stretch-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-stretch-keywords/-/css-font-stretch-keywords-1.0.1.tgz", + "integrity": "sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==", + "license": "MIT" + }, + "node_modules/css-font-style-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-style-keywords/-/css-font-style-keywords-1.0.1.tgz", + "integrity": "sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==", + "license": "MIT" + }, + "node_modules/css-font-weight-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-weight-keywords/-/css-font-weight-keywords-1.0.0.tgz", + "integrity": "sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==", + "license": "MIT" + }, + "node_modules/css-global-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-global-keywords/-/css-global-keywords-1.0.1.tgz", + "integrity": "sha512-X1xgQhkZ9n94WDwntqst5D/FKkmiU0GlJSFZSV3kLvyJ1WC5VeyoXDOuleUD+SIuH9C7W05is++0Woh0CGfKjQ==", + "license": "MIT" + }, + "node_modules/css-system-font-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz", + "integrity": "sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==", + "license": "MIT" + }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", + "license": "MIT" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "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-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "node_modules/d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-geo-projection": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-2.9.0.tgz", + "integrity": "sha512-ZULvK/zBn87of5rWAfFMc9mJOipeSo57O+BBitsKIXmU4rTVAnX1kSsJkE0R+TxY8pGNoM1nbyRRE7GYHhdOEQ==", + "license": "BSD-3-Clause", + "dependencies": { + "commander": "2", + "d3-array": "1", + "d3-geo": "^1.12.0", + "resolve": "^1.1.10" + }, + "bin": { + "geo2svg": "bin/geo2svg", + "geograticule": "bin/geograticule", + "geoproject": "bin/geoproject", + "geoquantize": "bin/geoquantize", + "geostitch": "bin/geostitch" + } + }, + "node_modules/d3-geo-projection/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/d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-time": "1" + } + }, + "node_modules/d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==", + "license": "BSD-3-Clause" + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -2904,6 +3655,21 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-kerning": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-kerning/-/detect-kerning-2.1.2.tgz", + "integrity": "sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -2912,18 +3678,85 @@ "node": ">=8" } }, + "node_modules/draw-svg-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/draw-svg-path/-/draw-svg-path-1.0.0.tgz", + "integrity": "sha512-P8j3IHxcgRMcY6sDzr0QvJDLzBnJJqpTG33UZ2Pvp8rw0apCHhJCWqYprqrXjrgHnJ6tuhP1iTJSAodPDHxwkg==", + "license": "MIT", + "dependencies": { + "abs-svg-path": "~0.1.1", + "normalize-svg-path": "~0.1.0" + } + }, + "node_modules/dtype": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz", + "integrity": "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/dup": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", + "integrity": "sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA==", + "license": "MIT" + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "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/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/element-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz", + "integrity": "sha512-eaN+GMOq/Q+BIWy0ybsgpcYImjGIdNLyjLFJU4XsLHXYQao5jCNb36GyN6C2qwmDDYSfIBmKpPpr4VnBdLCsPQ==", + "license": "MIT" + }, + "node_modules/elementary-circuits-directed-graph": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/elementary-circuits-directed-graph/-/elementary-circuits-directed-graph-1.3.1.tgz", + "integrity": "sha512-ZEiB5qkn2adYmpXGnJKkxT8uJHlW/mxmBpmeqawEHzPxh9HkLD4/1mFYX5l0On+f6rcPIt8/EWlRU2Vo3fX6dQ==", + "license": "MIT", + "dependencies": { + "strongly-connected-components": "^1.0.1" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.2", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", @@ -2944,6 +3777,58 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "node_modules/esbuild": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", @@ -2994,11 +3879,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint": { "version": "9.30.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.0.tgz", "integrity": "sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -3103,6 +4020,21 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -3120,6 +4052,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3148,7 +4093,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -3157,11 +4101,63 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/falafel": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.5.tgz", + "integrity": "sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==", + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "isarray": "^2.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/falafel/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3174,6 +4170,15 @@ "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==", "license": "MIT" }, + "node_modules/fast-isnumeric": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-isnumeric/-/fast-isnumeric-1.1.4.tgz", + "integrity": "sha512-1mM8qOr2LYz8zGaUdmiqRDiuue00Dxjgcb1NQR7TnhLVh6sQyngP9xvLo7Sl7LZpP/sk5eb+bcyWXw530NTBZw==", + "license": "MIT", + "dependencies": { + "is-string-blank": "^1.0.1" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3256,6 +4261,33 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "node_modules/flatten-vertex-data": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz", + "integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==", + "license": "MIT", + "dependencies": { + "dtype": "^2.0.0" + } + }, + "node_modules/font-atlas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/font-atlas/-/font-atlas-2.1.0.tgz", + "integrity": "sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==", + "license": "MIT", + "dependencies": { + "css-font": "^1.0.0" + } + }, + "node_modules/font-measure": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/font-measure/-/font-measure-1.2.2.tgz", + "integrity": "sha512-mRLEpdrWzKe9hbfaF3Qpr06TAjquuBVP5cHy4b3hyeNdjc9i0PO6HniGsX5vjL5OWv7+Bd++NiooNpT/s8BvIA==", + "license": "MIT", + "dependencies": { + "css-font": "^1.2.0" + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -3298,6 +4330,16 @@ } } }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3319,10 +4361,86 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", + "license": "ISC" + }, + "node_modules/get-canvas-context": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-canvas-context/-/get-canvas-context-1.0.2.tgz", + "integrity": "sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==", + "license": "MIT" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gl-mat4": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.2.0.tgz", + "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA==", + "license": "Zlib" + }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, + "node_modules/gl-text": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.4.0.tgz", + "integrity": "sha512-o47+XBqLCj1efmuNyCHt7/UEJmB9l66ql7pnobD6p+sgmBUdzfMZXIF0zD2+KRfpd99DJN+QXdvTFAGCKCVSmQ==", + "license": "MIT", + "dependencies": { + "bit-twiddle": "^1.0.2", + "color-normalize": "^1.5.0", + "css-font": "^1.2.0", + "detect-kerning": "^2.1.2", + "es6-weak-map": "^2.0.3", + "flatten-vertex-data": "^1.0.2", + "font-atlas": "^2.1.0", + "font-measure": "^1.2.2", + "gl-util": "^3.1.2", + "is-plain-obj": "^1.1.0", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "parse-unit": "^1.0.1", + "pick-by-alias": "^1.2.0", + "regl": "^2.0.0", + "to-px": "^1.0.1", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-util": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/gl-util/-/gl-util-3.1.3.tgz", + "integrity": "sha512-dvRTggw5MSkJnCbh74jZzSoTOGnVYK+Bt+Ckqm39CVcl6+zSsxqWk4lr5NKhkqXHL6qvZAU9h17ZF8mIskY9mA==", + "license": "MIT", + "dependencies": { + "is-browser": "^2.0.1", + "is-firefox": "^1.0.3", + "is-plain-obj": "^1.1.0", + "number-is-integer": "^1.0.1", + "object-assign": "^4.1.0", + "pick-by-alias": "^1.2.0", + "weak-map": "^1.0.5" + } + }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", @@ -3381,6 +4499,44 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/global-prefix": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", + "license": "MIT", + "dependencies": { + "ini": "^4.1.3", + "kind-of": "^6.0.3", + "which": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/global-prefix/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/globals": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", @@ -3399,11 +4555,218 @@ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", "dev": true }, + "node_modules/glsl-inject-defines": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz", + "integrity": "sha512-W49jIhuDtF6w+7wCMcClk27a2hq8znvHtlGnrYkSWEr8tHe9eA2dcnohlcAmxLYBSpSSdzOkRdyPTrx9fw49+A==", + "license": "MIT", + "dependencies": { + "glsl-token-inject-block": "^1.0.0", + "glsl-token-string": "^1.0.1", + "glsl-tokenizer": "^2.0.2" + } + }, + "node_modules/glsl-resolve": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz", + "integrity": "sha512-xxFNsfnhZTK9NBhzJjSBGX6IOqYpvBHxxmo+4vapiljyGNCY0Bekzn0firQkQrazK59c1hYxMDxYS8MDlhw4gA==", + "license": "MIT", + "dependencies": { + "resolve": "^0.6.1", + "xtend": "^2.1.2" + } + }, + "node_modules/glsl-resolve/node_modules/resolve": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", + "integrity": "sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg==", + "license": "MIT" + }, + "node_modules/glsl-resolve/node_modules/xtend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz", + "integrity": "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/glsl-token-assignments": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz", + "integrity": "sha512-OwXrxixCyHzzA0U2g4btSNAyB2Dx8XrztY5aVUCjRSh4/D0WoJn8Qdps7Xub3sz6zE73W3szLrmWtQ7QMpeHEQ==", + "license": "MIT" + }, + "node_modules/glsl-token-defines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz", + "integrity": "sha512-Vb5QMVeLjmOwvvOJuPNg3vnRlffscq2/qvIuTpMzuO/7s5kT+63iL6Dfo2FYLWbzuiycWpbC0/KV0biqFwHxaQ==", + "license": "MIT", + "dependencies": { + "glsl-tokenizer": "^2.0.0" + } + }, + "node_modules/glsl-token-depth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz", + "integrity": "sha512-eQnIBLc7vFf8axF9aoi/xW37LSWd2hCQr/3sZui8aBJnksq9C7zMeUYHVJWMhFzXrBU7fgIqni4EhXVW4/krpg==", + "license": "MIT" + }, + "node_modules/glsl-token-descope": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz", + "integrity": "sha512-kS2PTWkvi/YOeicVjXGgX5j7+8N7e56srNDEHDTVZ1dcESmbmpmgrnpjPcjxJjMxh56mSXYoFdZqb90gXkGjQw==", + "license": "MIT", + "dependencies": { + "glsl-token-assignments": "^2.0.0", + "glsl-token-depth": "^1.1.0", + "glsl-token-properties": "^1.0.0", + "glsl-token-scope": "^1.1.0" + } + }, + "node_modules/glsl-token-inject-block": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz", + "integrity": "sha512-q/m+ukdUBuHCOtLhSr0uFb/qYQr4/oKrPSdIK2C4TD+qLaJvqM9wfXIF/OOBjuSA3pUoYHurVRNao6LTVVUPWA==", + "license": "MIT" + }, + "node_modules/glsl-token-properties": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz", + "integrity": "sha512-dSeW1cOIzbuUoYH0y+nxzwK9S9O3wsjttkq5ij9ZGw0OS41BirKJzzH48VLm8qLg+au6b0sINxGC0IrGwtQUcA==", + "license": "MIT" + }, + "node_modules/glsl-token-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz", + "integrity": "sha512-YKyOMk1B/tz9BwYUdfDoHvMIYTGtVv2vbDSLh94PT4+f87z21FVdou1KNKgF+nECBTo0fJ20dpm0B1vZB1Q03A==", + "license": "MIT" + }, + "node_modules/glsl-token-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz", + "integrity": "sha512-1mtQ47Uxd47wrovl+T6RshKGkRRCYWhnELmkEcUAPALWGTFe2XZpH3r45XAwL2B6v+l0KNsCnoaZCSnhzKEksg==", + "license": "MIT" + }, + "node_modules/glsl-token-whitespace-trim": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz", + "integrity": "sha512-ZJtsPut/aDaUdLUNtmBYhaCmhIjpKNg7IgZSfX5wFReMc2vnj8zok+gB/3Quqs0TsBSX/fGnqUUYZDqyuc2xLQ==", + "license": "MIT" + }, + "node_modules/glsl-tokenizer": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz", + "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==", + "license": "MIT", + "dependencies": { + "through2": "^0.6.3" + } + }, + "node_modules/glsl-tokenizer/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/glsl-tokenizer/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/glsl-tokenizer/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/glsl-tokenizer/node_modules/through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", + "license": "MIT", + "dependencies": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/glslify": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glslify/-/glslify-7.1.1.tgz", + "integrity": "sha512-bud98CJ6kGZcP9Yxcsi7Iz647wuDz3oN+IZsjCRi5X1PI7t/xPKeL0mOwXJjo+CRZMqvq0CkSJiywCcY7kVYog==", + "license": "MIT", + "dependencies": { + "bl": "^2.2.1", + "concat-stream": "^1.5.2", + "duplexify": "^3.4.5", + "falafel": "^2.1.0", + "from2": "^2.3.0", + "glsl-resolve": "0.0.1", + "glsl-token-whitespace-trim": "^1.0.0", + "glslify-bundle": "^5.0.0", + "glslify-deps": "^1.2.5", + "minimist": "^1.2.5", + "resolve": "^1.1.5", + "stack-trace": "0.0.9", + "static-eval": "^2.0.5", + "through2": "^2.0.1", + "xtend": "^4.0.0" + }, + "bin": { + "glslify": "bin.js" + } + }, + "node_modules/glslify-bundle": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz", + "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==", + "license": "MIT", + "dependencies": { + "glsl-inject-defines": "^1.0.1", + "glsl-token-defines": "^1.0.0", + "glsl-token-depth": "^1.1.1", + "glsl-token-descope": "^1.0.2", + "glsl-token-scope": "^1.1.1", + "glsl-token-string": "^1.0.1", + "glsl-token-whitespace-trim": "^1.0.0", + "glsl-tokenizer": "^2.0.2", + "murmurhash-js": "^1.0.0", + "shallow-copy": "0.0.1" + } + }, + "node_modules/glslify-deps": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.2.tgz", + "integrity": "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag==", + "license": "ISC", + "dependencies": { + "@choojs/findup": "^0.2.0", + "events": "^3.2.0", + "glsl-resolve": "0.0.1", + "glsl-tokenizer": "^2.0.0", + "graceful-fs": "^4.1.2", + "inherits": "^2.0.1", + "map-limit": "0.0.1", + "resolve": "^1.0.0" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", + "license": "ISC" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3413,6 +4776,24 @@ "node": ">=8" } }, + "node_modules/has-hover": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-hover/-/has-hover-1.0.1.tgz", + "integrity": "sha512-0G6w7LnlcpyDzpeGUTuT0CEw05+QlMuGVk1IHNAlHrGJITGodjZu3x8BNDUMfKJSZXNB2ZAclqc1bvrd+uUpfg==", + "license": "MIT", + "dependencies": { + "is-browser": "^2.0.1" + } + }, + "node_modules/has-passive-events": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-passive-events/-/has-passive-events-1.0.0.tgz", + "integrity": "sha512-2vSj6IeIsgvsRMyeQ0JaCX5Q3lX4zMn5HpoVc7MEhQ6pv8Iq9rsXjsp+E5ZwaT7T0xhMT0KmU8gtt1EFVdbJiw==", + "license": "MIT", + "dependencies": { + "is-browser": "^2.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -3432,6 +4813,38 @@ "react-is": "^16.7.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3465,11 +4878,32 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, + "node_modules/is-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.1.0.tgz", + "integrity": "sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -3493,6 +4927,27 @@ "node": ">=0.10.0" } }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-firefox": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-firefox/-/is-firefox-1.0.3.tgz", + "integrity": "sha512-6Q9ITjvWIm0Xdqv+5U12wgOKEM2KoBw4Y926m0OFkvlCxnbG94HKAsVz8w3fWcfAS5YA2fJORXX1dLrkprCCxA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3514,6 +4969,57 @@ "node": ">=0.10.0" } }, + "node_modules/is-iexplorer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-iexplorer/-/is-iexplorer-1.0.0.tgz", + "integrity": "sha512-YeLzceuwg3K6O0MLM3UyUUjKAlyULetwryFp1mHy1I5PfArK0AEqlfa+MR4gkJjcbuJXoDJCvXbyqZVf5CR2Sg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-mobile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-4.0.0.tgz", + "integrity": "sha512-mlcHZA84t1qLSuWkt2v0I2l61PYdyQDt4aG1mLIXF5FDMm4+haBCxCPYSr/uwqQNRk1MiTizn0ypEuRAOLRAew==", + "license": "MIT" + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string-blank": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", + "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==", + "license": "MIT" + }, + "node_modules/is-svg-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-svg-path/-/is-svg-path-1.0.2.tgz", + "integrity": "sha512-Lj4vePmqpPR1ZnRctHv8ltSh1OrSxHkhUkd7wi+VQdcdP15/KvQFyk7LhNuM7ZW0EVbJz8kZLVmL9quLrfq4Kg==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3549,9 +5055,9 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "dependencies": { "argparse": "^2.0.1" @@ -3594,6 +5100,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "license": "MIT" + }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -3606,6 +5118,12 @@ "json5": "lib/cli.js" } }, + "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/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3615,11 +5133,21 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/levn": { "version": "0.4.1", @@ -3874,8 +5402,7 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -3912,6 +5439,159 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/map-limit": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz", + "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==", + "license": "MIT", + "dependencies": { + "once": "~1.3.0" + } + }, + "node_modules/map-limit/node_modules/once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/mapbox-gl": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz", + "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==", + "license": "SEE LICENSE IN LICENSE.txt", + "peer": true, + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.5.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^1.1.1", + "@mapbox/unitbezier": "^0.0.0", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.2", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.2.1", + "grid-index": "^1.1.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^7.1.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.1" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/maplibre-gl": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz", + "integrity": "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/maplibre-gl-style-spec": "^20.3.1", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "3.2.5", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "earcut": "^3.0.0", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.3", + "global-prefix": "^4.0.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.3.0", + "potpack": "^2.0.0", + "quickselect": "^3.0.0", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0", + "vt-pbf": "^3.1.3" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, + "node_modules/maplibre-gl/node_modules/@mapbox/tiny-sdf": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz", + "integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==", + "license": "BSD-2-Clause" + }, + "node_modules/maplibre-gl/node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/maplibre-gl/node_modules/earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/geojson-vt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/potpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/maplibre-gl/node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, + "node_modules/math-log2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz", + "integrity": "sha512-9W0yGtkaMAkf74XGYVy4Dqw3YUMnTNB2eeiw9aQbUl4A3KmuCEHTt2DgAB07ENzOYAjsYSAYufkAq0Zd+jU7zA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3928,7 +5608,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3979,11 +5658,49 @@ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.19.0.tgz", "integrity": "sha512-BuFTHINYmV07pdWs6lj6aI63vr2N4dg0vR+td0rtrdpWOhBzIkEklZyLcvKBoEtwSqx8Jg06vUB5RS0xDiUybw==" }, + "node_modules/mouse-change": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz", + "integrity": "sha512-vpN0s+zLL2ykyyUDh+fayu9Xkor5v/zRD9jhSqjRS1cJTGS0+oakVZzNm5n19JvvEj0you+MXlYTpNxUDQUjkQ==", + "license": "MIT", + "dependencies": { + "mouse-event": "^1.0.0" + } + }, + "node_modules/mouse-event": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/mouse-event/-/mouse-event-1.0.5.tgz", + "integrity": "sha512-ItUxtL2IkeSKSp9cyaX2JLUuKk2uMoxBg4bbOWVd29+CskYJR9BGsUqtXenNzKbnDshvupjUewDIYVrOB6NmGw==", + "license": "MIT" + }, + "node_modules/mouse-event-offset": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mouse-event-offset/-/mouse-event-offset-3.0.2.tgz", + "integrity": "sha512-s9sqOs5B1Ykox3Xo8b3Ss2IQju4UwlW6LSR+Q5FXWpprJ5fzMLefIIItr3PH8RwzfGy6gxs/4GAmiNuZScE25w==", + "license": "MIT" + }, + "node_modules/mouse-wheel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mouse-wheel/-/mouse-wheel-1.2.0.tgz", + "integrity": "sha512-+OfYBiUOCTWcTECES49neZwL5AoGkXE+lFjIvzwNCnYRlso+EnfvovcBxGoyQ0yQt806eSPjS675K0EwWknXmw==", + "license": "MIT", + "dependencies": { + "right-now": "^1.0.0", + "signum": "^1.0.0", + "to-px": "^1.0.1" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -4012,12 +5729,44 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==", + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -4027,6 +5776,30 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, + "node_modules/normalize-svg-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz", + "integrity": "sha512-1/kmYej2iedi5+ROxkRESL/pI02pkg0OBnaR4hJkSIX6+ORzepwbuUXfrdZaPjysTsJInj0Rj5NuX027+dMBvA==", + "license": "MIT" + }, + "node_modules/number-is-integer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-integer/-/number-is-integer-1.0.1.tgz", + "integrity": "sha512-Dq3iuiFBkrbmuQjGFFF3zckXNCQoSD37/SdSbgcBailUx6knDvDwb5CympBgcoWHy36sfS12u74MHYkXyHq6bg==", + "license": "MIT", + "dependencies": { + "is-finite": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4035,6 +5808,15 @@ "node": ">=0.10.0" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4099,6 +5881,12 @@ "node": ">=6" } }, + "node_modules/parenthesis": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz", + "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==", + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -4116,6 +5904,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-rect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parse-rect/-/parse-rect-1.2.0.tgz", + "integrity": "sha512-4QZ6KYbnE6RTwg9E0HpLchUM9EZt6DnDxajFZZDSV4p/12ZJEvPO702DZpGvRYEPo00yKDys7jASi+/w7aO8LA==", + "license": "MIT", + "dependencies": { + "pick-by-alias": "^1.2.0" + } + }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==", + "license": "MIT" + }, + "node_modules/parse-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz", + "integrity": "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4163,11 +5972,36 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/perfect-freehand": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.2.tgz", "integrity": "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT" + }, + "node_modules/pick-by-alias": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pick-by-alias/-/pick-by-alias-1.2.0.tgz", + "integrity": "sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4177,6 +6011,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "peer": true, "engines": { "node": ">=12" }, @@ -4193,6 +6028,80 @@ "node": ">= 6" } }, + "node_modules/plotly.js": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-3.3.1.tgz", + "integrity": "sha512-SrGSZ02HvCWQIYsbQX4sgjgGo7k4T+Oz8a+RQwE5Caz+yu1vputBM1UThmiOPY51B5HzO9ajym8Rl4pmLj+i9Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@plotly/d3": "3.8.2", + "@plotly/d3-sankey": "0.7.2", + "@plotly/d3-sankey-circular": "0.33.1", + "@plotly/mapbox-gl": "1.13.4", + "@plotly/regl": "^2.1.2", + "@turf/area": "^7.1.0", + "@turf/bbox": "^7.1.0", + "@turf/centroid": "^7.1.0", + "base64-arraybuffer": "^1.0.2", + "canvas-fit": "^1.5.0", + "color-alpha": "1.0.4", + "color-normalize": "1.5.0", + "color-parse": "2.0.0", + "color-rgba": "3.0.0", + "country-regex": "^1.1.0", + "d3-force": "^1.2.1", + "d3-format": "^1.4.5", + "d3-geo": "^1.12.1", + "d3-geo-projection": "^2.9.0", + "d3-hierarchy": "^1.1.9", + "d3-interpolate": "^3.0.1", + "d3-time": "^1.1.0", + "d3-time-format": "^2.2.3", + "fast-isnumeric": "^1.1.4", + "gl-mat4": "^1.2.0", + "gl-text": "^1.4.0", + "has-hover": "^1.0.1", + "has-passive-events": "^1.0.0", + "is-mobile": "^4.0.0", + "maplibre-gl": "^4.7.1", + "mouse-change": "^1.4.0", + "mouse-event-offset": "^3.0.2", + "mouse-wheel": "^1.2.0", + "native-promise-only": "^0.8.1", + "parse-svg-path": "^0.1.2", + "point-in-polygon": "^1.1.0", + "polybooljs": "^1.2.2", + "probe-image-size": "^7.2.3", + "regl-error2d": "^2.0.12", + "regl-line2d": "^3.1.3", + "regl-scatter2d": "^3.3.1", + "regl-splom": "^1.0.14", + "strongly-connected-components": "^1.0.1", + "superscript-text": "^1.0.0", + "svg-path-sdf": "^1.1.3", + "tinycolor2": "^1.4.2", + "to-px": "1.0.1", + "topojson-client": "^3.1.0", + "webgl-context": "^2.2.0", + "world-calendars": "^1.0.4" + }, + "engines": { + "node": ">=18.0.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/polybooljs": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/polybooljs/-/polybooljs-1.2.2.tgz", + "integrity": "sha512-ziHW/02J0XuNuUtmidBc6GXE8YohYydp3DWPWXYsd7O721TjcmN+k6ezjdwkDqep+gnWnFY+yqZHvzElra2oCg==", + "license": "MIT" + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -4220,6 +6129,12 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "license": "ISC" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4229,6 +6144,23 @@ "node": ">= 0.8.0" } }, + "node_modules/probe-image-size": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz", + "integrity": "sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==", + "license": "MIT", + "dependencies": { + "lodash.merge": "^4.6.2", + "needle": "^2.5.2", + "stream-parser": "~0.3.1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4240,6 +6172,12 @@ "react-is": "^16.13.1" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "license": "MIT" + }, "node_modules/proxy-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.1.tgz", @@ -4262,10 +6200,26 @@ "node": ">=6" } }, + "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/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4274,6 +6228,7 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -4357,6 +6312,19 @@ "react-dom": "^19.0.0" } }, + "node_modules/react-plotly.js": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-plotly.js/-/react-plotly.js-2.6.0.tgz", + "integrity": "sha512-g93xcyhAVCSt9kV1svqG1clAEdL6k3U+jjuSzfTV7owaSU9Go6Ph8bl25J+jKfKvIGAEYpe4qj++WHJuc9IaeA==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "plotly.js": ">1.34.0", + "react": ">0.13.0" + } + }, "node_modules/react-resizable": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.5.tgz", @@ -4370,6 +6338,33 @@ "react": ">= 16.3" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/recrawl-sync": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recrawl-sync/-/recrawl-sync-2.2.3.tgz", @@ -4389,6 +6384,104 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/regl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/regl/-/regl-2.1.1.tgz", + "integrity": "sha512-+IOGrxl3FZ8ZM9ixCWQZzFRiRn7Rzn9bu3iFHwg/yz4tlOUQgbO4PHLgG+1ZT60zcIV8tief6Qrmyl8qcoJP0g==", + "license": "MIT" + }, + "node_modules/regl-error2d": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/regl-error2d/-/regl-error2d-2.0.12.tgz", + "integrity": "sha512-r7BUprZoPO9AbyqM5qlJesrSRkl+hZnVKWKsVp7YhOl/3RIpi4UDGASGJY0puQ96u5fBYw/OlqV24IGcgJ0McA==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "color-normalize": "^1.5.0", + "flatten-vertex-data": "^1.0.2", + "object-assign": "^4.1.1", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0", + "update-diff": "^1.1.0" + } + }, + "node_modules/regl-line2d": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.1.3.tgz", + "integrity": "sha512-fkgzW+tTn4QUQLpFKsUIE0sgWdCmXAM3ctXcCgoGBZTSX5FE2A0M7aynz7nrZT5baaftLrk9te54B+MEq4QcSA==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "array-find-index": "^1.0.2", + "array-normalize": "^1.1.4", + "color-normalize": "^1.5.0", + "earcut": "^2.1.5", + "es6-weak-map": "^2.0.3", + "flatten-vertex-data": "^1.0.2", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0" + } + }, + "node_modules/regl-scatter2d": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.3.1.tgz", + "integrity": "sha512-seOmMIVwaCwemSYz/y4WE0dbSO9svNFSqtTh5RE57I7PjGo3tcUYKtH0MTSoshcAsreoqN8HoCtnn8wfHXXfKQ==", + "license": "MIT", + "dependencies": { + "@plotly/point-cluster": "^3.1.9", + "array-range": "^1.0.1", + "array-rearrange": "^2.2.2", + "clamp": "^1.0.1", + "color-id": "^1.1.0", + "color-normalize": "^1.5.0", + "color-rgba": "^2.1.1", + "flatten-vertex-data": "^1.0.2", + "glslify": "^7.0.0", + "is-iexplorer": "^1.0.0", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0", + "update-diff": "^1.1.0" + } + }, + "node_modules/regl-scatter2d/node_modules/color-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.4.3.tgz", + "integrity": "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/regl-scatter2d/node_modules/color-rgba": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.4.0.tgz", + "integrity": "sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q==", + "license": "MIT", + "dependencies": { + "color-parse": "^1.4.2", + "color-space": "^2.0.0" + } + }, + "node_modules/regl-splom": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.14.tgz", + "integrity": "sha512-OiLqjmPRYbd7kDlHC6/zDf6L8lxgDC65BhC8JirhP4ykrK4x22ZyS+BnY8EUinXKDeMgmpRwCvUmk7BK4Nweuw==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "array-range": "^1.0.1", + "color-alpha": "^1.0.4", + "flatten-vertex-data": "^1.0.2", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "raf": "^3.4.1", + "regl-scatter2d": "^3.2.3" + } + }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -4422,6 +6515,21 @@ "node": ">=4" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/right-now": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz", + "integrity": "sha512-DA8+YS+sMIVpbsuKgy+Z67L9Lxb1p05mNxRpDPNksPDEFir4vmBlUtuN9jkTGn9YMMdlBuK7XQgFiz6ws+yhSg==", + "license": "MIT" + }, "node_modules/rollup": { "version": "4.44.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", @@ -4460,11 +6568,55 @@ "fsevents": "~2.3.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", + "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", + "license": "BlueOak-1.0.0" + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4498,6 +6650,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/signum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", + "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==", + "license": "MIT" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4523,6 +6681,77 @@ "node": ">=0.10.0" } }, + "node_modules/stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==", + "engines": { + "node": "*" + } + }, + "node_modules/static-eval": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.1.tgz", + "integrity": "sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==", + "license": "MIT", + "dependencies": { + "escodegen": "^2.1.0" + } + }, + "node_modules/stream-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", + "integrity": "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==", + "license": "MIT", + "dependencies": { + "debug": "2" + } + }, + "node_modules/stream-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/stream-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/string-split-by": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz", + "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==", + "license": "MIT", + "dependencies": { + "parenthesis": "^3.1.5" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4640,6 +6869,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strongly-connected-components": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", + "integrity": "sha512-i0TFx4wPcO0FwX+4RkLJi1MxmcTv90jNZgxMu9XRnMXMeFUY1VJlIoXpZunPUvUUqbCT1pg5PEkFqqpcaElNaA==", + "license": "MIT" + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -4667,6 +6902,27 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supercluster": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", + "license": "ISC", + "dependencies": { + "kdbush": "^3.0.0" + } + }, + "node_modules/supercluster/node_modules/kdbush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==", + "license": "ISC" + }, + "node_modules/superscript-text": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/superscript-text/-/superscript-text-1.0.0.tgz", + "integrity": "sha512-gwu8l5MtRZ6koO0icVTlmN5pm7Dhh1+Xpe9O4x6ObMAsW+3jPbW14d1DsBq1F4wiI+WOFjXF35pslgec/G8yCQ==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4690,6 +6946,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-arc-to-cubic-bezier": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz", + "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==", + "license": "ISC" + }, + "node_modules/svg-path-bounds": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/svg-path-bounds/-/svg-path-bounds-1.0.2.tgz", + "integrity": "sha512-H4/uAgLWrppIC0kHsb2/dWUYSmb4GE5UqH06uqWBcg6LBjX2fu0A8+JrO2/FJPZiSsNOKZAhyFFgsLTdYUvSqQ==", + "license": "MIT", + "dependencies": { + "abs-svg-path": "^0.1.1", + "is-svg-path": "^1.0.1", + "normalize-svg-path": "^1.0.0", + "parse-svg-path": "^0.1.2" + } + }, + "node_modules/svg-path-bounds/node_modules/normalize-svg-path": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", + "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", + "license": "MIT", + "dependencies": { + "svg-arc-to-cubic-bezier": "^3.0.0" + } + }, + "node_modules/svg-path-sdf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/svg-path-sdf/-/svg-path-sdf-1.1.3.tgz", + "integrity": "sha512-vJJjVq/R5lSr2KLfVXVAStktfcfa1pNFjFOgyJnzZFXlO/fDZ5DmM8FpnSKKzLPfEYTVeXuVBTHF296TpxuJVg==", + "license": "MIT", + "dependencies": { + "bitmap-sdf": "^1.0.0", + "draw-svg-path": "^1.0.0", + "is-svg-path": "^1.0.1", + "parse-svg-path": "^0.1.2", + "svg-path-bounds": "^1.0.1" + } + }, "node_modules/tailwindcss": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", @@ -4740,6 +7036,22 @@ "node": ">=0.8" } }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", @@ -4755,6 +7067,47 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "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/to-float32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/to-float32/-/to-float32-1.1.0.tgz", + "integrity": "sha512-keDnAusn/vc+R3iEiSDw8TOF7gPiTLdK1ArvWtYbJQiVfmRg6i/CAvbKq3uIS0vWroAC7ZecN3DjQKw3aSklUg==", + "license": "MIT" + }, + "node_modules/to-px": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.0.1.tgz", + "integrity": "sha512-2y3LjBeIZYL19e5gczp14/uRWFDtDUErJPVN3VU9a7SJO+RjGRtYR47aMN2bZgGlxvW4ZcEz2ddUPVHXcMfuXw==", + "license": "MIT", + "dependencies": { + "parse-unit": "^1.0.1" + } + }, + "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-client/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/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -4778,6 +7131,12 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4790,6 +7149,34 @@ "node": ">= 0.8.0" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typedarray-pool": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.2.0.tgz", + "integrity": "sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ==", + "license": "MIT", + "dependencies": { + "bit-twiddle": "^1.0.0", + "dup": "^1.0.0" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", + "license": "MIT" + }, + "node_modules/update-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-diff/-/update-diff-1.1.0.tgz", + "integrity": "sha512-rCiBPiHxZwT4+sBhEbChzpO5hYHjm91kScWgdHf4Qeafs6Ba7MBl+d9GlGv72bcTZQO0sLmtQS1pHSWoCLtN/A==", + "license": "MIT" + }, "node_modules/uqr": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", @@ -4804,6 +7191,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -4818,9 +7211,10 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -4905,6 +7299,32 @@ "vite": ">2.0.0-0" } }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, + "node_modules/weak-map": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.8.tgz", + "integrity": "sha512-lNR9aAefbGPpHO7AEnY0hCFjz1eTkWCXYvkTRrTHs9qv8zJp+SkVYpzfLIFXQQiG3tVvbNFQgVg2bQS8YGgxyw==", + "license": "Apache-2.0" + }, + "node_modules/webgl-context": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz", + "integrity": "sha512-q/fGIivtqTT7PEoF07axFIlHNk/XCPaYpq64btnepopSWvKNFkoORlQYgqDigBIuGA1ExnFd/GnSUnBNEPQY7Q==", + "license": "MIT", + "dependencies": { + "get-canvas-context": "^1.0.1" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4929,6 +7349,15 @@ "node": ">=0.10.0" } }, + "node_modules/world-calendars": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/world-calendars/-/world-calendars-1.0.4.tgz", + "integrity": "sha512-VGRnLJS+xJmGDPodgJRnGIDwGu0s+Cr9V2HB3EzlDZ5n0qb8h5SJtGUEkjrphZYAglEiXZ6kiXdmk0H/h/uu/w==", + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -5017,6 +7446,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -5025,19 +7469,6 @@ "node": ">=18" } }, - "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/client/package.json b/client/package.json index 5185486..024c603 100644 --- a/client/package.json +++ b/client/package.json @@ -18,12 +18,14 @@ "leaflet": "^1.9.4", "lucide-react": "^0.539.0", "next-themes": "^0.4.6", + "plotly.js": "^3.3.1", "react": "^19.1.0", "react-dom": "^19.1.0", "react-grid-layout": "^1.5.2", "react-hook-form": "^7.62.0", "react-icons": "^5.5.0", "react-leaflet": "^5.0.0", + "react-plotly.js": "^2.6.0", "react-resizable": "^3.0.5", "tailwindcss": "^4.1.11", "uuid": "^11.1.0" diff --git a/client/public/images/NVIS_Logo_Black.png b/client/public/images/NVIS_Logo_Black.png new file mode 100644 index 0000000000000000000000000000000000000000..c420ba776f6d4d92f1c9651e48c7ae12191e1211 GIT binary patch literal 32979 zcmYIPby!v1(m(W}`_SD;cS?5)(%l`>-Cfck4U*EG(jd}}NSAa-Bk^tT``-KgK^&gV ze%9V=X3hL!qE(e;P>~3cKp+sRoUG(K5D2;-_`L-T4}70{drSxXKy;SXaRq_!F#mo* zeW$$=0fERta*|>iUO6YZ-uiLJM8mZTCDfR@smZwzlFn})Cw8}0n2}K@ z=E=Fq0+f_GZ!F%#oTTKYT0cB*?Uvb8X6<`h9X|xT^`AVv`FiK7@^kh4*>K9eF;zo@ zB}BYXl^zEvT)e`x4)`2HoeLZO@1H0gaj0`a|NDe!O`-VTYm=wNGylEn9Zg|4+rJl; z$crPJ0QdTPnS5Fpr2p@Yfe)#Sh|FOO|9*InMI%Qi`tO}E<2Tnjw~n^dj=bD7sMmX5 zRkyXJrQ!30eP8}ug?)wv^|m_k%POf@@u)F`{_lPtVsb;xN@|ezoPpOC9ew}%0eP0R zj6zV52yv8wdM<^}t%3NTRm2406XJ_XNIc*Da%$7@8rnm0t1j&z)a9~+2U$BBgP5xc zdtR%3?;QPV+r2KaKpGtw+}u2j?k7{tUzWSk2>Jud3IT}8!| zE?4`^`keu&a-$5;c-tZG^y}D9U>dYe2at38PaKFR<8@`SWKTLvGH z*-H{Oh&xTMX8*$ZDHfBW+a) zr@4cYk~#dLc1yJsJS=EG_}j(5u2drbZ!f&?IqmtNLtCg~$HF^1iP~1HICzt7D!=rt zd`BWZBkkd>n#kfte#z#=p8C?JXScGBv)1Xk^RwdC%~RqaI%RMvUFLs5(GOD_N0BV@4w*rr$R-xr}OD z3k#qiCknmQJxCZ63m*HgDJlwfOjaYIk>YE&ueY`l@bJcs?j}}dvA%a_-}UJ}{WpGg zV=Ugj&HS}<^~cqz)`imHg3-h9W2!s8$gfD#uu-|h32B2C$BLaH7OdgnVe9qPg4uLC z9etLg<72HOT{*G;hWLkpd;=BW22oKl)Mu5wTt4i;B`lUgWDv5 z;oL-+hv+muetNVBJ0~Ys;n2_SX#Vqm&!Cv9;)F!h@4DTH5-ukCLT^7E z-h3wb-)ngD!W0o~G*t3BjOy21KNQXuZDZfp>G}HdD&pP&Wc;woCvC^2g^orVadEWK zYiSW3Ni{J!o9NFe1^*ls6hn#m4}4w+7=|Lg3F|B@)^_G$^=91#DCD#}ErabFZv!Lz zr}FUGtT85Y-i9x&R>{};KFwEbkP$sC?Kr-}>To+nLRL^PFBK8%&lR*V%zk)+`|5We z1>DN(Q<55^3KA#1tN_%%=P&JYdm!2M%}xDLPrTJqjnNzTgxCFvi(A4-AOs}Wa2i}c zVESxRudc0uKs((dT3!=hmRO-cD=Xa5dtwY^jPd`hn)2?T{)aef*8SWMsC$btkAsUX zdDKdb!C9rmiQx$%-hwep>?9ji*#AGA1A7IF+B1QKKlC1%p?EQ0$*t@S9Be)}trZ7|5u)1M7sZEZvwSxQGTkLL2>3jth^SBT_&=SBX%OMP$R;dDI%e5Jhw%Pxks2U88 zLnE(wHJS}qdhEdCXt&ruoF6x~>7T6BOYA0n!xi z8W+DlZb%56QPcK2of76m#}s`sB#8KLf>%-lH{`>Gpas1~Sp@vDaB0tcd*fv5L`ye4 z`r)72T1lPA!BK!x@THl%($0GOzCyiZGV853hsWs;e5wQry`u;0+N3V z^?QhtJNM_eUSI>BP7SSPAbcl{qM*pT(laTE{#N-UEZf6_f?z}8d9%iuf&iHLYCXJ@ z4);R!Mq1mYns~yMmFemEkPqAz^mRC@AM+DWYV$7-cj&raFQCrXdq(EyR6eGqg=;w( z?p&tOd3}0NoMlxY$LSTn+MiHWX+=9)szn#>ezaaLfZ}RyQti}aJ~%iKRZ>E&w6z5V z!KV8DCckOSFEwO&{$qv6BIsE4Jh*R6HZ*DHsHx=TC*u&UYq29@ZLU>#p@ccME zQxgrGu|=f`XiZ$)fq~}>3)}@&RVbkQ2Tt@KMvZ+F-}`?}8C^VF(V2g?+Fbk7>hj$;3bUcX_F=<>)kS_INkKa(-qD*TC!VJ~^ey z4O#sl$d0LVzW)C45sj8s^|h8BGknUZhdirKMiB_cz5+RH%6d{O^Ua%VUP)8j;;M33 z!R)hxjaF9aI8qo$tQ6NvYzAw+%bLK+YA5=}2iK@!+TeyFtj9J!vj+|%E~l9s?bWI( zru*^y#E%1!m(L^UrY>?tg_ZDnKS}Y0jLBCc&}c|SxWYnV5YV@u!trS27_T9UTYrGsL-q?)`O`RBB&59EO+tl9PgS1wA(3ozPHI(~N19<%na_1u6&6 zEiFZ+gi-;$%A8|ejs)G98og;za2~02B7QI;Hl0{mdV=jy zhQg3qn6q`{MSK!SXVMR>e@v_HqBuwJx;~8ia%Qr$LahoCeUuqTQnAG)xHJVjeFlz zH*`HcptG0^+uwTCFSJA)f$QHsQC!Yl8U7J$o;23ho7l2BpJhF9lnpo zrpc9OAmp)+s+%*pxNw#c|G+_I*JQtrqQ`^K+u`dey{!cg3BsmRp>V#kggsrKSdJGp zFfgbxXvaFs%ZnStgNvQabvar_L?L1)u1F+{bwB;5(HEnJoDb8=aab-p!@@hzVSN!2 z`6*(6BKxi?HVy722T3O8x1q@BIWxKN8TMi>v&mu~N+~TZ<+Jip!;d|~K%gPe+Nq&r zIpbh4im!EcYi?8f=mw1wBh(L#>;WK&r=C}N3rzhuF-{o$nE0Xj=#+w*N_j316+I&T zP7j01m`zh4Alcma6$cHXLmhbSit2H7kPgmI;+A7GJ~x0_hUcqbOj!nd=a;o-D>MkT z(9zp&&*=`i2IWr{yv*6BN6FYtVJ|ZkjjfGEyTI^4V_G}WBdO9=f_kP|iRgxwZwG)@ zU3=%3%*k*Q(@IumukWlRY$UO`r6uiToh@XLvNy+x``Mb| z#)w-~S&7thbIcZ3;2K<<%iOOVDHl#$AY^+qFL|E@*8Y@*8OuNK4n`vrjG$E-ZA)X5 zBUuwzZg<5?#kIKGDtH}Mn%2Nu>yu7jZl;RzQWQjxsbEn2KJa5S+G|`DP3EX0m3l@) zD->wE+i=MeukcLSrbZAt>C8GaOaK;xEQ6>-zMof}Sydf!m9}D68uuE^dI8U3I<#n)F zjG+1O_`S+tPl${x+B0qNZz}+lU97}6r@z6a=9u*^F2Je?fn3TFcE{?Yh@@V5#>D}G z0YGxtV}5>vFJY0us^WN^)(bLHuxDR6(aJtrh?3!D>ba%55vC1K_CXm4cvj9g0||V7 z&dQl^z-_nvJ?#yfwWX*Ks|)*eZO?Rvg@Rm}nvEut5z8UYAX22n z*I0(r-%+*kopnY>b~c2ZSy+e~!hYqE0T>@q)}W<|WK6(uEp^qYy_U|NC2BsD7_(s4c=*)BIZ z3adpT%Nd*7$93MF^^LA8Gab)}Yfp}jjSURn9Gf^gMx6TI%I;+vc#A4Xz|f(Qi5>G) z)8xi{iz5&*w5AKMr;_R0VLf!0#fZ4Xrrl;m_Y9t@{qiL?&2Et+GwD8)+mlqc*_z^z zi!mOE>UV21)N-~y0sw%H9!b;m4kOK0>Ke!znh+msw=S$1Rp|;+5oQW|`KHn(%H?SY z;6i6=YS{IZ&r2n;+REYKu_gGW;dMDX0_I>#X6+yZn?&l|82!2S6gUB0c_%t4d~Fak zOlvSrfvjlKP=7y7Y^IX@_;{YMmT`2L!=2;ww5k;snM+}5X$(K7h38Hdiyoe<)kL}* zp{d#VdN&X#UxY%UmASFch>{E1!0A#9l2TE{_~`~y#PaPz{=ac{M!dKD?5jSjS77m7 z774RLSVpWLFUT9uI}+Ei|)U(t-z%%?i%x zN{iU+S|@Jav9VL+(SjX@0U3x?#3yX?`Hu??3!2kLkIdQKIVcv+{r!85rFqE|+W4)L zRiaRMblMsPN7HGXTxJ2vv&VR(snea=$b%Ko$Nc=n@F{NxZ^B6D%zNvN)WiM7Wp@|R@9A1+_D}=S zcCkFTerFgn-kX6I{>xHLUs~Ij`V8}_~F3bM#qF= z92&XNa-6z__Tt!O?_1J<%b7Sz%y)j)mqljh1mdAV26eUq6*Bvkj+7SfUo;Y*f>b$>}|nH^(maMwt}P*YZ86u*0&Hj^6%Z^{pG zd{9UWLZ6~?-gN578x}kO?GDBkU{XoTzNZ*ZWunwXi?EI(;L)$y#jtQ;WFmI>*(AmY z_prC+V$rcJh z)ERtq4;VCyAp#Ib)&@Qu3V^5x>NShQ>xe&QWJIDKFZNX`;C&YHW;}GOBzDkCVUFkm zLUtjWSKO%8cjIl>1SPM}!r*5=geY72@7gDgAD4YZ0-grHZ4F>NIPH-Jp7J|`w#rnn z$I5`7DKjfQkkfcRJtudrBolvm`N#H7nTohkr@Jr4*I#>>Vrh>o z>Iw=qo28REqSMlF>$QKjIu7+oM80unzB|=pGU`axW;s<>rVu5zSqMxO_H`jRrT0O zT#Ky@7!?(j@;EFqJlgrsRV;#y4RFqpC!>Q7tibipRVi)3Qq*U?~~ zCI7o}{{!dIGDr0v&C?2Eg@ciKNbT{hpIsVS&CKVUsy2_d#G2kHBAXDH4Rp94%>TgZ zM2Fp(%V32zPzT`Sc%A^R*7i30@SJ7|LYIM}I z&s|Olxi=4XCGB(l5pJ<#Z#vWF`>J4G6MCZA+fglA9oKO9hdb}c>g)vD z<2ir76Cxxa8gQ7#kSpt>2ME6R3PwGxpuxY_KQC# zmmLQROq&qmE|Ss$or+G`0PGhwhNUshd4vSf9}rN3j#&-yD3(QO539mS@h$AE0OrM zV7|XrXw|aRYwL@XsEKt2!bvs8jqE59a)(fgov(MIlVZiff$q*X=pbWtIZ405Fxw>|TXo|WUE45|jO|Iv$&`yWMOv5oqt>D)B+!j}VmoZ&-?*Lv>1*poX5i zhL|p=oY-!{sVJfTtj!cMlln^IMq7(b67lU)WA;ZGM zl8cBK%^qR`%`uX-VTlwIqyp`@ z^}K!1ejW$s{xzD7@_>`b`<5w2QuA9n_3^2mX?@4dLEl|lULI*Zx2(HSO7a=TA9+U1 zyhS=2ePJPdvIRz)%#Si&Z1etAV1Vk`4y6Q0udECyw}t~rE<*?}GEl<$fG3I?h6~F5 zb5~fa;I!=$OL5cu@rQV31|_I9`ONtHc$W1{1I;AXaxjbBq?D9N!51P^79-5>Qk$%v zysK?4FnI$J?3pAb_zn&qio=iRYb2N!M-s`R2N{@{c73~SQfMW&VR}Om(4&SJ_Z4n= zO@TDy-Sq8}M*Xny=jfBUV|f5%cgOe4ld@jcu`31Qak9$HyJ8hUjDko(+L#-S=+en3 zM!sPD>{{`{ zqPKf{gI7B7ez6)38j+G_3YfvOf1xV9&})SM>3Jr)@7jk9vtYZx7I30{U!x0`W;<(l zM_Fr*nX}7dJWUS#DZFVUQ7XaxdYOH2#0Z-oP2>%ag%nK_%|YhH_TC%Q`k2wt=Y(7_ z!Kgt-g%+{jz9RE;*s_F#SI3a z>-wh9&4v-_WM3cmkPSyK_r+a)=Y52Q`pX^FdF;tw6H#_7%vHJ{gUtu?A5pKPJS0T? zDDSwLO2JYWc^p|@t0LYxpOD(xFrtTQw9p}zBpAN$uY>95eofMRO!8}V?t}J9Af!3u z!rxMqgk8LUz>L>nO2k;2fWdA6uNP7^(8rdK(Pm!}!SiUP4*^YIFqB@@#%5*<@AIj) zn$Blk!a@)Pz$3wZ&yS|ZBCng$`owSjpQ6U<>-qfd!Q$xQ+uxFk+aP3#PCty2k~3jn z7rwBqu9476OB+9%gp*yK^P><>lbs{J<)9H;9!;rhJ`4t6GLj!Ea*)gMB6y|YeK{o` z&?aW{*o$Vc7+h@M9Lt+4Z0#)&H5keKdh0)3{N^EdmbyaOd0X4#mbc5BIh)twjx>3J zClUW`*Wd;dl53lfS8sEm2vC*46^DmW2{F{9S~)))NIe(oq`^ zTK5SO&b+?FChYHLvOAHUm6g2baX>=Xs?nD+BqdO?WEb=}2{JVJ@G|&$W~N4tX))ez zNs^F(SH3wtbCnqYb@dl2*9I^x zo3*^-TqF5Muk9g)Vrf*V1d4_5x@dyeXyp)78;kG(nZzxfeQdX*X{g@X3iUCq+Tlwo z8QIU;43iOP3e9rV5<&>g5YE(IVKE%bt;MN~zWBU{MyGPQ<#_;zqR|xNtlcR>` zomTNVU-HDcXyx3%Xbo~*{;}FjF0OYkFn;$J!PbhNDk{?5ybP~08P%~r=B#7|P^C9- zzS9J^tx{!vlmd#V(Y9-uPYH>Sb~Bu-oEk@cu*bN2p-8HteN-1DnMtoj|HLkNoolJd zMm@<4*dE0g0PT_g-XL{Aj{C0{z!m>&3rz}wECni6%ts6CX{N_%l$t(Dd&BNF=4SC;Ah5Wpwz7<`djsXxK4ee@E_thB$q`sS(R z?HoP7xxAPN850z0P)u9&y|;bTG?|qC%$ks_ozKs#Ok;vAn=o9L%Waza8agmXt4>13 z$%(zBl;q~&T$$J9Hr!XsKq)m7OdMQXwMI*&CN?g~l;L=jnRZT(v-QxuuWvT@xFJb1 zzy3m;933wR$zz#)B8Fd_gr)q36oWt*Yum_u1Na}TQfH`wtR8S~(HuD@g)$n{pKtK>3 z00WN^Ur|&P!ntKt$8F5X;_5OOsLBu~efkC%2`ND3k8G%P^RKp$C%im66^tVhCj6wU%XVm;FffvU ztN{zt)6|#U;vnI`L&SJwdw+R_AmDif3s>iSwh5A4zIVScv21RhcW_w>meQ*~QJOr5Zu&@(Nm4R}Ru0Xu)mT|_`2Y+sZ8 zj-Evy))V-OR?A*oxbd0&mQ*N82pT()w8UzYaVp^k2&%e(D_Z;}YqX5@N$qw&e#5U` z460qLtLI=uwBcp7VqPnW$oY^DA5g7#6~*4%x`RAzpYn4Bi^2g1eJ4c9URXi>tYdN6c;Kh6H+*M zM5LJ#mIWAFDOEbu&o=m2!p&s7kbii33;5-Ap=R zOqh6{Su^**;{qCF!aPES_Wlmc@nm{j4ISz?NeUsa3-S}hw^3vdA&JD0aFu)!Dvn1X zNUe5o>BOf(qn@wYd&jYYa?$knX08SR8Lmt$hy=W#nWikLhcL-#z)C#3v_e>TMsP2ZP6_172g0<7!th(1jL$N*-tY`56l!nD-oa z^zHSD8}OHnhr)cr+^rN2ut@P;0D&7TKOtC!Y>;WH-lNAA^%r$tp%?2j2SnR;b?*%XihUvEtH6y)R( z?Ut$~+%#}}+)l;wPcf*ieN~6(Mlk@KS|a2o*Bd6(KRKLk-E~m@?Hy62mjz}FYT)-& zCTQbH%+Eq@KRPb*xG>B5`;)S=q8rPUO&@nh>(1;M@UW07Si-EvbKhtbB7oqP2#fO- zoW?&1;3C|eNy(d1HW>Ew{YoUe+&a}0^1cSoSW#Uh=93M_6GxL8bc#wST{xLj#9TDi ziSAN>m{CybOipzrC8djEo11%=q@*N!$H!3`{rq6hgc^Ir12fj!+;`wLt8~M4o83UR zw8}ZqI3~ja?Lioxz#a~ z2el1hhl?a~Niw{Oi}Ru>-~S zS)4-fAAaH{UY_CI3iY854|NzASa>*-&4OTj&UGSIqi(F@$KS)halb}w&MBq7H?19} zip`eN@Br)?1AwGoFK43ojnaz-mK4<+iEDw9{@{B$Q&FbrDDq*gjzztzGn2`JC!nnn zqan09ZU7b_Ea2#)$ut;}b(hh`00?zEO28T^gJKW24)b&1l z#l*xoADxD5h7#)w1(H7d2)+i)-=F;8)Yj1sYII_aXm2kv_{jr3#z4qx8?{Ax2L91X zW@BS>q0^+jnV|bGr3OjQ4Qr76qEifCs+<`Fka)!@B~Rz~sXXg}Pf=o_2s=l(qsznJL2y12BNB<$Sj;yc&}@0yrK@D(TZl#kHZBPJXmwBcOelE_Kk` zzGP-%cG*2(w_mA)Vgfychl7O!hy^`Qhikh3I3F)M2nF5}19BK!fs;{_!yG*0_wTA8 zWC89b6N?{+oTZ9l+Y>}yskdN3&+|U=B?{FFO{GoZw_UFwnKHv=e{%f%ui!H~`}q!B z(vp(Csg3+)D*KoVp@848Jh!(|R=x9vsv7U#vrqmc(%PvlBoZt3fl&B+t0g_*_Jcn9 zy}s@E`D0s7I%4p{bWKHkfkEdbJu~WQ{O%aymI@HQ?CwV!af7!;=d5@{!60&O;asno z&tr=;9~yhZm_j@=qvFw~FvtngrJkaf_!@XIwhP+y^!0H%0lC-3pnlvYjEL7ZY~hV2 z__?hIEb3-X?MSyeTr87#aq`2}@8NOpZbt{zoqOJ-QB9@{qNt=~vQY9SA|5~3(`m!> zFb4PA#MnUivUXc1TAxTZk3`1Ai}0t9TmX&5?m9^<3Mi_8O~tR@gbYunCL ze;8L+)d>5W{*Sv1Tz2dFrrz}QbLB<$5btGi;Igx0gZ>%6|*jtyS^P*mB2S|_Jw7MP# z;1p~_B@6-B9?bYR41C0Bm(k`PJS_lGD>p_74bzf3`x#AB4eF)sGTlOFFrS*b%y_a& zM|(%c@R1S?5Vgtqb}>N4X|=4%d2fgA%>@I#Day3D6h~)!aS1frg}Tz?FvvwaAMR;i!f|{9EUoy_MEFex+iQ&P zE_{xXgTg-l%`eZWD539^FN1+smi0d?*!uu6`L8|m zwY4$RK@Xa(cL6stF~7GFk2TEj4p3OY{5~iOT2*0UbET?kFD3C$I;x<_q~e2>YN8^r zO>OD3FdS8Pm-F6=dtucN_-mKHTP)zwfN}rn>B+AvBUB8&p?_?tmi?y{MQQ90r697( z%F5+NClR3tjerpBii$9)52m@Aq>1@LebY%)^`<{Q$dy44q%*rb4a!|N9a0=>L|w&< zg|{y8Nt=$14fE4JSxKeId`y=zcWK1v;vC{sYUpRN2h?1dgLGJo@o6n`CTPC`M0h-%t?Ep0jLUW&R zAs8)(FJdC(NJER*O&g99PT!wiKu-MD17!y=9|4jpb?Jhpnp*Hw(GWhLH5)t%any)P z9@vkWAsQi;X5ns2^5JC%0YJL(Z(OuC8+#EJ*E$uLG5|B)gUh`2J9uNGh0a&C)d>aX(6#`YFa~ zGl`FZB_c5_EcN0#&B7Kaw1BWvhqN5>5bKBk=Q-s_d|5k+5+5||?QL@27m8w`&JL6Y zS^sNrMcOw6C*H7R+zzck|>#48MvqWgIT+zG;lFN0`DM?=K!{eWvP0F$};W1~0B81G07E#@j;ONt; zD#8d0Umu^wl^{cmom4T3l=Fw51xLNS&PkK8u`mLj_;{*~A`#7o3o^P%Y?o$DHgPrL zs++lsrt)wr!7qRs{6csHB^YVg1Wp%ku}2R%{@5q0oNS-*H5Rz2n9_v2;HmuM$I~ov z+#~Q+gI5;oa2#Q@7AuV)oa5$43jDo8h8bd6&cGz}hPK_;_3F#36vp$p@BPR13L)QR ziF0yuqV{_QxjuI!zhlq|6Nh<7k-A9EO^F4psl9V+@ktvnawfkvqX4WH_kEtVXz4cFAU>%{NJdAz(3b6(NGT; zC2O0QiVB^1qMqnYTSINnpVK;&bEbO5RdNRRzFgvqcrcl1)^1A!tX%xdakOWI#cl4v zMIlBL565s)N?90Vh+eCGba`WvcB3Q7ik2$ul>~sKgnjuQT`TpT@CU?tVj>qT&V@ zGkS)_GDu=7hlLkM#-kbv1+vr&Sx)4}_C05Aq*Af~t28&?`S>L=Qk&ww?NNt+zYk{w@n;S77e z7EAm12nwp@-%u^{V7{-`4u6w<}cu^F2TmAU!@lYQGSPY{jWDh9f2!!D) zDk|CnXwdXt9}xb^LYo24#&!Sa%j;@xaI;q3iyS z;I4qboj!>$Qo3fY3s9i9YZECAS%wV4-S0>N1t@w)CNakS^oZNmxa2(MLw88v6Lndm zy83$>h{B@x%|2?&=?a`HGO}Ve2i!S+s=;M(QUKLGfU+04vp#zrL2Lj(o+<1Pf7!N8 zy{*sX@?6dq$}T%b=@l97W>=(jM2yt{)+uX~&{r>`6Tg#nh2q=WS(t!Uh=kYdN z!O02`G1)y<`KokU(Q+grF&@r)+T0$Hil&Tux2R<5&6Zfk4eCGuYzsHMBMS_4JF<`N zM&Q5y{hlqwnFbC{V+m)~C!R7OVE1+KL80FH>Iu0!H}~S=#lOB<*mzph{#sAZvX$o? za5CH399v_@(y;oCO-PBVVi0oKf?Fv}hvM#L-i~>zI@`QV0qrM$nIiD;bqvO+? zTPo)#WL^FG>nad8XdN6usMh78%R>(rlA;%%e?YEtb?ZfB_lTrZ%!~7oc6fNvdr|oi zUf0n9h|#~L#ndnpa?wJoyM+NuVU)ufb8xWrYI)M~qFFMq&Nz~R@%|HIuv>~!SyIPEy*3}8B9-&?Z#Z)ry?GXjbB}V)9ER@I zN4WVKL*B_*(Fy6A7tWs8+4@B?ODv{3GQ@u+*WLKF$v(AG-pL`uc)E4wyF9LX@n%=FvgY(0eZn-kMxvUaY0ojl3q3`uruiz3iPt~^$N}YD*+TIPTIzWf%)s;RYEWc z!}0~Y*{%Kc;SxHPG|EVPm?S`^0RjLfu0Pb@d7t%VaC$D&Oj%&yeC7Ku<9z)5{862L z1h3LkTZ^YZ+g$Hhz6M}BawzXeqq3W0aYR$8~8{1OsOPYS*dVx%I6P69d4C+rpVh>F_WW&!$yt-=**I^=`aJI z1vDRPf&{lrLoxurx!HLi1|`)|N$`)WeM}NcwK%|~$cK$f(}?|RvG!_!P^2eD1qa&h z%^Tn??f-ECxZ^@%<)MbC6*vuun%rM@=GIo|iPtFtm_=0l*&3;Xz`6YMhy28~0F{{; zFgus_z)1&wX8=Z)KhP@1OF|judnR*??i%|_51iDdLIy03E zvABFLf(bn?i7)BQg=J&XN)8U@fqx?I$kun(2Y|`ScB*%1x56D%+f~==6KY|<21Znt zTkP;^40;lP=|C~Hq%0#dVAiHumynWS&oO$(C_HsLHL{~4LiLwO;0>Xk#|g%PK0x<= zPzU2(Z7J9QI?jWEB*^7{<^yfwSbxn*aF!tO6_?F zCM6{?Si?w2eA0AMF+5|z70b-x_3Zo%TJLFrHry6@=Xbz9tXO3UW{C01Bz{?9{>Ki= zHvI0#oBGiWGI!gn^G8S#xWXpU`L`Q>%h_r)?(yPQey=EOBq^i!(z@#J_NX1g-zC)=>&*jr1&W&%933wO^kLYHJgL zDW&@FOIjz`r?5%8sgdK5{?qssQ=nJe-l-i}EduKNRC02kin^FfX)hw1I?CmC}O3NA!Q-6_^d`8wIA`)sFi7S?+ zLq?X~mjBCVdBtO|ZY$u+o*HJrc9C{b*R?b-y2mv!QL-5;Fvxvh)}=A9)^0}beOF9k z;&Kr)_SdBX%uGu8KTG$l(GR}I$!>8nI<&KBz~Eu$5ZBiiia;ZRhlBgUw!1Vt+kc5o zr`F?WAib2u>k9z{Bk_%wX99yrN?{-y-z+^+oc*@gu2)i0QXm<7&XZvZh=u%KaL3_o zO-?oj>O2Q&nU-I}$0jy=F`?4WK&wvi_C4x-v#wLg8(_dJ#pyU@xmfrc&4lAU7@&oXtmtz1UEvoh859Ia z3f0urhgz!j)QTTj*?XY14`!N?Q9ki7cm|0{m1}&7aMaALcet`-(xJ@`jAJMQjID|f zf1LbYl|9sD$9NnHKCAwo8f>-id#Z%%tFy2 zp)1{?(P7VXnYq1*o1w6Z8?;e;1Q{wCJ#EkUB<#RQ`dPR?x6||TE-d;l z^&pN$A}%k_k@q%Gte`8;t8 z$@)qj?L~!!KsTb(;NWJ(p6?$a=^-Fc)=%aqLfSFlgQ!MSdZz+m+-#Ao6f{Y1ymp1C zOE3kvn+r=Sf>~iZ$G44#tA1F2R@tXLP&EJiEu0M$M*KEzAs}GT@fol}pnT#U$lWy; z$1I5i=rR*vg!!Er6pf8nx0l?-FXfjSXvG|C_;nNV_hJweYhw^9s{6mwHY%51aXZWwlBn?+4h{|y(sCa3 zC6Xww+bj4~(6g|v4g=71LV?zT*!L#HtTt^V==XoU0J8#b)VwaA_!n#SBBzUGq*#QI zkddQzKUWQ&BpP$me83`Hmze_eQgVo%Sn&8o#%h-kio55?57`d@VZkNu*hX#8)bPVT z>rEIpS9qiU8+1sl;^E_`v7u`Q^*WO|TFOgMK}!N!z4q*INI0?$#S_`zg8qR|vOB$- zPHFH->g~75Q5u%ms6B?&Jsk*UmoM+kxdLZsDHd}WFe!ThjA{d-83o}IFl$?J*ngh! z(Kg=sso4&n-=}Gic91<@M}|{UIw#ye@r)k2&i~Gl%XSriXE>qn*HnIV_0`UZN|iw~ zk|l{CrSTt+F8^1<-OToObClg5D91-`$MY0`T`Mt}1iqSZ#Uh76NdEh-Be;mwP*Txt zPZ-x*t)leZ_7&cg5&1bnyJKtuPdgEk3q{qdi#I(6V;GdN!Zg}vkpKWVd?Nn&lLlZo z0go0iT|($3>@8>PU?HUssShx)c}Nr*qhvAclH|(ERobJ0rRK-S5*I3sklxd8_}qb^ zY2Kzsj|vRx5o20WZ3$R|K$}+?tbeI(O-)Tn>BETJx8a%&x@LW|C0?(W#osPtMrr5! z=InY`&AC9<6C((%4SqXW2U;4ErEk!lX19~6X=uW=tDy7F0s+a4@`|AT-sk2R=#p7v zZ9mBgYydLQ()OtEJ_|kh9VbhC)r}FW$CSEF_)-d`g{6L|SG{`QY>9hjRtJag`a5e^SFsK~s);HibZuPFK zfV^kE-q&*!cg6kq;~EzQT8wPKIpPLvx9?qob`_b>PV-*;f0|!T`B@uTI6#mbtlk62 z7iz}Xwv(rOEMO(h5g>`+ zG%?XWJB|hHahpIB6D1G`rS^-}cq_TYdZh{R`lz9Bqa3r+&Q?ra0U6M7?(Yw=zXm+V z@ahBR|1zqS;Qow^Dkcs>DqLLL>w|UF*E|uU%Te*!0x^Fshm}uQC_1$3UgJeno!B|^=Op=+WrQd)G5(Uf4{nykt;s0ywt)i-o+IHcE zfRxhRCEeXfBhnx(T>^^^X({OvkQ8YF>F#bMMY_AYb5Gvy{r1@Z**}H{df-~?d1l@7 zs+;i0%&u3KqDV%|_da5a8x;0_TKg%p>w2UR^;wlbGO`<4t8pZeNe=GcO&AS1eUGOJ zNu0qv?FxyI47p>^r}cg{xw$xyJBU-B=i!^ zAFMM4?Ht+nw6zo#;}f4Op_Z~t`s^SV0EjE~)Tw>e6XOw^u*FMMF@D@cP~!yEK<9vV z7Z_6N>VD@&#Z^sM*%`ku@z=3+IF8Aa;AbCoc2QEc* zKU3tG^don5j!!RXaF0$m7;~eV$?3!#oE>R+n%-)wAVoE&A)3Nd)4n-*;IH?55^;2N z%utk_vzhCB*?)C}75WA>z1d5QN#jVw8+Z?yura5%`xG)1TlP<1@DUysZ? zT-1s{6*&J+9(dw%mq~8NbTkpZ3SC7-3{p}{s&dp1^>c0yNJNO(I3?X%%rOvgduQOiH$Kr(uu(5b#Iz$@Kp5a zui>ukOP`gaSX2=6&}LPzLa-R%rhASzo>>-gkIh z++l;afBIiQ4%tS7zQLQ1q$22gdal}%R&)V5aM1~)aGkiSr1TGF7kPj;z3W)9$Dh%w z0q%H^vm*1H0b_EO^*@0d5lvB`VAQzPnZ@`HTUDqTEM>v?%GIe8pOrNtbvaT6z~pXA zEfF~FkQmwP$ciXilC)4h6wlPdgobo@+#2YNTovN*$Vl+g*GR9Y0|$?ni&)N4%Brg3 zY_xDS3&o5s&AIjbz_ug7LR5 zwJ1BX#leT;vs+P@dm$qu$B5fYWH6nC4R&|HkN=9rp_O+bXGA$Fi!P)Nr~B#_gZK`Z zPmO?wm+N0Y|L}2Eulbs+ETW0Ykb5p?2l>MhA9lf;gE150^~-~~=tq|3!33}S z&=*=|+oLdzHFgqiL_}Hj`%8^@c7yoJ%7K*9TpEIIhtf)^q~f3Qm<5uIr%Dzi&%k*X zxHU4ki2eQSK@b2<(#}I754Y{U2RfFa(=#^H86riEAuPOf;b=caR+jx>B~2PUJyy@l zO}6J%j$dfSC^EePmC{oHYDT}qQ}GC3wgc)+{kxT9)ny{j6x|# z#&9|sZsCvABsNmd`laqOJVS=nM+zT+F|2Gxrh?J9Czc8X`#4)DPdA6N zS?-+NT#CCn-h6d6vC7StWqUgz=5X9&9mmw1SQdb_BG=ShEhqW$V`Y0^UZ{i|8>SA^ zSAo*{Jr=;zfiRCTLBsg@k{b@(O42B$VSTIS6dLHYAs(m9VEzE`8336a`dSShpYKW8 zu<*;^foQx{Mws?ER&uv|CVy~J3P4AI^zm>0Q9SKq3rdP-FiiCo$j*_ZVOo8qQKX0T zT45+m={v>a%z58C#AnqwBrEW>lrFt9^S@{WkXd_^N7#*8B2R#ozK5vo+&&rj93yZThGRNdU2>jaiE33J3a|Z6ctSojK z#abu*1gH6tY#VW*kxFe%^-hHiL>tm#Vla7%mul8 zX}BZib3gPd{vd>@rdsB?b@ZX)C~@Q`${$LP*`!PsiIyK$;o5MCWE$}n%L6Iz-6LMY zs$=BEV>z-V@wqUtq?cZ61>(Gucjj{SX%kZ<&@3_N%?-z6%#cYKJjs?>S-UzGof`Zz zEIp#%e5~_sHFo4j<<^KVE*VTKbjCkJ6*GUr9G9t(;r&CQjP|Rgfw$VK(djPkGve0C z+LG)e_pnw6OnU6R!lf%Cj*h#zVQjhcvLk(M>jZxt?s_5%4+mso(SJg&+JUGtWTIbt z#yA51;>}}WJC#+-;$l*(Jp+n4y$l|rWMcsog$I* z`qPG75tE~04kA)$NPcw~g6LN}vrHyiTPSb%a)nuMl#ZYpO#D$V{f9vM4LNCZT=PH4 zk@HO{ydSx}Z`I*~gM6L6`SFYNnhERc>o=x<5BR|xU+^xGQ*rLa%_5e z?0u#jMOrnlvU?*t_Z+KUCj|Iy4e4$#Rw6vkUo9OJTXXb|q;-FJd_X^M@R*Ye?>6=V zb|V_a+6MwsO=I4233|bm_#*DDMk1s2O%Kc6u!IaoK#31Xg)Yy zzNy@@|Fq%nqFbZhcLLjXSLOWY@s_Bl_zRw<%jP7|zI|GF*&F@%@!v<4fo4Is<@q9d z*QXm=(?453BY!vD*zSxM9*QSqFy+}vWiWj#;iyF9*1d(%l!q#RWt2mD2^MVuT>1?{ z+hRM0K@?#qZFIW&CxdiR@30%EEn`6aBSuFQ9X~MZ)?<=l(Y9TQU4(Gn({SS~v1o<( zz}_YD8}9c%np(40N_0o3Zm} zHtfPa0Vkt8(o0Koez`ZXsXv3A@K{&wy9e!EoxfN&*5GrRGkIJdeq6bWLPYCrZ++|l z+gieJq_^61F@p~06Q78MT?iG3N!T5y*+8HHv4U>Zm;7(H%TMEEv{59$Pt2Nrhw%2> z*^UF=6N)3-XHxVpTQD%wZfMNol_EC}=Q! z3LOLI1N~j`>oh(mlGME4H^@503yWg=ev6{(QyC!MWR$(+0bZK$xItzL`XrvY)O z$=DNa=aY-_2RB$;bMvywny=n#J(hDNBxyf>j1@_}-`;t+O8lxfOwQIu2E%GVNllHd zlrAi0>imi_J4;k2xq$AEg2GsyEJamqtxW3W0SME0d;f6X05xHG^&0PO|9lexk-9RmaU@PYi8A#m0uV@&0S3 z2ud#KD)l~#jBoG^r}=l?SM%R27qa*-4;C~{L?8)N)4z4L`dKD_ah=AzLlQ*74%dn zS5#cIyl#lLc6(uD4TS}vdR$!WVm}02GV=0XhEgMNXv1J)VaCKKg~U&&Q|IO_56!uS zM1)D|d5M1@6R*#Bfsw*(_fprIF^P*!dhk2*^7aH=D|1v5=*EDOYYqMic;S(u zVxP6}_2HX)Qr(WwiSVK`GM1yGqV)*)1g5jKnScrXm2k7b=-9s?S!(eX z^{}QiJ!st78#hM2EhfB&Z3 zx3mau(=25uddLh|fX9HTskII(w+idGj%^`M@g5Y_xY^1>#N+gs#+$By3PunSY7RV# zB*gVbt!x?i&`%v(lKH1dD;JWy!oNmXoMbq=e3CSoa zc?rOt{QP`!Q3K>;d5vP61vCsy!mlxM6*GD9WP3ceZ6PYzRuSJ$3IZi1)$`rqMBRg1 zQV28QU_V&ns$lWE9}2N}dSvNt4Qn;uZdnP6H@OZG5)1n@sp2df=U0Nv)&iBjc5)>K zDMfwKE`I))0c5KxO!bxAoG5^xv4MYiH75J?i?T0^FQS#aw=aP_X)GA}t`yNFt zD&FwdIj+&ny6%UVE2OU8Jw2eY{uC|UwKcjLUSfAH6O7m;G}=hv{*VO+>n!=7_}cjB z&Gq`ToA#pG%#_0d8Ib*_=e#nv z?hq9!qxmwUpu@ZMa+7DAG+oHs--1!HxwnOHZh=U0VIJAmLqImR-=1sZ zW6*xDo($G(Vc}=iSW0Hyur4O{RZeS-x?ZL3DkF&`F0QZ5KS<8cUjaDZO+y2>5fszO zjZ#RtfDU+BuBB?0-E8K^@PA`=k49%d2XB);0)7^QmI!YIG!2)j zLU@wdn)W9CFeY{W`uZexi(v~Qn5mBWS46%a-xuB4&Tw?~^lGX^i`Z$f8lW;-Ti1A< zPpjy)dT#|>*c2C+du+%VFK~}oU*D}4oE}nwU|w~>d`dm!s7NA{u0(?hvvRG7QW}NA z4$=<><_gLFB#o{}lRx>L8jwBIgdLz$>4;)Zm*+bp8b~i8v;dp9>F?`%doOu=uMVd} zm~wsDc$Ar$Ib1S#)!V}H4?5YUjZV0;6#n$x`U&Kh<~E2cP|3B=j)&2So?rMy+@yU- zjP3-41jta;WC#z7Dw!WvH%Z}w%Ebo+_!s8WCIsXLy-~UJKK^3<gpLzqQ9uFH@;)x38W>#-*IZk8)6D%i}= z6GI}SWt?AeQ_<&{g@2di=VGb~^HAlN%~f5!Ik~tf?pX9%OiJdV93TLmUHY}ksVZ&G z_J%+XhA|N6sQNcE%%1-F5S39HD|kR-H$7I)YJ#3^a?H?7((E7XkLGy`8h=!_m&Lir za|9?5A1M6w|1IR>aM{T*R89#C4|3hIycqpuXJEXr;mb-+&tEUpDD(W&ajgi&ZcM@9 zEfo)|53ix3RLDx=F2@FuRQdH@j(=r;Up_uSzm-Kzn2L%@ODC4b&|>&e&GsP+L^x@)*5B_%wY z(ia%cE-oQ1GsZ~xZ?c&Lbacw>Y!G0*y&+$;n8ZxwxyHvig&W=Hw&Vm;h6aW5YQYseQCK+eJ@-a+T@EmgnJ}J$g5X)&j{5&{Txgaml1yvsih=L|pe&DB zUw_NgFJ`Ka{d^owyu`zhw6w86k?}dB>`1%OFHR(QE_H*Zm3N;9EFilUz}XR<$GN4q~KhKZ#_n?U+ zWCx*J#$L^2bj3hxK5}2-!JYm4m(0YS&xx_IV7b189DF}R|4%Tn|E*El*k zC1dqQu^-G;V@>?lY!%^zRvt<>D;*S-2D^hWl!Szd4&QNVabgM>G5< z?_w``eqJy4Zn&?{aC0cRGvC@OVMkueO?}7zCS!EAkY5&*6m&;CSrI#3O)|F&tIF%yFma}7j{NBmFI&ij zMM#1_+v4PJI*Uz;X*=JW3(hai)(yUK*pQ>4O>7#;qRdWW`5}n4m!nE=Vr4r#`LWuo zepzU5vY>OoZmCYJ506!^Z~x}OO?r6G&R&^qZGT;oG_julY;7Px-qDfcC{5q!>nm40 zVkafuTFla|mS)dTXzSgju6Rjt_Ml#?tHNkr;~lxNpoN8n*b=P*{>Xsmm9xp=V#6ED zU?Y4QTHeGPqyDRiJY07)pGHH9@*g7s2obu`hw`I)Q?EL)^${+zM9j_2G3FdgbNym5 zDm}Lp+kL#7o0A<(deQgB$H(y_IYdy-WqiS(pPi-BbRETddP$&2Uk!Sg9*GW(j;5XL zC-~~%@Tq^YvZ}`T1+1)DAvG?mUf1N`iE(pPwf+aFR^#{Wzkv82n6S06VKT8a?V2PK zwxXtBrK2;%TwQ&iKg71gq9?oWw!*s<|EU$=DDto-CN=5h?<9IgdTZrfEJo69+N~>m z4oY)t%0E4LNGntxD@~v6{^K}so4I;FeL2L;41X$mwNEkdTce1sI@-&uIV0nX`{^+S z*73N(D9_U^!yUKvY`pvZwnK81*?D62v`4XSHD=#Zs}f`RVPmZwl9b1e+Y*LjD0T3O z`*L?=*;@AhY5^prr7`z59QoI}R0uumFO(~muHGD8&HVkT_j;bo8|JvU@_y;|R zhZ2<^H1Rtj)^l{H7~z}u8M+xa0>cHmqbM1f^wMa|CsV5dV*bx%U5}2*tL&jrB?-0g zcXD!+>q?};#*SyF)2Bc0F4?V8QvQUjbizZm=Vm#;KO!3PXwDFnX0|$>LPXv7KPR*v z?Llof{= z$L6}Pb$&31H}Y9+_Ym9$2Wx70EJrh7C|dV6x>*Eg=S-#+n*;BIbKe`80hO#qG3BJL zmR8$X3%hPjO$E(5FwO8?jS%qh3mSFmaNIy3U`MVTgnra3bD!f8Z!uaBc!fVohBy3! zYLXY+6x3*GYlIx;HSG_k{g`Rr&~z*;knq&-)Y(T>A??roiq0VO@(=JYi<`oO{70X0 ze@(?=Q2zpg&+d$PdwDc6;BlKp#v%(%>SocX_a~cc9SM0_+5o@x!&@xS7yKkS`6!7@ysXZ+-z`Whj-m ztESnLaX2#}flVkuPvZ+JiGbnkM8S(>m1d89M{v~K-|v+MdTpb-Iq&@gYQI`B4Go?~ zeQE}-Ddqhsol#dgRSrF!>W-^$JQUNsJo9}=T|7Lmy9KxZd3m3pkw|Kor&9kXs}`U;CA_k}AHiSQXZ;Ey?Xp)f_)*YJ+^sgCMhWi{gmfmR!mJIfVmZ!sj8cd%Mely zT;?xVu`nSJVNd7YDgsW#n^UtvLo<`e4~!abWrDd3kdPTtq3oa>^)o88w7zAnMSf{CpxUr1PS$!?{?t3wIX#K^+(8*Boa|NkQj<>O z*+M1#5f+1d*F72<`09K`Fyh*JcKrbwAUUpxuMcM--I2H$#H$I zgekMKPDb2Sli?vpSv9*;#o=qcQCI`Bz`0bm+WXV~Z1`iu&vn07>BW@}ogHhI%B7^6 z!&D>P>;0t1P3~9#N1m?s+ghp(yxCpl3H8lg5}Qi zdT+`iv6xBLf*NwYxp(vVb`^g3o~mhcg(9~K8Tk4)*wpLxKIC_WJ5h%$2N}jW z*{G82AHYLd^{ODH{{HqSyC3}ayU#kJC755o-e2wxy1YE?0LtFj__T;{%~GV@mQ6m3 zV5F>JJkG~p^gTr;EZxlUSI(kzR<}E`9xQ%3+?M>R;{N%JaKJP}E z7#qWA3h0R>3Z|k}T%C?l_h=Qv9{@?kG@-z%=1?&UF-=bXpI26^iG zAXMe%1&<8q-DRNHRl0ECT15PoS8kT-bu)!OqW9eet?5&mXzwH!7rD$BH6~ICHa!XK z?WJEea{=+;%l`>`zpX~=hG@2Xl(+kn*54P zoGI3Dy${2NUzqtdW)zk#r@mu22o#9lrQ!4HQ(j%Ocxn+vw2*%6qjWPUA<;uhmZ#|i zO;{jbl6L4e!CT#TR8{5ZiO?{$2#;1WOfUr{DBoEW75zJm%R;BEVbJY^1@$T{o|*&- z1$BV;0|fFzz!`mh&C~VBc%(6HOa3LKl%Fc}erxNNTWQjvq9l`o=CeG_I;dmbdKpuG z>9FXUm`{tjLjW9_y1ejbs_kCIbkoF}5w1=)KHr_Jj8eS{cSS3BVuTUam#?2y_^onA zqWodU$C(D`6(oXgFX+59lbp<*NpEjgUeXpBD%!Hl&~4p7SLM?sAbg&?TzSK3VtghI zpv??ydSzj$!HJ7=5UgFG*Hrq)Ft-F*K7`Zy;PY(Ix4khAdMzUTYf*{v1|84bLW86JX6)?*duLEgf#dg_F06o)vT zRJlLvFk^UiaIuJbS#*!Fzx4X}^wF!937kbCK<;4;$_G<+38#+-Sp)AsD5Enm{ejQc zi6*_lko(DR*BWa^i~}SqxJB<}E$yWZZ>nbP#` z>Of5ZQyY_FWd#vQlQ=l}6V>_l#S>6cN9)V<#npmae~vVX=1B@88$C9HD~6KVhd# zmXeYpxv-rTV?!CT40c5dL9ns@{-dK4Ger!c%tn^c|M4RYu0oR-{#`Wh4^&7feHh#; zuMHtSt&@ALq*Sq>tIUlN{+7+5)b_rRd-8~IRxci+IW4SS##mYOYn`zgj1gig4$5!B zWN#=|nhlWos5-Kx=|$eA3weap9N>^~=}CP)Ph#J9G#009dO)^t5OAR$d4esylQcz@ z@K*JU+?ap5cSDER%(W<>$4Vy8D4e+16iOzezSc~_qHm0f9b@-*0hjnwezgOfmEXK} zARjUXe{ACK-%jt+J3fzVM8zNck)Dep{vN<5M>Wvi-Yz+_b%J#*0-#AN9x3W^v;`XS9@w1JN;$`b!RDvhr>Ps z%<^i`nPgwC-X{?ryhurhoqx@#h6@>Dhh;0%(X%sgcah<&!q^|#6`Q7WwY6t#tS0k@ zqa^9*A;(u}b_-fIVQgvld2T;Qj4DBwZjN|v-J3=LO)fN z^kUIQ_9Ub%G6Bcq;-I(W>7e$2ucO|UQe}lHx-tMZg-0_9^Wf3@S4UCM)d^u=cq!Dv zLMC2~?;X=MC+@q@cc0rmeIRWE73+J`VQwT#P3{39h}|yhONkiZ+Kg!t0FX5+5DkbC zr6k)HlPPgL_0u;K>KT;%t;Ck(Pm(YZr};=v35nqGS_-%9AYH`VTwVf7O0}g})ma#4 zp)TP|50CA>J6IHCdi6jeLL!vB2$(kSZRPd`+CA?>B7ZCLHoTe`)W8R#pMC;xBp!q>!WHG0B%TBkp zjNKrW@!i~Q$4_U}Re^Mvwlm7D%pTVo&=N?5!jt55DbHqWC@rYC73s$f{#QRemVm;- z)yXUHonGR`q0rZ40&uglI+8^5v3=wAj%BpcXyDL4wu_~rfK=JOQ6H7)M3$-eK;F^7 zEozd9*~?R(2Gdq%&I_@>``&Rg>bI7Ggw3Gsb|h*zc#x1}4Hx`X3=ZS=HNI>gPl-YF zyjRc;!jnjg&jEwrg8oz=BJ^XwMWEoqVTancdPkg)`j-j9ZFNB%C>z8?sW8eoq)Gcz(E?U_c(euxhU-=-WM zW?i}>om` z`V#6sKn6=r`Hi@p5sC3VO1rGN-k5VM2g3JOG*U^H5s~Q=E!>oamJp>$*;<&Fv~5q;$y3+ zJdn4l$UDtTe!MQkD%kM;M>Yw-cb}hMp)}J_^NCs6|LXoxaMEru;<`)Hi_?>b2l|2U zJ#KVUVpksqRj8wlJk$7KOcfl&rl>g7arF1w97js|QmN-;?l>iBLHJjD3r4}*y& zOMbAQ?+!^^?UocJ;g(5*6X5wzch8n#*gaH)$=+MX~tVWFp{Qe zY=C@N#xFNHnfzAzmBrWgSl&Gt$cj|2wHXxt{i3=r`BqX2FD*_jUHUuKsl$c-haR8n zbo%8t-m!9L6ff9ZW}TnGz#f0Q)*yA30Cfg4nVIlbXph$gj_#FjI388GrF!j0>lFmW zSF}6Ph?LHU(wDW@BxX>vH=lcPQgx3VHiRXv4(G+BHE@fVm_dk^WS}z{^X_sOf{zcr$TxL8+od`cV^;KmJ=x$ImHz)hw_~_dyQsEF6rO%h`xklFS6@5il z0uR*u5J?(ic(#I{X3;=K!2W)j#}J{bx2-M9S8sOg+QUVY5lO7Y2W+BWY)X~`wSPAv z@`-l$P=>gp@S;q4R{4)|b8}bNdjn+iCxkImFrTg#h&Z7qCGB)2u){U}qEbY@&)!9sv!^NV0kMm(kGam&-6nhz0PQ8Jga?n zx;5mDbQOV;lq&lY)E#B*heaGWt20jb-uHZS!(SdvWh>X$v2&cV8u++su=rqlv1brd zQ)BaJ3+U;O(a=+*COA3#;g333y85vo5Uj;sCvZ)5I4byEh%7=}cZK%6E^!O1Dd!ty z+>A}B1!!oU;1?%syHg_;bc91C7LYF@64^)5Yxcon{+zuQ9EM$N+)&q$1=S4- z?MUS<;PVI*G8nL&4CL~zTXB!Pp0@0tE8W@{3~Yij;&qska|pe7^b6X`W>#7`S&5YW zF1-~+V%S;PJpVLhP=r|oFl)bnd8C^?RpVbkwTfA;3qvlH=1g<%F==D79O&O@e@HAH zO$IWx`-t4V@2}Y#_YIe{(Cx0aLp$gYy*(}{4%)gLG}q+PY1lC9zs0(m03OGz04IEaH4&a;D?dLbTta&q4eQUeq4Kh_t|QM&?Ece} zt#;TVCQi<{4}nASgey}QVSNAgrb^9@>~KQZ+I}?klHSG~g}Dl)UNqL)R`&j_h<8KB zlez2Z+V=Em!G6O1DARIK|1+A`fuxm?^Q8;_Ng+(oJJjbnKvP&uIS-fu%~E#|fQ7al z9dm5YW{8eYM@$hK5)zmh8*8Y;&deF`QNs_Bl$12*o5Q)?C2JPdNXgL&Rrfa85>k|4 zF@ry%DAOIx!ovKt%b3d8^n=U_S@T`n;|n!^^r+PiP4W^?hR6Hee{Y`3D)`@FtCM#C z4SmSkJB4?COWD23FmNzu^$u!M=teu^xsn|yW}&$8D+84Z=VxzzeY`Ph7ympQX5c7v z3S)SbF1&woN&;(e?K`cWowO-uOpfQd7Eaz#Dq1Q{r8fSFy$&1xZ8%;63}ms@vzKLx zlcVE2nKSgvw}p<>IWz7fYZi;dRfSv{S(8Vz`6u~(pY3<{ zKl-SwQOEbCt>%fmAk%{{>frNiqgz=S9q~yzw*||ckW8S=GA34j1Z!uB42}Z;m2BdQ!#UYa*_;$t54E^-_ao zV+HkR8jnW>ie-gQfZV)R?|a|*^);<2MSJ__%hfj=oE+ExMl1U0ff~R^`+*8Hb&~8W zb!d9H8AGmf{d&KA_oLA@NNy}i{)*+tGnZ%nC#gr;6`$P(p91BWmQ$bqgi0EYLFi9q zg{-H#+VI4u(sbeF9<>_qpaEAqJ28G3({B}j@Q^T|i@_P5_?JF^Mu~oFR7IN`T-fG} ziTR0*>gKpGF3NM zE1fn%i#)}8fv#WEcBb$b$-WgN6Ohg~1_MijA)V6LLYXb|A@&>A(SMWDPhzIhMV$!! z5t;{rF^a5u@Ip*N5izexc!+z^NW?v)UrHZW$UT(1u|76?B{PfG`H~5HFsP0T@bRyn zhhiy%{GB^=sh^;c8as3V8}5vwgub(9R?ZNXct}m78SIj)c#Tz7kw8X4DawnN%{N5@ zBAh{w5oHBi5(YBd6f0?~QI})7Qond| z$Fl$BSOOiwqOW3_zc<|??Cj2oK5tE{JkNI$G-*9Ma zdu@HB58C)4rq8%oPmFDijwQjIJ&ZKz?0M zn)?|X^hW5`KJfK38_4v`1{UtwJ(1ldp*eK^jdQSfCg1(X%kxaK=~*==ZR49SrtwF# zWK!rJ)iUdbKsh)o@n0)2Yy5|EYO>FqaLrYvKkNMV2C#fQx+B%zp2kG=dc)H*bCsu2 zB!bAZpB9_=8K-#J3_-DX((Y6a9W<9-YWu>6C%oXnHZwDmJBjiN(QvjJs=K*&`moyV zZ?bt*8({58!%ts7@jydE&C%?6VY&xcJNA*EB5!K=-S?+`8!S8;E|eQs0maC<^4sB0 z>(&Uyp6^`6O#qbV)9N3%rgPQ{trz;YoK#<5n{ZVlmwGPTUJw+h%h@;qvU0fGio>DY zA<%0YPeQxCw+l(`D(*)@cO4pG7dAFF`kuv5QhWXtQNxFdILQq zf+vFK*Np3R7MWpyaCu}Q;IiA*XX^MC_BYv)RBaa3XE?SgFS>J^z-py&@KG01trCq;+?1jZjC+GTHS$8~p-1N~l4#I`}N(iN4sb_*%VT!~N-UKA%k{ zfn#Gy+O9-m(YmD35ZUMcc%`tg2?I|eUih}&9%~&5j74hc)j{yF-w4P%BmR|YM$5LM z#HgS?i@ceMcri~w)3FZkQF6GTugLq$;ZWtF&rt~w0-Vmto;-D@$oYFO&}0kd0pYy2 z;;6MZdQj7_tWglSNTXlq2f~Zmk%4OmJY~dIuHxki-|WuLZq^&tHA>!`%*aS?iZxLx zNSV_JDR4%Tw3a*)wBDa%-Ci67>^N<6fDa9V5F` zY|NUAETYDJnVz!U;r`6$WeFPc(>4Cn=Q9E-{;aAzSn|8|@F=UP`qGd>sQy7cCN4EN zri{(FL6d+1y}iAK-MOkd10;5P6<{AN0-FZ?P29oCE+JvZ_&hPNu*D2+e7+NTU2gWL z?~)#$Hy@1?3k&3|im}8|F>_+6jIommkzz>NTiPIeB!T7^=99A;gyqCzH+f%`h(Eze zp|=lDzPxpD&MeIG2ci*EQ!}u2EAEhaxsT-osTMZ}Kuu%@Qfp=DvW?XVcMA*4DyxH{ zYH*)_Xli0K$cdX23k?sc4xynJF*iHyz$RGdFE6hUe`t_KfA5G+jCtGcbm?%%JomTE zXJ|yL>e|=Fy|)IcRb~2)z{=+7i z(E}v}tx?dihtiUgD8Pa=jr2+C={04f=jV%(E#Xy2VMvx$R{CY8lbUi6tYXF;UoHsd z_Ugrs{6~OgVFgkVGL;$9F#-@wPEEV>4;MVI$_nTjz935;`=0pQV-kS^>9N`4!>FQr zx5)8q1pBYb3_}p$9 z@^+Vq1O753Hv9Oz9}CE&8>_!rk;we7G*7&M?*Er&59hzFda>n-1OH#~-~aQmm{WDW-Vg}n!#hba_5Zxe z|9qd-8!L!UVb{Y&ewX+2kI8kEML5nX6nt*E{DgziIokE#&cSr?vk2V(ZTSrDRsSC? b;XRoOhn^Hp7xXLm0?0clMadFzL;wE+yo@Jb literal 0 HcmV?d00001 diff --git a/client/public/images/NVIS_Logo_transparent.png b/client/public/images/NVIS_Logo_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..6cf134548736041cbed17ea517bbcbbfb795b6f1 GIT binary patch literal 26531 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 A { console.log("data: ", data); if (data.volcanoname === '') { @@ -14,6 +17,10 @@ export default function AlertLevel({id, isEditable, isDelete, DeleteWidget}) { setIsSubmit(true); reset(); } + const onCancel = () => { + reset(); + setIsSubmit(false); + } const [isSubmit, setIsSubmit] = useState(false); // Prop Forwarding @@ -32,7 +39,7 @@ export default function AlertLevel({id, isEditable, isDelete, DeleteWidget}) { pr={3} display="flex" flexDir="column" - gap="10" + gap={12} > @@ -56,7 +63,12 @@ export default function AlertLevel({id, isEditable, isDelete, DeleteWidget}) { - + + + + + + ):( // DISPLAYING ALERT LEVEL DATA diff --git a/client/src/components/Canvas/CanvasComponents/CancelButton.jsx b/client/src/components/Canvas/CanvasComponents/CancelButton.jsx index 28b68a7..93ff729 100644 --- a/client/src/components/Canvas/CanvasComponents/CancelButton.jsx +++ b/client/src/components/Canvas/CanvasComponents/CancelButton.jsx @@ -2,6 +2,6 @@ import { Button } from "@chakra-ui/react"; export function CancelButton ({onCancel}) { return ( - + ) } \ No newline at end of file diff --git a/client/src/components/Canvas/CanvasComponents/DialogPopup.jsx b/client/src/components/Canvas/CanvasComponents/DialogPopup.jsx index 9957dc9..abad4e2 100644 --- a/client/src/components/Canvas/CanvasComponents/DialogPopup.jsx +++ b/client/src/components/Canvas/CanvasComponents/DialogPopup.jsx @@ -1,7 +1,7 @@ import { Dialog, Portal, CloseButton, Button } from "@chakra-ui/react"; export default function DialogPopup({dialog}) { - console.log("3. dialog popup") + //console.log("3. dialog popup") return( diff --git a/client/src/components/Canvas/CanvasComponents/FirstWidgetButton.jsx b/client/src/components/Canvas/CanvasComponents/FirstWidgetButton.jsx index 83c7c9e..0dfa2fc 100644 --- a/client/src/components/Canvas/CanvasComponents/FirstWidgetButton.jsx +++ b/client/src/components/Canvas/CanvasComponents/FirstWidgetButton.jsx @@ -1,7 +1,7 @@ import { Box, Menu, Portal } from '@chakra-ui/react' import WidgetMenu from './WidgetMenu'; -export default function FirstWidgetButton({ appendWidget }) { +export default function FirstWidgetButton({ view, appendWidget }) { const triggerItem = (<> ); const positioning = { placement: "bottom-middle" }; - const WidgetMenuProps = { appendWidget, triggerItem, positioning }; + const WidgetMenuProps = { view, appendWidget, triggerItem, positioning }; return ( ); diff --git a/client/src/components/Canvas/CanvasComponents/GasFilter.jsx b/client/src/components/Canvas/CanvasComponents/GasFilter.jsx new file mode 100644 index 0000000..1001673 --- /dev/null +++ b/client/src/components/Canvas/CanvasComponents/GasFilter.jsx @@ -0,0 +1,232 @@ +import { useEffect, useMemo, useState } from "react"; +import { + Box, Button, Checkbox, CheckboxGroup, Field, HStack, + Input, Spinner, Stack, Text } +from "@chakra-ui/react"; + +// DEBUG +console.log({ + Field, + Checkbox, + CheckboxGroup, +}); + +async function fetchJson(url, { signal } = {}) { + const res = await fetch(url, { signal }); + if (!res.ok) { + const body = await res.text().catch(() => ""); + throw new Error(`HTTP ${res.status}: ${body || res.statusText}`); + } + return res.json(); +} + +function buildGasQuery(filters) { + const params = new URLSearchParams(); + + // Multi-select fields ~ Add more once working + for (const v of filters.volcanoname ?? []) params.append("volcanoname", v); + for (const c of filters.compoundname ?? []) params.append("compoundname", c); + for (const s of filters.sitename ?? []) params.append("sitename", s); + + if (filters.date_from) params.set("date_from", filters.date_from); + if (filters.date_to) params.set("date_to", filters.date_to); + + return params.toString(); +} + +/** + * Filters a list of values using a case-insensitive substring search. + * + * If `searchText` is empty, null, or undefined, the original list is returned. + * The function is defensive against null/undefined inputs and will never throw. + * + * @param {Array | null | undefined} list + * The list of items to filter. Items are converted to strings before + * comparison. If null or undefined, an empty array is returned. + * + * @param {string | null | undefined} searchText + * The text to search for within each list item. Leading/trailing whitespace + * is ignored and matching is case-insensitive. + */ +function filterList(list, searchText) { + const q = (searchText ?? "").trim().toLowerCase(); + if (!q) return list ?? []; + + return (list ?? []).filter((x) => String(x).toLowerCase().includes(q)); +} + +export default function GasFilter({ apiBase, onChange }) { + const [options, setOptions] = useState(null); + const [err, setErr] = useState(null); + const [loading, setLoading] = useState(true); + + // filter state + const [filters, setFilters] = useState({ + volcanoname: [], + compoundname: [], + sitename: [], + date_from: "", + date_to: "", + }); + + // local search inputs + const [volcanoSearch, setVolcanoSearch] = useState(""); + const [compoundSearch, setCompoundSearch] = useState(""); + + useEffect(() => { + const ac = new AbortController(); + setLoading(true); + setErr(null); + + // TODO - Add options end point to populate dropdowns + fetchJson(`${apiBase}/options`, { signal: ac.signal }) + .then(setOptions) + .catch((e) => e.name !== "AbortError" && setErr(e)) + .finally(() => setLoading(false)); + + return () => ac.abort(); + }, [apiBase]); + + // Emit query string whenever filters change + useEffect(() => { + const qs = buildGasQuery(filters); + onChange?.(qs, filters); + }, [filters, onChange]); + + const volcanoList = useMemo( + () => filterList(options?.volcanoname, volcanoSearch), + [options, volcanoSearch] + ); + + const compoundList = useMemo( + () => filterList(options?.compoundname, compoundSearch), + [options, compoundSearch] + ); + + if (loading) { + return ( + + + Loading Filters + + ); + } + + if (err) { + return ( + + Couldn't load filter options + + {String(err.message ?? err)} + + + ); + } + + return ( + + + Gas Filters + + + + + {/*Volcano multi-select*/} + + Volcano + setVolcanoSearch(e.target.value)} + mb={2} + /> + + setFilters((f) => ({ ...f, volcanoname: vals })) + } + > + + {volcanoList.map((v) => ( + + {v} + + ))} + + + + + {/* Compound multi-select */} + + Compound + setCompoundSearch(e.target.value)} + mb={2} + /> + + setFilters((f) => ({...f, compoundname: vals})) + } + > + + {compoundList.map((c) => ( + + {c} + + ))} + + + + + {/* Date Range */} + + + From + + setFilters((f) => ({...f, date_from: e.target.value })) + } + /> + + + To + + setFilters((f) => ({...f, date_to: e.target.value })) + } + /> + + + + + ); +} \ No newline at end of file diff --git a/client/src/components/Canvas/CanvasComponents/GasWidget.jsx b/client/src/components/Canvas/CanvasComponents/GasWidget.jsx new file mode 100644 index 0000000..e1ed1f7 --- /dev/null +++ b/client/src/components/Canvas/CanvasComponents/GasWidget.jsx @@ -0,0 +1,264 @@ +/** + * Gas Measurements Widget + * + * Renders a Plotly time-series chart of gas measurement values returned from the API. + * Fetches rows from the backend, normalizes them into plottable points, groups points by compound, + * and displays one trace per compound. + */ +import { useEffect, useMemo, useState } from "react"; +import Plot from "react-plotly.js"; +import { Box, Center, Spinner, Text } from "@chakra-ui/react"; +import Widget from "./Widget"; +// import GasFilter from "./GasFilter"; + +const API_BASE = "http://localhost:8000"; // Possibly import.meta.env.VITE_API_BASE_URL + +/** + * Fetches JSON from a URL and throws a helpful error if the response is not OK. + * + * @param {string} url URL to request + * @param {object} [options] Optional options object + * @param {AbortSignal} [options.signal] Abort signal used to cancel the request + * @returns {Promise} Parsed JSON response body + * @throws {Error} If the HTTP response is not OK (non-2xx) + */ +async function fetchJson(url, { signal } = {}) { + const res = await fetch(url, { signal }); + if (!res.ok) { + const body = await res.text().catch(() => ""); + throw new Error(`HTTP ${res.status}: ${body || res.statusText}`); + } + return res.json(); +} + +/** + * Safely converts an input value into a finite number; otherwise returns null. + * Useful for filtering out non-numeric measurement values before plotting. + * + * @param {any} value Input value to check/convert + * @param {any} [rawvalue] Optional original/raw value (currently unused) + * @returns {number|null} Finite numeric value, or null if invalid + */ +function toNumberOrNull(value, rawvalue) { + if (typeof value === "number" && Number.isFinite(value)) + return value; + else + return null; +} + +/** + * Groups an array of items into a Map based on the provided key function. + * + * @template T + * @param {T[]} arr Array of items to group + * @param {(item: T) => any} keyFn Function that returns the grouping key for an item + * @returns {Map} Map from key -> array of items for that key + */ +function groupBy(arr, keyFn) { + const m = new Map(); + for (const item of arr) { + const k = keyFn(item); + if (!m.has(k)) m.set(k, []); + m.get(k).push(item); + } + return m; +} + +/** + * Normalizes raw API rows into Plotly-friendly point objects. + * Filters out rows missing a valid numeric value or sample date, and sorts by date ascending. + * + * @param {Array} rows Raw rows returned by the API + * @returns {Array} Normalized points with x/y and helpful metadata for hover tooltips + */ +function normalizeRowsToPoints(rows) { + const points = []; + for (const r of rows ?? []) { + const y = toNumberOrNull(r.value); + if (y === null) continue; + if (!r.sampledate) continue; + + points.push({ + x: r.sampledate, + y, + compound: r.compoundname ?? "unknown", + volcanoname: r.volcanoname ?? "", + sitename: r.sitename ?? "", + meta: r, + }); + } + points.sort((a, b) => (a.x < b.x ? -1 : a.x > b.x ? 1 : 0)); + return points; +} + +/** + * GasWidget React component. + * + * Fetches gas measurement rows from the backend and renders a multi-trace Plotly time series. + * Each trace represents a different compound (e.g., SO2, CO2), drawn as lines+markers. + * + * @param {object} props Component props + * @param {string} props.id Widget instance id (used by the Widget wrapper) + * @param {boolean} props.isEditable Whether the dashboard is in edit mode + * @param {boolean} props.isDelete Whether the dashboard is in delete mode + * @param {(id: string) => void} props.DeleteWidget Callback to delete this widget + * @returns {JSX.Element} Rendered widget UI + */ +export default function GasWidget({ id, config, isEditable, isDelete, DeleteWidget }) { + const [rows, setRows] = useState([]); + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + const volcano = config?.volcano_name ?? null; + const site = config?.site_name ?? null; + const compounds = Array.isArray(config?.compound_name) ? config.compound_name: []; + const start_date = config?.start_date ?? null; + const end_date = config?.end_date ?? null; + + const compoundsKey = useMemo(() => compounds.slice().sort().join("|"), [compounds]); + + const endpoint = useMemo(() => { + const params = new URLSearchParams(); + params.set("include_measurements", "true"); + params.set("include_personnel", "false"); + + // Supported /search filters + if (volcano) params.set("volcano_name", volcano); + if (site) params.set("site_name", site); + for (const c of compounds) params.append("compound_name", c); + if (start_date) params.set("start_date", start_date); + if (end_date) params.set("end_date", end_date); + + return `${API_BASE}/search/?${params.toString()}`; + }, [volcano, site, compoundsKey, start_date, end_date]); + + // DEBUG + useEffect(() => { + console.log("[GasWidget] endpoint:", endpoint) + }, [endpoint]); + + useEffect(() => { + console.log("[GasWidget] volcano_name:", volcano); + }, [volcano]); + + /** + * Fetch rows on mount (and when the endpoint changes). + * Uses AbortController to cancel in-flight requests on unmount or dependency change. + */ + useEffect(() => { + const ac = new AbortController(); + setIsLoading(true); + setError(null); + + fetchJson(endpoint, { signal: ac.signal }) + .then(setRows) + .catch((e) => e.name !== "AbortError" && setError(e)) + .finally(() => setIsLoading(false)); + + return () => ac.abort(); + }, [endpoint]); + + /** + * Normalized point data derived from raw rows. + * Memoized to avoid recomputation unless rows change. + */ + const points = useMemo(() => normalizeRowsToPoints(rows), [rows]); + + /** + * Plotly trace data (one trace per compound). + * Groups points by compound name and creates a scatter trace with lines+markers. + */ + const plotData = useMemo(() => { + const byCompound = groupBy(points, (p) => p.compound); + const traces = []; + + for (const [compound, pts] of byCompound.entries()) { + traces.push({ + x: pts.map((p) => p.x), + y: pts.map((p) => p.y), + name: compound, + type: "scatter", + mode: "lines+markers", + text: pts.map((p) => `${p.volcanoname} - ${p.sitename}`), + hovertemplate: + "Date: %{x}
Value: %{y}
%{text}" + compound + "", + }); + } + + traces.sort((a, b) => a.name.localeCompare(b.name)); + return traces; + }, [points]); + + /** + * Plotly layout configuration for axis, margins, and legend placement. + * Memoized once since it is static. + */ + const layout = useMemo( + () => ({ + // title: { text: "Gas Measurements" }, // This is redundant + autosize: true, + margin: { l: 55, r: 20, t: 20, b: 30 }, + xaxis: { title: "Sample Date", type: "date" }, + yaxis: { title: "Value" }, + legend: { + orientation: "h", + x: 0, + y: -0.25, // push legend below plot area + xanchor: "left", + yanchor: "top", + font: { size: 10 }, + itemwidth: 80, // helps wrap/pack items tighter + }, + }), + [] + ); + + return ( + + + {/* setQueryString(qs)} + /> */} + + {isLoading && ( +
+ +
+ )} + + {!isLoading && error && ( +
+ Couldn't load data + + {String(error.message ?? error)} + +
+ )} + + {!isLoading && !error && points.length === 0 && ( +
+ No numeric values found +
+ )} + + {!isLoading && !error && points.length > 0 && ( + + + + )} +
+
+ ); +} \ No newline at end of file diff --git a/client/src/components/Canvas/CanvasComponents/MapButtonControls.jsx b/client/src/components/Canvas/CanvasComponents/MapButtonControls.jsx index 5bcab69..0446a58 100644 --- a/client/src/components/Canvas/CanvasComponents/MapButtonControls.jsx +++ b/client/src/components/Canvas/CanvasComponents/MapButtonControls.jsx @@ -38,7 +38,7 @@ export default function MapButtonControls({settingsBtn, position = "topright"}) return () => { control.remove(); }; - }, [map, position, settingsBtn ]); + }, [map, position]); return container ? createPortal( settingsBtn, diff --git a/client/src/components/Canvas/CanvasComponents/MapFilter.jsx b/client/src/components/Canvas/CanvasComponents/MapFilter.jsx new file mode 100644 index 0000000..6aacb46 --- /dev/null +++ b/client/src/components/Canvas/CanvasComponents/MapFilter.jsx @@ -0,0 +1,130 @@ +import { Box, Checkbox, CheckboxGroup, Separator , chakra, CloseButton, Text, IconButton, Accordion, RadioGroup, } from "@chakra-ui/react"; +import { useState } from "react"; +import { CiFilter } from "react-icons/ci"; + +function GenerateFilters ({config, selected, setFilterValue, toggleInArray}){ + return ( + + {config.map((f) => { + if (f.type === "checkbox") { + const current = Array.isArray(selected?.[f.id]) ? selected[f.id] : []; + + return ( + + + + + {f.label} + {current.length ? ` (${current.length})` : ""} + + + + + + {f.options.map((opt) => { + const checked = current.includes(opt.value); + + return ( + toggleInArray(f.id, opt.value)} + > + + + {opt.label} + + ); + })} + + + + ); + } + if (f.type === "radio") { + const current = selected?.[f.id] ?? ""; + + return ( + + + + + {f.label} + {current ? ` (${current})` : ""} + + + + + + setFilterValue(f.id, details.value)} + > + + {f.options.map((opt) => ( + + + + {opt.label} + + ))} + + + + + + ); + } + + return null; + })} + + ); + +} + +function Header(props) { + const { setOpen } = props; + return ( + + Filters + setOpen(false)}/> + + ); +} + + +export default function MapFilter({config, selected, setFilterValue, toggleInArray}) { + const [open, setOpen] = useState(false); + + return open ? ( + +
+ + + + ) : ( + setOpen(true)} + > + + + ); + +} \ No newline at end of file diff --git a/client/src/components/Canvas/CanvasComponents/MapLegend.jsx b/client/src/components/Canvas/CanvasComponents/MapLegend.jsx new file mode 100644 index 0000000..321d1df --- /dev/null +++ b/client/src/components/Canvas/CanvasComponents/MapLegend.jsx @@ -0,0 +1,49 @@ + +import {Menu, MenuTrigger, MenuContent, MenuPositioner, Portal, chakra, Text, CloseButton, Box, Separator} from "@chakra-ui/react"; +import { IconButton } from "@chakra-ui/react"; +import {useState} from "react"; +import { IoIosInformationCircleOutline } from "react-icons/io"; + + +function Header(props) { + const { setOpen } = props; + return ( + + Legend + setOpen(false)}/> + + ); +} + + +export default function MapLegend({legend}) { + const [open, setOpen] = useState(false); + + return open ? ( + +
+ {legend} + + + + ) : ( + setOpen(true)} + > + + + ); + +} \ No newline at end of file diff --git a/client/src/components/Canvas/CanvasComponents/MapSettingsButton.jsx b/client/src/components/Canvas/CanvasComponents/MapSettingsButton.jsx index 0011f88..ef0d6fa 100644 --- a/client/src/components/Canvas/CanvasComponents/MapSettingsButton.jsx +++ b/client/src/components/Canvas/CanvasComponents/MapSettingsButton.jsx @@ -1,4 +1,4 @@ -import { Box, Checkbox, Separator , CloseButton, Text, chakra, Field, IconButton, Icon, Input, Fieldset, Flex } from "@chakra-ui/react"; +import { Box, Checkbox, Separator , CloseButton, Text, Field, chakra, IconButton, Icon, Input, Fieldset, Flex } from "@chakra-ui/react"; import { useState } from "react"; import { FiSettings } from "react-icons/fi"; import { IoIosArrowDown } from "react-icons/io"; diff --git a/client/src/components/Canvas/CanvasComponents/PlaceholderWidget.jsx b/client/src/components/Canvas/CanvasComponents/PlaceholderWidget.jsx new file mode 100644 index 0000000..99d985e --- /dev/null +++ b/client/src/components/Canvas/CanvasComponents/PlaceholderWidget.jsx @@ -0,0 +1,33 @@ +import Widget from "src/components/Canvas/CanvasComponents/Widget.jsx"; +import { Box, Text } from "@chakra-ui/react"; + +export default function PlaceholderWidget({ + title = "Widget", + id, + isEditable, + isDelete, + DeleteWidget +}) { return ( + + + + {title} widget coming soon + + + + ); +} diff --git a/client/src/components/Canvas/CanvasComponents/SettingsButton.jsx b/client/src/components/Canvas/CanvasComponents/SettingsButton.jsx index dca8273..236fabf 100644 --- a/client/src/components/Canvas/CanvasComponents/SettingsButton.jsx +++ b/client/src/components/Canvas/CanvasComponents/SettingsButton.jsx @@ -3,9 +3,16 @@ import { IoIosArrowBack } from "react-icons/io"; import { FiSettings } from "react-icons/fi"; import WidgetMenu from "./WidgetMenu"; -export function SettingsButton({ onEditWidgets, appendWidget}) { - const triggerItem = (<> Add Widget ); - const WidgetMenuProps = { appendWidget, triggerItem } +export function SettingsButton({ view, onEditWidgets, appendWidget}) { + const triggerItem = ( + <> + + Add Widget + + ); + + const WidgetMenuProps = { view, appendWidget, triggerItem } + return ( @@ -13,11 +20,12 @@ export function SettingsButton({ onEditWidgets, appendWidget}) { + - + Edit Widgets diff --git a/client/src/components/Canvas/CanvasComponents/WidgetInspector.jsx b/client/src/components/Canvas/CanvasComponents/WidgetInspector.jsx new file mode 100644 index 0000000..9d61c48 --- /dev/null +++ b/client/src/components/Canvas/CanvasComponents/WidgetInspector.jsx @@ -0,0 +1,529 @@ +import { + Box, + CloseButton, + Text, + Input, + Button, + Stack, + HStack, + Select, + RadioGroup, + Checkbox, + CheckboxGroup, + Code, + Field, + Fieldset, + createListCollection, +} from "@chakra-ui/react"; +import { useEffect, useMemo, useState } from "react"; +import { WIDGET_INSPECTOR_REGISTRY } from "@/constants/widgetInspectorRegistry"; + +const API_BASE = "http://localhost:8000"; + +async function fetchJson(url, { signal } = {}) { + const res = await fetch(url, { signal }); + if (!res.ok) { + const body = await res.text().catch(() => ""); + throw new Error(`HTTP ${res.status}: ${body || res.statusText}`); + } + return res.json(); +} + +function getByPath(obj, path) { + if (!obj || !path) return undefined; + return path.split(".").reduce((acc, k) => (acc == null ? acc : acc[k]), obj); +} + +function buildQueryString(draft) { + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(draft)) { + if (value == null) continue; + // array (checkbox group) + if (Array.isArray(value)) { + if (value.length === 0) continue; + value.forEach(v => params.append(key, v)); + continue; + } + // scalar + if (value === "") continue; + params.append(key, value); + } + return params.toString(); +} + +export default function WidgetInspector({ + open, + onClose, + target, + widgetArray, + updateWidgetConfig +}) { + if (!open || !target) return null; + + const { id, widgetKey } = target; + const widget = widgetArray.find(w => w.i === id); + const def = WIDGET_INSPECTOR_REGISTRY[widgetKey]; + + if (!widget || !def) return null; + + // current saved config + const cfg = widget.config ?? {}; + + // local draft config (only applied on Submit) + const initialDraft = useMemo(() => { + const base = def?.defaultConfig ?? {}; + return {...base, ...cfg,}; + }, [def, cfg]); + + const [draft, setDraft] = useState(initialDraft); + + // reset draft when switching widgets or repoening + useEffect(() => setDraft(initialDraft), [initialDraft]); + + // source payloads (e.g. { options: { volcanoname:[], ... } }) + // const [options, setOptions] = useState({ + // volcanoname: [], + // compoundname: [], + // sitename: [], + // date_min: null, + // date_max: null, + // }); + + const [sources, setSources] = useState({}); + const [optError, setOptError] = useState(null); + const [optLoading, setOptLoading] = useState(false); + + useEffect(() => { + if (!open) return; + const ac = new AbortController(); + + const srcDefs = def.optionSources ?? {}; + const entries = Object.entries(srcDefs); + + if (entries.length === 0) { + setSources({}); + setOptLoading(false); + setOptError(null); + return; + } + + setOptLoading(true); + setOptError(null); + + Promise.all( + entries.map(async ([name, src]) => { + const url = + typeof src.url === "function" + ? src.url({ draft }) + : src.url; + + // DEBUG + console.log("Fetching options source:", name, `${API_BASE}${url}`) + + const data = await fetchJson(`${API_BASE}${url}`, { signal: ac.signal }); + return [name, data]; + }) + ) + .then((pairs) => setSources(Object.fromEntries(pairs))) + .catch((e) => e.name !== "AbortError" && setOptError(e)) + .finally(() => setOptLoading(false)); + + return () => ac.abort(); + }, [open, def, draft?.volcano_name]); + + // DEBUG + useEffect(() => { + if (open) console.log("sources", sources); + }, [open, sources]); + + const patchDraft = (partial) => setDraft((d) => ({ ...d, ...partial })); + + const validationError = useMemo(() => { + for (const f of def.fields ?? []) { + if (typeof f.validate === "function") { + const msg = f.validate(draft, sources); + if (msg) return msg; + } + } + return null; + }, [def, draft, sources]); + + const onSubmit = () => { + if (validationError) return; + + const keys = def.submitKeys ?? Object.keys(def.defaultConfig ?? {}); + const patch = {}; + + for (const k of keys) patch[k] = draft[k] ?? null; + + // DEBUG + console.log("submit keys:", keys); + console.log("draft:", draft); + console.log("patch:", patch); + + // Build and log query string for debugging + const queryString = buildQueryString(patch); + console.log("Query string:", `?${queryString}`); + // Full URL + console.log("Full URL:", `${API_BASE}/search?${queryString}`); + + updateWidgetConfig(id, patch); + onClose(); + }; + + const onReset = () => { + setDraft(def?.defaultConfig ?? {}); + }; + + // --- Field renderers i.e. Radio, Checkbox, Select, Search --- + + // 'Select' Field Handler + const renderField = (field) => { + if (field.type === "select") { + const srcObj = sources[field.optionsFrom?.source]; + const raw = getByPath(srcObj, field.optionsFrom?.path) ?? []; + + // DEV WARNING + if (process.env.NODE_ENV !== "production" && raw != null && !Array.isArray(raw)) { + console.warn( + `[WidgetInspector] optionsFrom path "${field.optionsFrom.path}" did not resolve to an array`, + { + widget: widgetKey, + field: field.key, + source: field.optionsFrom.source, + path: field.optionsFrom.path, + resolvedValue: raw, + } + ); + } + + const items = [ + { label: field.placeholder ?? "(all)", value: "" }, + ...raw.map((v) => ({ label: String(v), value: String(v) })), + ]; + + const collection = createListCollection({ items }); + + const current = draft[field.key] ?? ""; + const value = current ? [current] : []; // empty means (all) + + return ( + + {field.label} + { + const next = e.value?.[0] ?? ""; // "" is "(all)" + const clears = field.clearOnChange ?? []; + const partial = { [field.key]: next || null }; + for (const c of clears) partial[c] = null; + + patchDraft(partial); + }} + disabled = { optLoading || items.length <= 1 } // only "(all)" exists + + // size="sm" + // placeholder={optLoading ? "Loading…" : field.placeholder ?? "(all)"} + // value={draft[field.key] ?? ""} + // isDisabled={optLoading || opts.length === 0} + // onChange={(e) => { + // const v = e.target.value || null; + + // // handle clears on change + // const clears = field.clearOnChange ?? []; + // const partial = { [field.key]: v }; + // for (const c of clears) partial[c] = null; + + // patchDraft(partial); + // }} + > + + + + + + {items.map((it) => ( + + {it.label} + + + ))} + + {/* {opts.map((x) => ( + + ))} */} + + + ); + } + + // 'Radio Button' Field Handler + if (field.type === "radio") { + const srcObj = sources[field.optionsFrom?.source]; + const raw = getByPath(srcObj, field.optionsFrom?.path); + + if (process.env.NODE_ENV !== "production" && raw != null && !Array.isArray(raw)) { + console.warn( + `[WidgetInspector] optionsFrom path "${field.optionsFrom.path}" did not resolve to an array`, + { + widget: widgetKey, + field: field.key, + source: field.optionsFrom.source, + path: field.optionsFrom.path, + resolvedValue: raw, + } + ); + } + + const options = Array.isArray(raw) ? raw : []; + const isMulti = field.mode === "multiple"; + const value = isMulti + ? Array.isArray(draft[field.key]) ? draft[field.key] : [] + : draft[field.key] ?? ""; + + return ( + + {field.label} + + { + if (isMulti) { + patchDraft({ [field.key]: e.value }); + } else { + patchDraft({ [field.key]: e.value[0] ?? null }); + } + }} + > + + {options.map((opt) => ( + + + + {opt} + + ))} + + + + ); + } + + // 'Checkbox' Field Handler + if (field.type === "checkbox") { + const srcObj = sources[field.optionsFrom?.source]; + const raw = getByPath(srcObj, field.optionsFrom?.path); + + if (process.env.NODE_ENV !== "production" && raw != null && !Array.isArray(raw)) { + console.warn( + `[WidgetInspector] optionsFrom path "${field.optionsFrom.path}" did not resolve to an array`, + { + widget: widgetKey, + field: field.key, + source: field.optionsFrom.source, + path: field.optionsFrom.path, + resolvedValue: raw, + } + ); + } + + // const options = Array.isArray(raw) ? raw.map(String) : []; + + // items: [{ label, value }] + const items = (Array.isArray(raw) ? raw : []).map((x) => ({ + label: String(x), + value: String(x), + })); + + // const selected = Array.isArray(draft[field.key]) ? draft[field.key] : []; + const value = Array.isArray(draft[field.key]) ? draft[field.key].map(String) : []; + + // Can be used to validate fields ~ optional + const invalid = typeof field.validate === "function" ? !!field.validate(draft, sources) : false; + + return ( + + {field.label} + + { + console.log("group next:", next) + patchDraft({ [field.key]: next }) + }} + > + + {items.map((item) => ( + + + + + + {item.label} + + ))} + + + + + {value.length} selected + + + + {invalid && field.errorText && ( + {field.errorText} + )} + + ); + } + // Date Range Handler + if (field.type === "dateRange") { + const srcObj = sources[field.minFrom?.source]; + const min = getByPath(srcObj, field.minFrom?.path) ?? undefined; + const max = getByPath(srcObj, field.maxFrom?.path) ?? undefined; + + const startKey = field.startKey; + const endKey = field.endKey; + + return ( + + {field.label} + + + patchDraft({ [startKey]: e.target.value || null })} + /> + + to + + patchDraft({ [endKey]: e.target.value || null })} + /> + + + {!!validationError && ( + + {validationError} + + )} + + + {(field.presets ?? []).map((p) => { + const variant = p.variant ?? "outline"; + return ( + + ); + })} + + + ); + } + + // fallback (future types) + return null; + }; + + return ( + + + {def.title ?? widgetKey} + + + + {/* Options loading/error */} + + {optLoading && Loading options…} + {!optLoading && optError && ( + Options failed: {String(optError.message ?? optError)} + )} + + + {/* Debug */} + {/* + + Saved config + + {JSON.stringify(cfg ?? {}, null, 2)} + + Draft (pending) + + {JSON.stringify(draft ?? {}, null, 2)} + */} + + {/* Fields */} + + {(def.fields ?? []).map(renderField)} + + {/* Actions */} + + + + + + + ); +} diff --git a/client/src/components/Canvas/CanvasComponents/WidgetMenu.jsx b/client/src/components/Canvas/CanvasComponents/WidgetMenu.jsx index 0bf3f66..0128464 100644 --- a/client/src/components/Canvas/CanvasComponents/WidgetMenu.jsx +++ b/client/src/components/Canvas/CanvasComponents/WidgetMenu.jsx @@ -1,23 +1,25 @@ import { Menu, Portal } from "@chakra-ui/react"; -import AlertLevel from "./AlertLevelWidget"; +import { getWidgetsForView } from "@/constants/widgetRegistry"; +// import AlertLevel from "./AlertLevelWidget"; -export default function WidgetMenu({appendWidget, triggerItem, positioning}) { +export default function WidgetMenu({view, appendWidget, triggerItem, positioning}) { + const entries = getWidgetsForView(view); + return ( {triggerItem} - appendWidget(AlertLevel, 4, 7, 4, 7, 8, 14)} // width and height are passed through here for each widget + {entries.map((w) => ( + appendWidget(w.key)} > - Alert Level + {w.label} - CO2 - IVANS - Plume Heights - Alert Level Changes + ))} diff --git a/client/src/components/Canvas/views/Admin.jsx b/client/src/components/Canvas/views/Admin.jsx index 8deb1d3..445151a 100644 --- a/client/src/components/Canvas/views/Admin.jsx +++ b/client/src/components/Canvas/views/Admin.jsx @@ -1,5 +1,6 @@ import { Flex, Tabs, HStack } from '@chakra-ui/react'; import { MEDataEntry } from './AdminTabs/MEDataEntry'; +import { NVEWS } from './AdminTabs/NVEWS' import { ProjectCollector } from './AdminTabs/ProjectCollector'; import { NASApplication } from './AdminTabs/NASApplication'; import { IVANS } from './AdminTabs/IVANS' @@ -30,7 +31,7 @@ export default function Admin({ view, onChangeView, onEditWidgets, appendWidget as="header" position="sticky" top="0" - zIndex="dropdown" // zIndex: 1100 + zIndex={2000} // zIndex: 1100 bg="base200" pl={5} pr={2} @@ -42,25 +43,28 @@ export default function Admin({ view, onChangeView, onEditWidgets, appendWidget > {/* HEADER */} - + {/* - + */} {/* CANVAS */} - - - - - - - - - - - + + + + + + + + + + + + + + ); } \ No newline at end of file diff --git a/client/src/components/Canvas/views/AdminTabs/MEDataEntry.jsx b/client/src/components/Canvas/views/AdminTabs/MEDataEntry.jsx index f3ee9ba..08bb518 100644 --- a/client/src/components/Canvas/views/AdminTabs/MEDataEntry.jsx +++ b/client/src/components/Canvas/views/AdminTabs/MEDataEntry.jsx @@ -15,7 +15,7 @@ const yearItems = Array.from( ); const years = createListCollection({ -items: yearItems.reverse(), + items: yearItems.reverse(), }); const quarters = createListCollection({ @@ -200,7 +200,7 @@ export function MEDataEntry() { - + Number of people benefitting from VDAP activities, disaggregate by sex diff --git a/client/src/components/Canvas/views/AdminTabs/NVEWS.jsx b/client/src/components/Canvas/views/AdminTabs/NVEWS.jsx new file mode 100644 index 0000000..10cba7b --- /dev/null +++ b/client/src/components/Canvas/views/AdminTabs/NVEWS.jsx @@ -0,0 +1,236 @@ +import React from "react"; +import { Box, Text, Accordion, Table, Separator, Center,Button,Link} from "@chakra-ui/react"; +import { useEffect, useState, useMemo } from "react"; +import Map from "../NVEWSMap.jsx"; +import {mapFilterConfig} from "@/utils/map-utils/map-configs/nvewsMapFilterConfig.js"; +import { pctStyles } from "@/utils/pctStyles.js"; + +const SECTIONS = [ + { + id: "volcano", + title: "Volcano", + fields:[ + {label: "VNUM", key: "vnum"}, + {label: "State", key: "volcano_subgroup"}, + {label: "Threat Level", key: "nvews_threat_text"}, + {label: "Observatory", key: "volcano_group"}, + {label: "Network Completion %",key: "network_pct_complete"}, + {label: "Network Area (km2)", key: "network_area_km2"}, + {label: "Polygon dimensions (km x km or km if radius)", key: "r_or_p_dimensions_km"}, + ] + + }, + { + id: "seismic", + title: "Seismic", + fields:[ + {label: "Stations", key: "seismic_station_total"}, + {label: "Target", key: "seismic_target"}, + {label: "% Complete", key: "seismic_pct_complete"}, + ] + }, + { + id: "gnss", + title: "GNSS", + fields:[ + {label: "Stations", key: "gnss_station_total"}, + {label: "Target", key: "gnss_target"}, + {label: "% Complete", key: "gnss_pct_complete"}, + ] + }, + { + id: "cameras", + title: "Cameras", + fields:[ + {label: "Stations", key: "camera_station_total"}, + {label: "Target", key: "camera_target"}, + {label: "% Complete", key: "camera_pct_complete"}, + ] + }, + { + id: "infrasound", + title: "Infrasound", + fields:[ + {label: "Stations", key: "infrasound_station_total"}, + {label: "Target", key: "infrasound_target"}, + {label: "% Complete", key: "infrasound_pct_complete"}, + ] + }, + { + id: "tilt", + title: "Tilt", + fields:[ + {label: "Stations", key: "tiltmeter_station_total"}, + {label: "Target", key: "tiltmeter_target"}, + {label: "% Complete", key: "tiltmeter_pct_complete"}, + ] + }, + { + id: "real-time gas", + title: "Real-Time Gas", + fields:[ + {label: "Stations", key: "gas_station_total"}, + {label: "Target", key: "gas_target"}, + {label: "% Complete", key: "gas_pct_complete"}, + ] + }, + +]; + +const COLUMNS = SECTIONS.flatMap(section => + section.fields.map(field => ({ + id: `${section.id}.${field.key}`, + sectionTitle: section.title, + label: field.label, + key: field.key, + isPercent: field.label.includes('%') + })) +); + + +async function fetchData(){ + const res = await fetch("https://dev-vhptools.usgs.gov/vmidapi-free/reports/nvewsCompletionStatus"); + if(!res.ok) throw new Error(res.statusText); + return res.json(); +} + +export function NVEWS() { + + const origin = [45.61142478054226, -122.49630033167544]; + const zoom = 3; + const type = ["https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",'© OpenStreetMap contributors']; + + + const [data, setData] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + const [selectedFilters, setSelectedFilters] = useState(() => { + const initial = {}; + for (const f of mapFilterConfig) initial[f.id] = f.defaultValue; + return initial; + }); + const [selectedVolcano, setSelectedVolcano] = useState(null); + + + function handleTableClick(v){ + setSelectedVolcano([v.latitude, v.longitude]); + } + + function setFilterValue(id, value) { + setSelectedFilters(prev => ({ ...prev, [id]: value })); + } + + function toggleInArray(id, value) { + setSelectedFilters(prev => { + const arr = Array.isArray(prev[id]) ? prev[id] : []; + const next = arr.includes(value) ? arr.filter(x => x !== value) : [...arr, value]; + return { ...prev, [id]: next }; + }); + } + + function applyDataFilters(rows, config, selected) { + return rows.filter(row => { + return config.every(f => { + if (f.target?.kind !== "data") return true; + + const sel = selected[f.id]; + if (sel == null) return true; + if (Array.isArray(sel) && sel.length === 0) return true; + + const value = row[f.target.field]; + + switch (f.target.op) { + case "in": + return Array.isArray(sel) ? sel.includes(value) : true; + case "eq": + return value === sel; + default: + return true; + } + }); + }); + } + + + useEffect(() => { + fetchData() + .then(setData) + .catch(err => setError(err.message)) + .finally(() => setLoading(false)); + },[]); + + + if (error) return
{error}
; + if (loading) return

Loading...

; + console.log("raw",data) + + const filteredData = applyDataFilters(data ?? [], mapFilterConfig, selectedFilters); + console.log("filtered",filteredData); + + + + return( + + + + + {/**/} +
+ + NVEWS Completion Status - + + View Full Report + + +
+ + + + + + Name + {SECTIONS.map(s => ( + + {s.title} + + ))} + + + + {COLUMNS.map(c => ( + {c.label} + ))} + + + + + {filteredData.map(v => ( + + handleTableClick(v)}> + {v.volcano_name} + + {COLUMNS.map(c => { + const value = v?.[c.key]; + const pct = Number(String(value).replace("%","")); + return ( + handleTableClick(v)} + cursor="pointer" _hover={{textDecoration: "underline"}}> + {value ?? "-"} + + ); + })} + + ))} + + + + + +
+ + ) + +} \ No newline at end of file diff --git a/client/src/components/Canvas/views/GlobalMap.jsx b/client/src/components/Canvas/views/GlobalMap.jsx index 7b0f7af..7c6e066 100644 --- a/client/src/components/Canvas/views/GlobalMap.jsx +++ b/client/src/components/Canvas/views/GlobalMap.jsx @@ -3,21 +3,23 @@ import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; import MapButtonControls from "../CanvasComponents/MapButtonControls"; import MapSettingsButton from "../CanvasComponents/MapSettingsButton"; -export default function GlobalMap() { - const position = [45.61142478054226, -122.49630033167544]; // [latitude, longitude] +export default function GlobalMap({markers = []}) { + const origin = [45.61142478054226, -122.49630033167544]; // [latitude, longitude] return ( - + } position="topright" /> - - - CVO — Home of the volcanoligists - - + {markers.map(m => ( + + + {m.label} + + + ))} ); } \ No newline at end of file diff --git a/client/src/components/Canvas/views/Home.jsx b/client/src/components/Canvas/views/Home.jsx index a04309f..c3944f1 100644 --- a/client/src/components/Canvas/views/Home.jsx +++ b/client/src/components/Canvas/views/Home.jsx @@ -1,10 +1,12 @@ -import { HStack, Flex, Tabs } from "@chakra-ui/react"; +import { useState } from "react"; +import { HStack, Flex, Tabs, Box } from "@chakra-ui/react"; import MyDashboard from "./HomeTabs/MyDashboard"; import { SearchBar } from "../CanvasComponents/SearchBar"; import { SettingsButton } from "../CanvasComponents/SettingsButton"; import { DailyActivity } from "./HomeTabs/DailyActivity"; import { Gas } from "./HomeTabs/Gas"; import { Seismic } from "./HomeTabs/Seismic"; +import { Deformation } from "./HomeTabs/Deformation"; import { RemoteSensing } from "./HomeTabs/RemoteSensing"; import { CancelButton } from "../CanvasComponents/CancelButton"; import { SaveButton } from "../CanvasComponents/SaveButton"; @@ -12,44 +14,103 @@ import DeleteAllButton from "../CanvasComponents/DeleteAllButton"; import { v4 as uuid } from 'uuid'; import { CanvasHeaderTabs } from '../CanvasComponents/CanvasHeaderTabs'; import { DASHBOARD_TABS } from "@/constants/viewKeys"; +import { WIDGET_REGISTRY } from "@/constants/widgetRegistry"; +import { WIDGET_INSPECTOR_REGISTRY } from "@/constants/widgetInspectorRegistry"; +import WidgetInspector from "../CanvasComponents/WidgetInspector"; export default function Home(props) { // deconstructing props - const { view, layout, setLayout, setOriginalLayout, onLayoutChange, onChangeView, onCancel, setOriginalWidgets, - isDelete, setIsDelete, beginEditIfNeeded, isEditable, setIsEditable, widgetArray, setWidgetArray } = props; + const { view, layout, setLayout, onLayoutChange, onChangeView, onCancel, isDelete, setIsDelete, + beginEditIfNeeded, isEditable, setIsEditable, widgetArray, setWidgetArray } = props; - function appendWidget(name, w, h, minw, minh, maxw, maxh) { + const [inspectorOpen, setInspectorOpen] = useState(false); + const [inspectorTarget, setInspectorTarget] = useState(null); + + const openInspector = ({ id, widgetKey }) => { + setInspectorTarget({ id, widgetKey, view }); + setInspectorOpen(true); + }; + + function appendWidget(widgetKey, overrides = {}) { beginEditIfNeeded(); // Save state before anything else - + // Place canvas in edit mode upon adding a new widget + setIsEditable(true); + setIsDelete(true); + + const def = WIDGET_REGISTRY[widgetKey]; + if(!def) { + console.warn("appendWidget: unknown widgetKey:", widgetKey); + return; + }; + + // Widget Inspector Configuration Settings + const inspectorDef = WIDGET_INSPECTOR_REGISTRY[widgetKey]; + const defaultConfig = inspectorDef?.defaultConfig ?? {}; + const newID = uuid(); // could be replaced with something else like guid const COLS = 16; - const W = w ?? 3; - const H = h ?? 3; - setWidgetArray([ // updating widget array containing info on the types of widgets - ...widgetArray, - {i: newID, widget: name } - ]); - // onAddWidget(newID, w, h, minw, minh, maxw, maxh); // updating array containing info on layout of widgets + if (widgetKey === "gas_measurements") { + setInspectorTarget({ view, id: newID, widgetKey }); + setInspectorOpen(true); + } + const { + w = def.defaults.w, + h = def.defaults.h, + minW = def.defaults.minW, + minH = def.defaults.minH, + maxW = def.defaults.maxW, + maxH = def.defaults.maxH, + } = overrides; + + // DEBUG + console.log("after add:", { widgetKey, layoutLen: layout.length, widgetLen: widgetArray.length }); + + // IMPORTANT: use functional updates so you don't rely on stale widgetArray/layout + setWidgetArray(prev => [ + ...prev, + { + i: newID, + widgetKey, + // config: JSON.parse(JSON.stringify(defaultConfig)), // Can use this if Safari complains + config: structuredClone(defaultConfig) + }, + ]); + setLayout(prev => [ ...prev, { i: newID, - x: (prev.length * W) % COLS, + x: (prev.length * w) % COLS, y: 0, - w: w, - h: h, - minW: minw, - minH: minh, - maxW: maxw, - maxH: maxh, + w, + h, + minW, + minH, + maxW, + maxH, static: false, resizeHandles: ['se'] }, ]); } + function updateWidgetConfig(id, patchOrFn) { + setWidgetArray(prev => + prev.map(w => { + if (w.i !== id) return w; + + const nextConfig = + typeof patchOrFn === "function" + ? patchOrFn(w.config ?? {}) + : {...(w.config ?? {}), ...(patchOrFn ?? {}) }; + + return { ...w, config: nextConfig }; + }) + ); + } + const onSave = () => { console.log("saving widgets..."); // Set all widgets back to static: true @@ -61,8 +122,6 @@ export default function Home(props) { ); setIsEditable(false); setIsDelete(false); - setOriginalLayout([]); // Clear backup layout - setOriginalWidgets([]); } const onEditWidgets = () => { @@ -97,8 +156,11 @@ export default function Home(props) { } // Prop Forwarding - const MyDashboardProps = { layout, onLayoutChange, isEditable, beginEditIfNeeded, isDelete, DeleteWidget, widgetArray, appendWidget }; - const settingsButtonProps = { onEditWidgets, appendWidget }; + const MyDashboardProps = { view, layout, onLayoutChange, isEditable, + beginEditIfNeeded, isDelete, DeleteWidget, widgetArray, appendWidget, + openInspector + }; + const settingsButtonProps = { view, onEditWidgets, appendWidget }; return ( /* HEADER */ @@ -150,10 +212,25 @@ export default function Home(props) { {/* CANVAS */} - + + + + + + {inspectorOpen && inspectorTarget && ( + setInspectorOpen(false)} + target={inspectorTarget} + widgetArray={widgetArray} + updateWidgetConfig={updateWidgetConfig} + /> + )} + + - + @@ -164,6 +241,9 @@ export default function Home(props) { + + + ); } \ No newline at end of file diff --git a/client/src/components/Canvas/views/HomeTabs/Deformation.jsx b/client/src/components/Canvas/views/HomeTabs/Deformation.jsx new file mode 100644 index 0000000..17a0c8f --- /dev/null +++ b/client/src/components/Canvas/views/HomeTabs/Deformation.jsx @@ -0,0 +1,22 @@ +import { Box, Text } from "@chakra-ui/react"; + +export function Deformation() { + return( + + + Deformation dashboard coming soon. + + + ); +} \ No newline at end of file diff --git a/client/src/components/Canvas/views/HomeTabs/MyDashboard.jsx b/client/src/components/Canvas/views/HomeTabs/MyDashboard.jsx index 91364e2..4b31270 100644 --- a/client/src/components/Canvas/views/HomeTabs/MyDashboard.jsx +++ b/client/src/components/Canvas/views/HomeTabs/MyDashboard.jsx @@ -2,9 +2,13 @@ import GridLayout, { WidthProvider } from 'react-grid-layout'; import { useSlotRecipe, chakra, Flex } from '@chakra-ui/react'; import CustomResizeHandle from '../../CanvasComponents/CustomResizeHandle'; import FirstWidgetButton from '../../CanvasComponents/FirstWidgetButton'; +import PlaceholderWidget from '../../CanvasComponents/PlaceholderWidget'; +import { WIDGET_REGISTRY, WIDGET_COMPONENTS } from '@/constants/widgetRegistry'; -export default function MyDashboard({layout, onLayoutChange, isEditable, isDelete, DeleteWidget, widgetArray, appendWidget}) { - console.log("rendering MyDashboard"); +export default function MyDashboard({view, layout, onLayoutChange, + isEditable, isDelete, DeleteWidget, widgetArray, appendWidget, openInspector +}) { + //console.log("rendering MyDashboard"); const recipe = useSlotRecipe({ key: "widget" }); const styles = recipe(); // using root recipe for widget below const ResponsiveGridLayout = WidthProvider(GridLayout); // canvas width now adjusts to window size @@ -12,8 +16,8 @@ export default function MyDashboard({layout, onLayoutChange, isEditable, isDelet const handleDragStop = () => { document.body.style.userSelect = ""; }; // allow text highlighting all other times let merged = []; - console.log("widgetArray: ", widgetArray); - console.log("layout: ", layout); + //console.log("widgetArray: ", widgetArray); + //console.log("layout: ", layout); if (widgetArray.length && layout.length) { const widgetMap = new Map(widgetArray.map(item => [item.i, item])); @@ -36,7 +40,7 @@ export default function MyDashboard({layout, onLayoutChange, isEditable, isDelet bg="gray.150" borderRadius="md" > - + ); } @@ -59,11 +63,36 @@ export default function MyDashboard({layout, onLayoutChange, isEditable, isDelet position="relative" resizeHandle={} > - { merged.map((item) => { - const Widget = item.widget; + {merged.map((item) => { + const def = WIDGET_REGISTRY[item.widgetKey]; + + const isReal = def?.kind === "real"; + + const Widget = isReal + ? WIDGET_COMPONENTS[item.widgetKey] + : PlaceholderWidget; + + const title = + def?.PlaceholderTitle ?? + def?.label ?? + item.widgetKey ?? + "Widget"; + return ( - + openInspector({ id: item.i, widgetKey: item.widgetKey }) + : undefined + } + /> ); }) diff --git a/client/src/components/Canvas/views/NVEWSLegend.jsx b/client/src/components/Canvas/views/NVEWSLegend.jsx new file mode 100644 index 0000000..bcca5aa --- /dev/null +++ b/client/src/components/Canvas/views/NVEWSLegend.jsx @@ -0,0 +1,60 @@ +import {Box, Text, Flex, Image} from '@chakra-ui/react' +import volcanoDefault from "@/utils/map-utils/icons/green-triangle.svg"; +import redVolcano from "@/utils/map-utils/icons/red-triangle.svg"; +import yellowVolcano from "@/utils/map-utils/icons/yellow-triangle.svg"; +import seismicDefault from "@/utils/map-utils/icons/triangle-black.svg"; +import cameraDefault from "@/utils/map-utils/icons/camera.svg"; +import gpsDefault from "@/utils/map-utils/icons/gps-solid.svg"; +import soundIcon from "@/utils/map-utils/icons/sound-black.svg"; +import tiltIcon from "@/utils/map-utils/icons/line-segment.svg"; +import cloudIcon from "@/utils/map-utils/icons/cloud.svg"; + + +export const NVEWSLegend = ( + + + red volcano + 0–70% Complete + + + + yellow volcano + 70–100% Complete + + + + green volcano + 100% Complete + + + + green volcano + Seismometer + + + + green volcano + Camera + + + + green volcano + GPS + + + + green volcano + Infrasound + + + + green volcano + Tiltmeter + + + + green volcano + Gas + + +); \ No newline at end of file diff --git a/client/src/components/Canvas/views/NVEWSMap.jsx b/client/src/components/Canvas/views/NVEWSMap.jsx new file mode 100644 index 0000000..4c152de --- /dev/null +++ b/client/src/components/Canvas/views/NVEWSMap.jsx @@ -0,0 +1,168 @@ +import React, {useState} from "react"; +import { MapContainer, TileLayer, Marker, Popup, Circle, CircleMarker, Rectangle, Polygon,Polyline} from "react-leaflet"; +import { Box, Text } from "@chakra-ui/react"; +import MapButtonControls from "@/components/Canvas/CanvasComponents/MapButtonControls.jsx"; +import MapFilter from "../CanvasComponents/MapFilter.jsx"; +import MapLegend from "../CanvasComponents/MapLegend.jsx"; +import { NVEWSLegend } from "../views/NVEWSLegend.jsx"; +import {MapClickLogger} from "@/utils/map-utils/functions/mapHelpers.js"; +import {ZoomWatcher} from "@/utils/map-utils/functions/mapHelpers.js"; +import {FlyTo} from "@/utils/map-utils/functions/mapHelpers.js"; +import {createIcon} from "@/utils/map-utils/functions/markerHelpers.js"; +import {handleMarkerClick} from "@/utils/map-utils/functions/markerHelpers.js"; +import {computePolyPositions} from "@/utils/map-utils/functions/markerHelpers.js"; +import {shiftPos} from "@/utils/map-utils/functions/markerHelpers.js"; +import volcanoDefault from "@/utils/map-utils/icons/green-triangle.svg"; +import redVolcano from "@/utils/map-utils/icons/red-triangle.svg"; +import yellowVolcano from "@/utils/map-utils/icons/yellow-triangle.svg"; +import seismicDefault from "@/utils/map-utils/icons/triangle-black.svg"; +import cameraDefault from "@/utils/map-utils/icons/camera.svg"; +import gpsDefault from "@/utils/map-utils/icons/gps-solid.svg"; +import soundIcon from "@/utils/map-utils/icons/sound-black.svg"; +import tiltIcon from "@/utils/map-utils/icons/line-segment.svg"; +import cloudIcon from "@/utils/map-utils/icons/cloud.svg"; + + +export default function NVEWSMap ({type=[], origin=[], markers=[], mapFilters=[], zoom, selectedFilters, setFilterValue, + toggleInArray, selectedMarker, overlays = {}}) { + + const [target, setTarget] = useState(null); + const [zoomLevel, setZoomLevel] = useState(zoom); + + const link= type[0] + const attr = type[1] + const pos = [origin[0],origin[1]] + const enabledOverlays = selectedFilters?.overlay ?? []; + + + return ( + + + + + } + position="topright" /> + } + position="bottomright" /> + {enabledOverlays.map(id => ( + + {overlays[id] ?? null} + + ))} + + + + + {markers.map(marker => { + + let bounds = null; + let stationTypes = ["seismic_stations", "camera_stations", "gas_stations", "gnss_stations", "tiltmeter_stations", + "infrasound_stations"]; + let stationMarkers = []; + let volcanoIcon = null; + + const val = Number(String(marker.network_pct_complete).replace("%","")); + + if(val < 70){ + volcanoIcon = createIcon(redVolcano); + } + else if(val > 100){ + volcanoIcon = createIcon(yellowVolcano); + } + else{ + volcanoIcon = createIcon(volcanoDefault); + } + + if(zoomLevel >= 7 && marker.include_reason === "Within Radius"){ + bounds = ; + } + else if (zoomLevel >= 7 && marker.include_reason === "Within Polygon"){ + bounds = ; + } + + // const rawSelected = selectedFilters?.stations ?? []; + // + // const enabledStations = + // rawSelected.length === 0 || rawSelected.length === stationTypes.length + // ? stationTypes + // : rawSelected; + + const enabledStations = selectedFilters?.stations ?? []; + + if(zoomLevel >= 9){ + for(const station of stationTypes){ + if(marker[station].length > 0 && enabledStations.includes(station)){ + for(let i =0 ; i< marker[station].length; i++){ + stationMarkers.push(marker[station][i]); + } + } + } + } + + + return( + + handleMarkerClick(marker,setTarget) + }}> + + + {marker.volcano_name} + Network Completion: {marker.network_pct_complete} + Seismic Count: {marker.seismic_station_total} Target: {marker.seismic_target} + GNSS Count: {marker.gnss_station_total} Target: {marker.gnss_target} + Camera Count: {marker.camera_station_total} Target: {marker.camera_target} + InfraSound Count: {marker.infrasound_station_total} Target: {marker.infrasound_target} + Tilt Meter Count: {marker.tiltmeter_station_total} Target: {marker.tiltmeter_target} + Real-Time Gas Count: {marker.gas_station_total} Target: {marker.gas_target} + + + + {bounds} + {stationMarkers.map(subMarker => { + let iconType = null; + if(subMarker.category === "Seismometer"){ + iconType = createIcon(seismicDefault); + } + else if(subMarker.category === "Camera"){ + iconType = createIcon(cameraDefault); + } + else if(subMarker.category === "GPS"){ + iconType = createIcon(gpsDefault); + } + else if(subMarker.category === "Infrasound"){ + iconType = createIcon(soundIcon); + } + else if(subMarker.category === "Tiltmeter"){ + iconType = createIcon(tiltIcon); + } + else if(subMarker.category === "Gas"){ + iconType = createIcon(cloudIcon); + } + // TODO: Add other icons as needed + return ( + + + + {subMarker.station} + Type: {subMarker.category} + Sensor Total: {subMarker.sensor_total} + + + + ) + })} + + ) + })} + + ); +} \ No newline at end of file diff --git a/client/src/components/Dashboard.jsx b/client/src/components/Dashboard.jsx index 3f0dc4f..f3824fb 100644 --- a/client/src/components/Dashboard.jsx +++ b/client/src/components/Dashboard.jsx @@ -8,62 +8,127 @@ import DialogPopup from './Canvas/CanvasComponents/DialogPopup.jsx'; export default function Dashboard() { const recipe = useRecipe({ key: "dashboard" }); const styles = recipe(); - - const [darkMode, setDarkMode] = useState(false); - const [isEditable, setIsEditable] = useState(false); // editable=true means static=false (vice versa) - const [isDelete, setIsDelete] = useState(false); - const [originalLayout, setOriginalLayout] = useState([]); - const [originalWidgets, setOriginalWidgets] = useState([]); - const [view, setView] = useState("mydashboard"); - const [layout, setLayout] = useState([]); - const [widgetArray, setWidgetArray] = useState([]); const dialog = useDialog(); + const [darkMode, setDarkMode] = useState(false); + const [view, setView] = useState("mydashboard"); + + const emptyDash = () => ({ + layout: [], + widgetArray: [], + originalLayout: [], + originalWidgets: [], + isEditable: false, + isDelete: false, + }) + + // Add views as needed to initialize widget canvas views + const CANVAS_VIEWS = new Set([ "mydashboard", "gas" ]); + + // Widget dashboard state + const [dashboards, setDashboards] = useState(() => ({ + mydashboard: emptyDash(), + gas: emptyDash(), + })); + + const isCanvasView = CANVAS_VIEWS.has(view); + const dash = dashboards[view] ?? emptyDash(); + + const updateDash = (key, updater) => { + setDashboards(prev => ({ + ...prev, + [key]: updater(prev[key] ?? emptyDash()), + })); + }; + + // Widget setter functions + const setLayout = (fnOrValue) => { + if (!isCanvasView) return; + updateDash(view, s => ({ + ...s, + layout: typeof fnOrValue === "function" ? fnOrValue( s.layout ) : fnOrValue, + })); + }; + + const setWidgetArray = (fnOrValue) => { + if (!isCanvasView) return; + updateDash(view, s => ({ + ...s, + widgetArray: typeof fnOrValue === "function" ? fnOrValue(s.widgetArray) : fnOrValue, + })); + }; + + const setIsEditable = (value) => { + if (!isCanvasView) return; + updateDash(view, s => ({ ...s, isEditable: value })); + }; + + const setIsDelete = (value) => { + if (!isCanvasView) return; + updateDash(view, s => ({ ...s, isDelete: value })); + }; + const beginEditIfNeeded = () => { - if ( !isEditable ) { - setOriginalLayout(layout); - setOriginalWidgets(widgetArray); - } + if (!isCanvasView) return; + updateDash(view, s => { + if(s.isEditable) return s; // already editing + return { + ...s, + originalLayout: s.layout, + originalWidgets: s.widgetArray, + }; + }); }; const onCancel = () => { - console.log("canceling changes..."); - console.log(originalWidgets); - if (originalLayout.length) { - setLayout(originalLayout.map(item => ({ - ...item, - static: true - }))); - }; - setWidgetArray(originalWidgets); - setIsEditable(false); - setIsDelete(false); - setOriginalLayout([]); - setOriginalWidgets([]); - } - - // Prevent navigation while in Edit mode without saving or cancelling - const handleViewChange = (nextView) => { - console.log("1. handling view change") - if (view === "mydashboard" && isEditable) { - console.log("2. currently editing"); + if (!isCanvasView) return; + + updateDash(view, s => ({ + ...s, + layout: (s.originalLayout?.length ? s.originalLayout : s.layout).map(it => ({ + ...it, + static: true, + })), + widgetArray: s.originalWidgets ?? s.widgetArray, + isEditable: false, + isDelete: false, + originalLayout: [], + originalWidgets: [], + })); + }; + + const onLayoutChange = (newLayout) => { + setLayout(newLayout); + }; + + const handleViewChange = (nextView) => { + // prevent nav only when current view is a widget canvas AND you're editing + if (CANVAS_VIEWS.has(view) && dash.isEditable) { dialog.setOpen(true); return; - - // const confirmed = window.confirm( - // "You have unsaved changes. Discard them and continue?" - // ); - // if (!confirmed) return; - // onCancel(); } setView(nextView); }; - - const onLayoutChange = (newLayout) => setLayout(newLayout); // Prop Forwarding - const canvasProps = { view, layout, setLayout, setOriginalLayout, onLayoutChange, onChangeView: handleViewChange, - onCancel, isEditable, setIsEditable, isDelete, setIsDelete, widgetArray, setWidgetArray, originalWidgets, setOriginalWidgets, beginEditIfNeeded + const canvasProps = { + view, + onChangeView: handleViewChange, + + // active canvas props + layout: dash.layout, + widgetArray: dash.widgetArray, + isEditable: dash.isEditable, + isDelete: dash.isDelete, + + // handler functions + setLayout, + setWidgetArray, + setIsEditable, + setIsDelete, + beginEditIfNeeded, + onCancel, + onLayoutChange, }; const sidebarProps = { view, onChangeView: handleViewChange, darkMode, setDarkMode }; const headerProps = { onChangeView: handleViewChange }; diff --git a/client/src/components/Header/Header.jsx b/client/src/components/Header/Header.jsx index a89eed9..ff8cfb4 100644 --- a/client/src/components/Header/Header.jsx +++ b/client/src/components/Header/Header.jsx @@ -1,20 +1,35 @@ -import { chakra, useRecipe, Box, Button, Stack, Text, Flex } from "@chakra-ui/react" +import { chakra, useRecipe, Box, Button, Stack, Text, Flex, Image } from "@chakra-ui/react" +import { useColorMode } from "@/components/ui/color-mode"; import UserAvatarMenu from "./UserAvatarMenu.jsx"; //TODO: Dynamically populate per each user const user = { - name: "venessa kuchenik", - email: "vkuchenik@contractor.usgs.gov", + name: "Dan Hansen", + email: "dshansen@contractor.usgs.gov", // avatar: "src\\assets\\user_profile.svg" } export default function Header({ onChangeView }) { const recipe = useRecipe({ key: "header" }); const styles = recipe(); + const { colorMode } = useColorMode(); + + const logoSource = + colorMode === "dark" + ? "/images/NVIS_Logo_Black.png" + : "/images/NVIS_Logo_transparent.png" return ( - + {user.name} diff --git a/client/src/components/Sidebar/Sidebar.jsx b/client/src/components/Sidebar/Sidebar.jsx index 4829f91..0c1b39a 100644 --- a/client/src/components/Sidebar/Sidebar.jsx +++ b/client/src/components/Sidebar/Sidebar.jsx @@ -43,7 +43,7 @@ export default function Sidebar({ view, onChangeView, darkMode, setDarkMode }) { // Track currrent view from both Sidebar and Canvas when 'view' changes useEffect(() => { - console.log("Current view = ", view); + //console.log("Current view = ", view); const newIndex = buttons.findIndex((btn) => { if (btn.view === "mydashboard") { return DASHBOARD_VIEWS.includes(view); diff --git a/client/src/components/ui/color-mode.jsx b/client/src/components/ui/color-mode.jsx index b2d1cd6..01c5d97 100644 --- a/client/src/components/ui/color-mode.jsx +++ b/client/src/components/ui/color-mode.jsx @@ -2,6 +2,7 @@ import { ClientOnly, IconButton, Skeleton, Span } from '@chakra-ui/react' import { ThemeProvider, useTheme } from 'next-themes' +import { useEffect } from "react" import * as React from 'react' import { LuMoon, LuSun } from 'react-icons/lu' @@ -18,6 +19,12 @@ export function useColorMode() { const toggleColorMode = () => { setTheme(resolvedTheme === 'dark' ? 'light' : 'dark') } + + // Debug log + useEffect(() => { + //console.log(`Color mode changed to: ${colorMode}`); + }, [colorMode]); + return { colorMode: colorMode, setColorMode: setTheme, diff --git a/client/src/constants/viewKeys.js b/client/src/constants/viewKeys.js index b511ae5..600c4f8 100644 --- a/client/src/constants/viewKeys.js +++ b/client/src/constants/viewKeys.js @@ -1,12 +1,21 @@ // Main views called from Home tab -> Canvas navigation header +// 'isCanvas' is used to detirmine which views contain widgets and their state +// Add seismic, remote, and deformation once ready to add additional widgets export const DASHBOARD_TABS = [ - { value: "mydashboard", label: "My Dashboard" }, - { value: "gas", label: "Gas" }, - { value: "seismic", label: "Seismic" }, - { value: "remote", label: "Remote Sensing" }, - { value: "daily", label: "Daily Activity" }, + { value: "mydashboard", label: "My Dashboard", isCanvas: true }, + { value: "gas", label: "Gas", isCanvas: true }, + { value: "seismic", label: "Seismic", isCanvas: false }, + { value: "remote", label: "Remote Sensing", isCanvas: false }, + { value: "deformation", label: "Deformation", isCanvas: false }, + { value: "daily", label: "Daily Activity", isCanvas: false }, ] +// Used to check if widget state needs to be initialized per view +// Set has O(1) lookup, trying it becuase it seems fast +export const DASHBOARD_CANVAS_VIEWS = new Set( + DASHBOARD_TABS.filter(t => t.isCanvas).map(t => t.value) +); + // Volcano views called from Volcano tab -> Canvas navigation header export const VOLCANO_TABS = [ { value: "volcanohome", label: "Volcano Home" }, @@ -19,6 +28,7 @@ export const VOLCANO_TABS = [ // Admin views called from Admin tab -> Canvas navigation header export const ADMIN_TABS = [ { value: "medataentry", label: "M&E Data Entry" }, + { value: "nvews", label: "NVEWS" }, { value: "projectcollector", label: "Project Collector" }, { value: "nasapplication", label: "NAS Application" }, { value: "ivans", label: "IVANS" }, @@ -41,6 +51,7 @@ export const VIEW_LABELS = { global: "Global Map", regional: "Regional Map", volcano: "Volcano", + deformation: "Deformation", // Volcano navigation tabs volcanohome: "Volcano Home", diff --git a/client/src/constants/widgetInspectorRegistry.js b/client/src/constants/widgetInspectorRegistry.js new file mode 100644 index 0000000..752c067 --- /dev/null +++ b/client/src/constants/widgetInspectorRegistry.js @@ -0,0 +1,97 @@ +export const WIDGET_INSPECTOR_REGISTRY = { + "gas_measurements": { + title: "Gas Measurements Settings", + defaultConfig: { + volcano_name: null, + site_name: null, + compound_name: [], + start_date: null, + end_date: null, + include_measurements: true, + include_personnel: false, // optional + }, + + // Option sources for this widget's fields (from /options endpoint) + optionSources: { + options: { + url: "/options", // relative to API_BASE + cacheKey: "options", // used by generic loader + }, + sites: { + url: ({ draft }) => { + const v = draft?.volcano_name; + return v + ? `/options/sites?volcano_name=${encodeURIComponent(v)}` + : "/options/sites"; + }, + cacheKey: ({ draft }) => `sites:${draft?.volcano_name ?? "all"}`, + dependsOn: ["volcano_name"] + }, + }, + + // How to render + bind each config key + fields: [ + { + key: "volcano_name", + label: "Volcano", + type: "select", + placeholder: "(all)", + optionsFrom: { source: "options", path: "volcano_name" }, + clearOnChange: ["site_name"], // when volcano changes, clear site + }, + { + key: "site_name", + label: "Site", + type: "select", + placeholder: "(all)", + optionsFrom: { source: "sites", path: "site_name" }, + }, + // Single Select Option + // { + // key: "compound", + // label: "Compound", + // type: "select", + // placeholder: "(all)", + // optionsFrom: { source: "options", path: "compound_name" }, + // }, + { + // Multiple Select Option + key: "compound_name", + label: "Compounds", + type: "checkbox", + placeholder: "(all)", + optionsFrom: { source: "options", path: "compound_name" }, + }, + { + key: "date_interval", + label: "Date Interval", + type: "dateRange", + startKey: "start_date", + endKey: "end_date", + minFrom: { source: "options", path: "date_min" }, + maxFrom: { source: "options", path: "date_max" }, + presets: [ + { label: "Use full range", startFrom: "date_min", endFrom: "date_max" }, + { label: "Clear", start: null, end: null, variant: "ghost" }, + ], + validate: (draft) => { + const s = draft.start_date; + const e = draft.end_date; + if (s && e && s > e) return "Start date must be on or before end date."; + return null; + }, + }, + ], + + // Optional: control exactly what gets submitted (prevents "junk" keys) + submitKeys: [ + "volcano_name", + "site_name", + "compound_name", + "start_date", + "end_date", + "include_measurements", + "include_personnel", + ], + }, +}; diff --git a/client/src/constants/widgetRegistry.js b/client/src/constants/widgetRegistry.js new file mode 100644 index 0000000..106ef30 --- /dev/null +++ b/client/src/constants/widgetRegistry.js @@ -0,0 +1,106 @@ +// This file is intended to track default widget states as a baseline for any created widgets +import AlertLevel from "src/components/Canvas/CanvasComponents/AlertLevelWidget.jsx" +import GasWidget from "src/components/Canvas/CanvasComponents/GasWidget"; + +// Example widgets, returns actual baseline widget +export const WIDGET_REGISTRY = { + // ***************** My Dashboard Widgets ***************** + alert_level: { + key: "alert_level", + label: "Alert Level", + kind: "real", + defaults: { w: 4, h: 7, minW: 4, minH: 7, maxW: 8, maxH: 14 }, + category: "generic", + }, + + ivans: { + key: "ivans", + label: "IVANS", + kind: "placeholder", + placeholderTitle: "IVANS", + defaults: {w: 4, h: 7, minW: 4, minH: 5, maxW: 8, maxH: 12 }, + category: "generic", + }, + + plume_heights: { + key: "plume_heights", + label: "Plume Heights", + kind: "placeholder", + placeholderTitle: "Plume Heights", + defaults: {w: 4, h: 7, minW: 4, minH: 5, maxW: 8, maxH: 12 }, + category: "generic", + }, + + alert_level_changes: { + key: "alert_level_changes", + label: "Alert Level Changes", + kind: "placeholder", + placeholderTitle: "Alert Level Changes", + defaults: {w: 4, h: 7, minW: 4, minH: 5, maxW: 8, maxH: 12 }, + category: "generic", + }, + + // ***************** Gas Widgets ***************** + gas_measurements: { + key: "gas_measurements", + label: "Gas Measurements", + kind: "real", + defaults: {w: 4, h: 6, minW: 4, minH: 5, maxW: 8, maxH: 12 }, + category: "gas", + // ~ defaultConfig is being moved to widgetInspectorRegistry + // defaultConfig: { + // // for later filters + // rangeDays: 30, + // compound: null, + // volcano: null, + // site: null, + // }, + }, + + gas_so2: { + key: "gas_so2", + label: "SO₂", + kind: "placeholder", + placeholderTitle: "SO2", + defaults: {w: 4, h: 7, minW: 4, minH: 5, maxW: 8, maxH: 12 }, + category: "gas", + }, + + // Add Additional Widgets to the Registry as they are generated + // Gas Widgets + // Seismic Widgets + // Remote Sensing Widgets + // Deformation Widgets +}; + +// Used to define components without adding .jsx to a .js registry file +export const WIDGET_COMPONENTS = { + alert_level: AlertLevel, + gas_measurements: GasWidget, // clicking CO2 renders the temp gas widget +}; + +// Which widgets show up in "Add Widget" for each view +// Make sure these match the keys inside the WIDGET_REGISTRY +export const VIEW_WIDGET_KEYS = { + mydashboard: [ + "alert_level", + "gas_measurements", + "ivans", + "plume_heights", + "alert_level_changes" + ], + + gas: [ + "gas_measurements", + "gas_so2", + ], + + seismic: ["alert_level"], +}; + +export const getWidgetsForView = (view) => { + const keys = VIEW_WIDGET_KEYS[view] ?? []; + return keys + .map((k) => WIDGET_REGISTRY[k]) + .filter(Boolean); +}; \ No newline at end of file diff --git a/client/src/theme.js b/client/src/theme.js index 33e4ff4..88eeba1 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -72,7 +72,7 @@ const widgetRecipe = defineSlotRecipe({ borderColor: "{base300}", textAlign: "center", height: "100%", - position: "fixed" + //position: "fixed" }, header: { flexShrink: "0", @@ -138,18 +138,21 @@ const config = defineConfig({ filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", } }, - '.leaflet-control-zoom-in .leaflet-control-zoom-out .leaflet-control-attribution .leaflet-container': { + '.leaflet-control-zoom-in, .leaflet-control-zoom-out, .leaflet-control-attribution, .leaflet-container': { _dark: { filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", } }, '.leaflet-control-attribution': { + maxWidth: "100%", _dark: { filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", } }, '.leaflet-container': { fontFamily: "georgia, system-ui, sans-serif !important", + width: "100%", + height: "100%", _dark: { filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)", } diff --git a/client/src/utils/map-utils/functions/mapHelpers.js b/client/src/utils/map-utils/functions/mapHelpers.js new file mode 100644 index 0000000..b6579d3 --- /dev/null +++ b/client/src/utils/map-utils/functions/mapHelpers.js @@ -0,0 +1,57 @@ +import {useMap, useMapEvents} from "react-leaflet"; +import {useEffect} from "react"; + +/** + * Helper component for console logging where on a map the user clicked for debugging purposes. + * + * This component renders nothing and is intended to be mounted inside a + */ +export function MapClickLogger() { + + const map = useMap(); + useMapEvents({ + click(e) { + console.log("lat:", e.latlng.lat, "lng:", e.latlng.lng); + }, + }); + + return null; +} + +/** + * Helper component that detects when a zoom change as completed and sets the callback to the updated zoom level + * + * This component renders nothing and is intended to be mounted inside a + * + * @param onZoom Callback set to the current zoom level on zoomend + */ +export function ZoomWatcher({onZoom}){ + const map = useMap(); + useMapEvents({ + zoomend(e){ + onZoom(map.getZoom()); + + } + }) + return null; +} + +/** + * Helper component that take triggers a flyTo animation when a position is present + * + * This component returns nothing and is intended to be mounted inside a + * + * @param position Array of two integers representing the desired coordinates to flyTo + * @param zoom Integer representing the desired zoom level to display + + */ +export function FlyTo({ position, zoom }) { + const map = useMap(); + + useEffect(() => { + if (!position) return; + map.flyTo(position, zoom, { animate: true }); + }, [position, zoom, map]); + + return null; +} \ No newline at end of file diff --git a/client/src/utils/map-utils/functions/markerHelpers.js b/client/src/utils/map-utils/functions/markerHelpers.js new file mode 100644 index 0000000..815bbfa --- /dev/null +++ b/client/src/utils/map-utils/functions/markerHelpers.js @@ -0,0 +1,63 @@ +import L from "leaflet"; + + +/** + * Takes an image URL and optional size, anchor, popup params and returns a Leaflet Icon. + * + * @param path Image URL + * @param size Array of 2 integers representing the size of the icon + * @param anchor Array of 2 integers representing the point of the icon which corresponds to the icon location + * @param popup Array of 2 integers representing the position of the popup relative to the icon acnhor + * @returns A Leaflet Icon + */ +export function createIcon(path, + {size=[24,24], anchor=[12,24], popup=[0,-20]} = {}){ + return L.icon({ + iconUrl: path, + iconSize: size, + iconAnchor: anchor, + popupAnchor: popup + + }); + +} + + +/** + * Updates a state setter with the latitude and longitude of a marker. + * + * @param marker Marker Object with lat and lon fields + * @param setter React Hook to be set with Marker position values + */ +export function handleMarkerClick(marker,setter){ + setter([marker.latitude,marker.longitude]); +} + +/** + * Returns an array of positions representing points from which to draw Leaflet polygons or polylines. + * + * @param marker Object with polygon field + * @returns Array of positions that can be passed to Leaflet polygons/polylines + */ +export function computePolyPositions(marker) { + const positions = []; + for (const pos of marker.polygon) { + positions.push([pos[1], pos[0]]); + } + //console.log(positions); + return positions; +} + + +/** + * Returns a latitude integer shifted by 360 degrees. + * + * @param lon Integer representing a latitude value + * @returns An integer representing the latitude value shifted by 360 degrees + */ +export function shiftPos(lon){ + if( lon > 0){ + return lon - 360; + } + return lon; +} \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/black-triangle.svg b/client/src/utils/map-utils/icons/black-triangle.svg new file mode 100644 index 0000000..7dec057 --- /dev/null +++ b/client/src/utils/map-utils/icons/black-triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/src/utils/map-utils/icons/camera.svg b/client/src/utils/map-utils/icons/camera.svg new file mode 100644 index 0000000..3f17e34 --- /dev/null +++ b/client/src/utils/map-utils/icons/camera.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/cloud.svg b/client/src/utils/map-utils/icons/cloud.svg new file mode 100644 index 0000000..617046d --- /dev/null +++ b/client/src/utils/map-utils/icons/cloud.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/gps-solid.svg b/client/src/utils/map-utils/icons/gps-solid.svg new file mode 100644 index 0000000..d07caac --- /dev/null +++ b/client/src/utils/map-utils/icons/gps-solid.svg @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/green-triangle.svg b/client/src/utils/map-utils/icons/green-triangle.svg new file mode 100644 index 0000000..3c0726b --- /dev/null +++ b/client/src/utils/map-utils/icons/green-triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/src/utils/map-utils/icons/line-segment.svg b/client/src/utils/map-utils/icons/line-segment.svg new file mode 100644 index 0000000..6560c33 --- /dev/null +++ b/client/src/utils/map-utils/icons/line-segment.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/red-triangle.svg b/client/src/utils/map-utils/icons/red-triangle.svg new file mode 100644 index 0000000..8da16aa --- /dev/null +++ b/client/src/utils/map-utils/icons/red-triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/src/utils/map-utils/icons/sound-black.svg b/client/src/utils/map-utils/icons/sound-black.svg new file mode 100644 index 0000000..fce266b --- /dev/null +++ b/client/src/utils/map-utils/icons/sound-black.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/triangle-black.svg b/client/src/utils/map-utils/icons/triangle-black.svg new file mode 100644 index 0000000..900d83a --- /dev/null +++ b/client/src/utils/map-utils/icons/triangle-black.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/volcano-smoke.svg b/client/src/utils/map-utils/icons/volcano-smoke.svg new file mode 100644 index 0000000..d0c5118 --- /dev/null +++ b/client/src/utils/map-utils/icons/volcano-smoke.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/volcano-solid.svg b/client/src/utils/map-utils/icons/volcano-solid.svg new file mode 100644 index 0000000..da34fcc --- /dev/null +++ b/client/src/utils/map-utils/icons/volcano-solid.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/client/src/utils/map-utils/icons/yellow-triangle.svg b/client/src/utils/map-utils/icons/yellow-triangle.svg new file mode 100644 index 0000000..48603ed --- /dev/null +++ b/client/src/utils/map-utils/icons/yellow-triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/src/utils/map-utils/map-configs/nvewsMapFilterConfig.js b/client/src/utils/map-utils/map-configs/nvewsMapFilterConfig.js new file mode 100644 index 0000000..585f8f1 --- /dev/null +++ b/client/src/utils/map-utils/map-configs/nvewsMapFilterConfig.js @@ -0,0 +1,44 @@ + + +export const mapFilterConfig = [ + { + id: "observatory", + label: "Observatory", + type: "checkbox", + options: [ + { label: "CVO", value: "CVO" }, + { label: "HVO", value: "HVO" }, + { label: "AVO", value: "AVO" }, + { label: "YVO", value: "YVO" }, + ], + target: { kind: "data", field: "volcano_group", op: "in" }, + defaultValue: [], + }, + { + id:"stations", + label: "Stations", + type: "checkbox", + options: [ + { label: "Seismic", value: "seismic_stations"}, + { label: "GNSS", value: "gnss_stations"}, + { label: "Cameras", value: "camera_stations"}, + { label: "Infrasound", value: "infrasound_stations"}, + { label: "Tilt", value: "tiltmeter_stations"}, + { label: "Real-Time Gas", value: "gas_stations"}, + ], + target: { kind: "stationType"}, + defaultValue: [], + + }, + // { + // id: "overlay", + // label: "Overlay", + // type: "checkbox", + // options: [ + // { label: "Overlay 1", value: "overlay1" }, + // { label: "Overlay 2", value: "overlay2" }, + // ], + // target: { kind: "mapOverlay" }, + // defaultValue: [], + // }, +]; \ No newline at end of file diff --git a/client/src/utils/pctStyles.js b/client/src/utils/pctStyles.js new file mode 100644 index 0000000..0a8bbe5 --- /dev/null +++ b/client/src/utils/pctStyles.js @@ -0,0 +1,7 @@ +export function pctStyles(val, high, mid) { + if (val == null || Number.isNaN(val)) return {}; + if (val < mid) return { bg: "red.100", color: "red.800", fontWeight: "semibold" }; + if (val < high) return { bg: "yellow.100", color: "yellow.800" }; + if (val > high) return { bg: "blue.100", color: "blue.800" }; + return { bg: "green.100", color: "green.800" }; +} \ No newline at end of file