diff --git a/.github/workflows/pr-build-check.yml b/.github/workflows/pr-build-check.yml new file mode 100644 index 0000000..358ff2a --- /dev/null +++ b/.github/workflows/pr-build-check.yml @@ -0,0 +1,36 @@ +name: PR Build Check + +# Run this workflow on pull requests to validate the build +on: + pull_request: + branches: [cwbi-dev] + types: [opened, synchronize, reopened] + +jobs: + build-check: + name: Build React App + runs-on: ubuntu-latest + + steps: + # Step 1: Clone the repository + - name: Git clone the repository + uses: actions/checkout@v3 + + # Step 2: Set up Node.js (React app requires Node.js) + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 'node' # Use the latest version of Node.js + + # Step 3: Install dependencies + - name: Install Dependencies + run: npm install + + # Step 4: Build the React app for production + - name: Build React App + run: npm run build + env: + REACT_APP_AUTH_HOST: https://identityc-test.cwbi.us/auth + REACT_APP_AUTH_REDIRECT_URL: https://cumulus.dev.cwbi.us + REACT_APP_CUMULUS_API_URL: https://cumulus.dev.cwbi.us/api + REACT_APP_ISDEVELOPMENT: true \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 55cac9f..97043f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,34 +1,42 @@ { "name": "cumulus-ui", - "version": "1.4.4", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cumulus-ui", - "version": "1.4.4", + "version": "1.5.0", "dependencies": { + "@date-fns/utc": "^2.1.0", "@headlessui/react": "^1.4.3", "@heroicons/react": "^2.0.14", - "@observablehq/plot": "^0.4.1", + "@observablehq/plot": "^0.6.15", "@tailwindcss/forms": "^0.5.3", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", + "@turf/turf": "^7.2.0", "@usace/create-jwt-api-bundle": "^0.3.1", - "@usace/create-keycloak-auth-bundle": "^0.2.1", + "@usace/create-keycloak-auth-bundle": "^2.0.1", "@usace/create-rest-bundle": "^0.1.2", - "d3": "^7.3.0", - "date-fns": "^2.28.0", + "d3": "^7.9.0", + "date-fns": "^4.0.0", "internal-nav-helper": "^3.1.0", + "leaflet": "^1.9.4", + "leaflet-easybutton": "^2.4.0", "money-clip": "^3.0.5", "react": "^18.2.0", "react-colorful": "^5.5.1", "react-datepicker": "^4.7.0", "react-dom": "^18.2.0", + "react-leaflet": "^4.0.0", "react-select": "^5.2.2", "redux-bundler": "^28.0.3", "redux-bundler-react": "^1.2.0", + "shpjs": "^6.1.0", + "terra-draw": "^1.0.0", + "terra-draw-leaflet-adapter": "^1.0.0", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -1977,16 +1985,23 @@ } }, "node_modules/@babel/runtime": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "license": "MIT", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/@babel/template": { "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", @@ -2326,6 +2341,12 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@date-fns/utc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.0.tgz", + "integrity": "sha512-176grgAgU2U303rD2/vcOmNg0kGPbhzckuH1TEP2al7n0AQipZIy9P15usd2TKQCG1g+E1jX/ZVQSzs4sUDwgA==", + "license": "MIT" + }, "node_modules/@emotion/babel-plugin": { "version": "11.10.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", @@ -3521,12 +3542,14 @@ } }, "node_modules/@observablehq/plot": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@observablehq/plot/-/plot-0.4.3.tgz", - "integrity": "sha512-uGR8Jy2sFhWc6vIeSQkW2JuePJXs9b8k1tQdN4RZN9USifATQxjFt5E5f0TIyH4J9ZdyM8/0wHHUQTUPZvT3yA==", + "version": "0.6.17", + "resolved": "https://registry.npmjs.org/@observablehq/plot/-/plot-0.6.17.tgz", + "integrity": "sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==", + "license": "ISC", "dependencies": { - "d3": "^7.3.0", - "isoformat": "0.2" + "d3": "^7.9.0", + "interval-tree-1d": "^1.0.0", + "isoformat": "^0.2.0" }, "engines": { "node": ">=12" @@ -3591,6 +3614,17 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-leaflet/core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", + "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3928,117 +3962,2159 @@ "node": ">=10" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", + "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, + "node_modules/@testing-library/dom": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", + "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.4.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", + "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@turf/along": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-7.2.0.tgz", + "integrity": "sha512-Cf+d2LozABdb0TJoIcJwFKB+qisJY4nMUW9z6PAuZ9UCH7AR//hy2Z06vwYCKFZKP4a7DRPkOMBadQABCyoYuw==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "^7.2.0", + "@turf/destination": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/angle": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-7.2.0.tgz", + "integrity": "sha512-b28rs1NO8Dt/MXadFhnpqH7GnEWRsl+xF5JeFtg9+eM/+l/zGrdliPYMZtAj12xn33w22J1X4TRprAI0rruvVQ==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/rhumb-bearing": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/area": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.2.0.tgz", + "integrity": "sha512-zuTTdQ4eoTI9nSSjerIy4QwgvxqwJVciQJ8tOPuMHbXJ9N/dNjI7bU8tasjhxas/Cx3NE9NxVHtNpYHL0FSzoA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.2.0.tgz", + "integrity": "sha512-wzHEjCXlYZiDludDbXkpBSmv8Zu6tPGLmJ1sXQ6qDwpLE1Ew3mcWqt8AaxfTP5QwDNQa3sf2vvgTEzNbPQkCiA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-clip": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-7.2.0.tgz", + "integrity": "sha512-q6RXTpqeUQAYLAieUL1n3J6ukRGsNVDOqcYtfzaJbPW+0VsAf+1cI16sN700t0sekbeU1DH/RRVAHhpf8+36wA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-polygon": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-7.2.0.tgz", + "integrity": "sha512-Aj4G1GAAy26fmOqMjUk0Z+Lcax5VQ9g1xYDbHLQWXvfTsaueBT+RzdH6XPnZ/seEEnZkio2IxE8V5af/osupgA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bearing": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-7.2.0.tgz", + "integrity": "sha512-Jm0Xt3GgHjRrWvBtAGvgfnADLm+4exud2pRlmCYx8zfiKuNXQFkrcTZcOiJOgTfG20Agq28iSh15uta47jSIbg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bezier-spline": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-7.2.0.tgz", + "integrity": "sha512-7BPkc3ufYB9KLvcaTpTsnpXzh9DZoENxCS0Ms9XUwuRXw45TpevwUpOsa3atO76iKQ5puHntqFO4zs8IUxBaaA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-clockwise": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-7.2.0.tgz", + "integrity": "sha512-0fJeFSARxy6ealGBM4Gmgpa1o8msQF87p2Dx5V6uSqzT8VPDegX1NSWl4b7QgXczYa9qv7IAABttdWP0K7Q7eQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-concave": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-concave/-/boolean-concave-7.2.0.tgz", + "integrity": "sha512-v3dTN04dfO6VqctQj1a+pjDHb6+/Ev90oAR2QjJuAntY4ubhhr7vKeJdk/w+tWNSMKULnYwfe65Du3EOu3/TeA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-contains": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-7.2.0.tgz", + "integrity": "sha512-dgRQm4uVO5XuLee4PLVH7CFQZKdefUBMIXTPITm2oRIDmPLJKHDOFKQTNkGJ73mDKKBR2lmt6eVH3br6OYrEYg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/boolean-point-on-line": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-crosses": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-7.2.0.tgz", + "integrity": "sha512-9GyM4UUWFKQOoNhHVSfJBf5XbPy8Fxfz9djjJNAnm/IOl8NmFUSwFPAjKlpiMcr6yuaAoc9R/1KokS9/eLqPvA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/line-intersect": "^7.2.0", + "@turf/polygon-to-line": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-disjoint": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-7.2.0.tgz", + "integrity": "sha512-xdz+pYKkLMuqkNeJ6EF/3OdAiJdiHhcHCV0ykX33NIuALKIEpKik0+NdxxNsZsivOW6keKwr61SI+gcVtHYcnQ==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/line-intersect": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/polygon-to-line": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-equal": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-7.2.0.tgz", + "integrity": "sha512-TmjKYLsxXqEmdDtFq3QgX4aSogiISp3/doeEtDOs3NNSR8susOtBEZkmvwO6DLW+g/rgoQJIBR6iVoWiRqkBxw==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "geojson-equality-ts": "^1.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-intersects": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-7.2.0.tgz", + "integrity": "sha512-GLRyLQgK3F14drkK5Qi9Mv7Z9VT1bgQUd9a3DB3DACTZWDSwfh8YZUFn/HBwRkK8dDdgNEXaavggQHcPi1k9ow==", + "license": "MIT", + "dependencies": { + "@turf/boolean-disjoint": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-overlap": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-7.2.0.tgz", + "integrity": "sha512-ieM5qIE4anO+gUHIOvEN7CjyowF+kQ6v20/oNYJCp63TVS6eGMkwgd+I4uMzBXfVW66nVHIXjODdUelU+Xyctw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/line-intersect": "^7.2.0", + "@turf/line-overlap": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "geojson-equality-ts": "^1.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-parallel": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-7.2.0.tgz", + "integrity": "sha512-iOtuzzff8nmwv05ROkSvyeGLMrfdGkIi+3hyQ+DH4IVyV37vQbqR5oOJ0Nt3Qq1Tjrq9fvF8G3OMdAv3W2kY9w==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/line-segment": "^7.2.0", + "@turf/rhumb-bearing": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.2.0.tgz", + "integrity": "sha512-lvEOjxeXIp+wPXgl9kJA97dqzMfNexjqHou+XHVcfxQgolctoJiRYmcVCWGpiZ9CBf/CJha1KmD1qQoRIsjLaA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "point-in-polygon-hao": "^1.1.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-on-line": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-7.2.0.tgz", + "integrity": "sha512-H/bXX8+2VYeSyH8JWrOsu8OGmeA9KVZfM7M6U5/fSqGsRHXo9MyYJ94k39A9kcKSwI0aWiMXVD2UFmiWy8423Q==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-touches": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-touches/-/boolean-touches-7.2.0.tgz", + "integrity": "sha512-8qb1CO+cwFATGRGFgTRjzL9aibfsbI91pdiRl7KIEkVdeN/H9k8FDrUA1neY7Yq48IaciuwqjbbojQ16FD9b0w==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/boolean-point-on-line": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-valid": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-valid/-/boolean-valid-7.2.0.tgz", + "integrity": "sha512-xb7gdHN8VV6ivPJh6rPpgxmAEGReiRxqY+QZoEZVGpW2dXcmU1BdY6FA6G/cwvggXAXxJBREoANtEDgp/0ySbA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/boolean-crosses": "^7.2.0", + "@turf/boolean-disjoint": "^7.2.0", + "@turf/boolean-overlap": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/boolean-point-on-line": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/line-intersect": "^7.2.0", + "@types/geojson": "^7946.0.10", + "geojson-polygon-self-intersections": "^1.2.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-within": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-7.2.0.tgz", + "integrity": "sha512-zB3AiF59zQZ27Dp1iyhp9mVAKOFHat8RDH45TZhLY8EaqdEPdmLGvwMFCKfLryQcUDQvmzP8xWbtUR82QM5C4g==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/boolean-point-on-line": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/buffer": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-7.2.0.tgz", + "integrity": "sha512-QH1FTr5Mk4z1kpQNztMD8XBOZfpOXPOtlsxaSAj2kDIf5+LquA6HtJjZrjUngnGtzG5+XwcfyRL4ImvLnFjm5Q==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/center": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/jsts": "^2.7.1", + "@turf/meta": "^7.2.0", + "@turf/projection": "^7.2.0", + "@types/geojson": "^7946.0.10", + "d3-geo": "1.7.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/buffer/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/@turf/buffer/node_modules/d3-geo": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz", + "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/@turf/center": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-7.2.0.tgz", + "integrity": "sha512-UTNp9abQ2kuyRg5gCIGDNwwEQeF3NbpYsd1Q0KW9lwWuzbLVNn0sOwbxjpNF4J2HtMOs5YVOcqNvYyuoa2XrXw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-mean": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-7.2.0.tgz", + "integrity": "sha512-NaW6IowAooTJ35O198Jw3U4diZ6UZCCeJY+4E+WMLpks3FCxMDSHEfO2QjyOXQMGWZnVxVelqI5x9DdniDbQ+A==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-median": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-7.2.0.tgz", + "integrity": "sha512-/CgVyHNG4zAoZpvkl7qBCe4w7giWNVtLyTU5PoIfg1vWM4VpYw+N7kcBBH46bbzvVBn0vhmZr586r543EwdC/A==", + "license": "MIT", + "dependencies": { + "@turf/center-mean": "^7.2.0", + "@turf/centroid": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-of-mass": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-7.2.0.tgz", + "integrity": "sha512-ij3pmG61WQPHGTQvOziPOdIgwTMegkYTwIc71Gl7xn4C0vWH6KLDSshCphds9xdWSXt2GbHpUs3tr4XGntHkEQ==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "^7.2.0", + "@turf/convex": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.2.0.tgz", + "integrity": "sha512-yJqDSw25T7P48au5KjvYqbDVZ7qVnipziVfZ9aSo7P2/jTE7d4BP21w0/XLi3T/9bry/t9PR1GDDDQljN4KfDw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/circle": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-7.2.0.tgz", + "integrity": "sha512-1AbqBYtXhstrHmnW6jhLwsv7TtmT0mW58Hvl1uZXEDM1NCVXIR50yDipIeQPjrCuJ/Zdg/91gU8+4GuDCAxBGA==", + "license": "MIT", + "dependencies": { + "@turf/destination": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clean-coords": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-7.2.0.tgz", + "integrity": "sha512-+5+J1+D7wW7O/RDXn46IfCHuX1gIV1pIAQNSA7lcDbr3HQITZj334C4mOGZLEcGbsiXtlHWZiBtm785Vg8i+QQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clone": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.2.0.tgz", + "integrity": "sha512-JlGUT+/5qoU5jqZmf6NMFIoLDY3O7jKd53Up+zbpJ2vzUp6QdwdNzwrsCeONhynWM13F0MVtPXH4AtdkrgFk4g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-7.2.0.tgz", + "integrity": "sha512-sKOrIKHHtXAuTKNm2USnEct+6/MrgyzMW42deZ2YG2RRKWGaaxHMFU2Yw71Yk4DqStOqTIBQpIOdrRuSOwbuQw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-dbscan": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-7.2.0.tgz", + "integrity": "sha512-VWVUuDreev56g3/BMlnq/81yzczqaz+NVTypN5CigGgP67e+u/CnijphiuhKjtjDd/MzGjXgEWBJc26Y6LYKAw==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-kmeans": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-7.2.0.tgz", + "integrity": "sha512-BxQdK8jc8Mwm9yoClCYkktm4W004uiQGqb/i/6Y7a8xqgJITWDgTu/cy//wOxAWPk4xfe6MThjnqkszWW8JdyQ==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "skmeans": "0.9.7", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/collect": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-7.2.0.tgz", + "integrity": "sha512-zRVGDlYS8Bx/Zz4vnEUyRg4dmqHhkDbW/nIUIJh657YqaMj1SFi4Iv2i9NbcurlUBDJFkpuOhCvvEvAdskJ8UA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/combine": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-7.2.0.tgz", + "integrity": "sha512-VEjm3IvnbMt3IgeRIhCDhhQDbLqCU1/5uN1+j1u6fyA095pCizPThGp4f/COSzC3t1s/iiV+fHuDsB6DihHffQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/concave": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-7.2.0.tgz", + "integrity": "sha512-cpaDDlumK762kdadexw5ZAB6g/h2pJdihZ+e65lbQVe3WukJHAANnIEeKsdFCuIyNKrwTz2gWu5ws+OpjP48Yw==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/tin": "^7.2.0", + "@types/geojson": "^7946.0.10", + "topojson-client": "3.x", + "topojson-server": "3.x", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/convex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-7.2.0.tgz", + "integrity": "sha512-HsgHm+zHRE8yPCE/jBUtWFyaaBmpXcSlyHd5/xsMhSZRImFzRzBibaONWQo7xbKZMISC3Nc6BtUjDi/jEVbqyA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "concaveman": "^1.2.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/destination": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-7.2.0.tgz", + "integrity": "sha512-8DUxtOO0Fvrh1xclIUj3d9C5WS20D21F5E+j+X9Q+ju6fcM4huOqTg5ckV1DN2Pg8caABEc5HEZJnGch/5YnYQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/difference": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-7.2.0.tgz", + "integrity": "sha512-NHKD1v3s8RX+9lOpvHJg6xRuJOKiY3qxHhz5/FmE0VgGqnCkE7OObqWZ5SsXG+Ckh0aafs5qKhmDdDV/gGi6JA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/dissolve": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-7.2.0.tgz", + "integrity": "sha512-gPG5TE3mAYuZqBut8tPYCKwi4hhx5Cq0ALoQMB9X0hrVtFIKrihrsj98XQM/5pL/UIpAxQfwisQvy6XaOFaoPA==", + "license": "MIT", + "dependencies": { + "@turf/flatten": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-7.2.0.tgz", + "integrity": "sha512-HBjjXIgEcD/wJYjv7/6OZj5yoky2oUvTtVeIAqO3lL80XRvoYmVg6vkOIu6NswkerwLDDNT9kl7+BFLJoHbh6Q==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance-weight": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-7.2.0.tgz", + "integrity": "sha512-NeoyV0fXDH+7nIoNtLjAoH9XL0AS1pmTIyDxEE6LryoDTsqjnuR0YQxIkLCCWDqECoqaOmmBqpeWONjX5BwWCg==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/ellipse": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-7.2.0.tgz", + "integrity": "sha512-/Y75S5hE2+xjnTw4dXpQ5r/Y2HPM4xrwkPRCCQRpuuboKdEvm42azYmh7isPnMnBTVcmGb9UmGKj0HHAbiwt1g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/rhumb-destination": "^7.2.0", + "@turf/transform-rotate": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/envelope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-7.2.0.tgz", + "integrity": "sha512-xOMtDeNKHwUuDfzQeoSNmdabsP0/IgVDeyzitDe/8j9wTeW+MrKzVbGz7627PT3h6gsO+2nUv5asfKtUbmTyHA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/bbox-polygon": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/explode": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-7.2.0.tgz", + "integrity": "sha512-jyMXg93J1OI7/65SsLE1k9dfQD3JbcPNMi4/O3QR2Qb3BAs2039oFaSjtW+YqhMqVC4V3ZeKebMcJ8h9sK1n+A==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flatten": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-7.2.0.tgz", + "integrity": "sha512-q38Qsqr4l7mxp780zSdn0gp/WLBX+sa+gV6qIbDQ1HKCrrPK8QQJmNx7gk1xxEXVot6tq/WyAPysCQdX+kLmMA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flip": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-7.2.0.tgz", + "integrity": "sha512-X0TQ0U/UYh4tyXdLO5itP1sO2HOvfrZC0fYSWmTfLDM14jEPkEK8PblofznfBygL+pIFtOS2is8FuVcp5XxYpQ==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/geojson-rbush": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/geojson-rbush/-/geojson-rbush-7.2.0.tgz", + "integrity": "sha512-ST8fLv+EwxVkDgsmhHggM0sPk2SfOHTZJkdgMXVFT7gB9o4lF8qk4y4lwvCCGIfFQAp2yv/PN5EaGMEKutk6xw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/great-circle": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-7.2.0.tgz", + "integrity": "sha512-n30OiADyOKHhor0aXNgYfXQYXO3UtsOKmhQsY1D89/Oh1nCIXG/1ZPlLL9ZoaRXXBTUBjh99a+K8029NQbGDhw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", + "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/hex-grid": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-7.2.0.tgz", + "integrity": "sha512-Yo2yUGxrTCQfmcVsSjDt0G3Veg8YD26WRd7etVPD9eirNNgXrIyZkbYA7zVV/qLeRWVmYIKRXg1USWl7ORQOGA==", + "license": "MIT", + "dependencies": { + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/intersect": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/interpolate": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-7.2.0.tgz", + "integrity": "sha512-Ifgjm1SEo6XujuSAU6lpRMvoJ1SYTreil1Rf5WsaXj16BQJCedht/4FtWCTNhSWTwEz2motQ1WNrjTCuPG94xA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/centroid": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/hex-grid": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/point-grid": "^7.2.0", + "@turf/square-grid": "^7.2.0", + "@turf/triangle-grid": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/intersect": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-7.2.0.tgz", + "integrity": "sha512-81GMzKS9pKqLPa61qSlFxLFeAC8XbwyCQ9Qv4z6o5skWk1qmMUbEHeMqaGUTEzk+q2XyhZ0sju1FV4iLevQ/aw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.2.0.tgz", + "integrity": "sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isobands": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-7.2.0.tgz", + "integrity": "sha512-lYoHeRieFzpBp29Jh19QcDIb0E+dzo/K5uwZuNga4wxr6heNU0AfkD4ByAHYIXHtvmp4m/JpSKq/2N6h/zvBkg==", + "license": "MIT", + "dependencies": { + "@turf/area": "^7.2.0", + "@turf/bbox": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/explode": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "marchingsquares": "^1.3.3", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isolines": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-7.2.0.tgz", + "integrity": "sha512-4ZXKxvA/JKkxAXixXhN3UVza5FABsdYgOWXyYm3L5ryTPJVOYTVSSd9A+CAVlv9dZc3YdlsqMqLTXNOOre/kwg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "marchingsquares": "^1.3.3", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/jsts": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@turf/jsts/-/jsts-2.7.2.tgz", + "integrity": "sha512-zAezGlwWHPyU0zxwcX2wQY3RkRpwuoBmhhNE9HY9kWhFDkCxZ3aWK5URKwa/SWKJbj9aztO+8vtdiBA28KVJFg==", + "license": "(EDL-1.0 OR EPL-1.0)", + "dependencies": { + "jsts": "2.7.1" + } + }, + "node_modules/@turf/kinks": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-7.2.0.tgz", + "integrity": "sha512-BtxDxGewJR0Q5WR9HKBSxZhirFX+GEH1rD7/EvgDsHS8e1Y5/vNQQUmXdURjdPa4StzaUBsWRU5T3A356gLbPA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/length": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-7.2.0.tgz", + "integrity": "sha512-LBmYN+iCgVtWNLsckVnpQIJENqIIPO63mogazMp23lrDGfWXu07zZQ9ZinJVO5xYurXNhc/QI2xxoqt2Xw90Ig==", + "license": "MIT", + "dependencies": { + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-arc": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-7.2.0.tgz", + "integrity": "sha512-kfWzA5oYrTpslTg5fN50G04zSypiYQzjZv3FLjbZkk6kta5fo4JkERKjTeA8x4XNojb+pfmjMBB0yIh2w2dDRw==", + "license": "MIT", + "dependencies": { + "@turf/circle": "^7.2.0", + "@turf/destination": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-chunk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-7.2.0.tgz", + "integrity": "sha512-1ODyL5gETtWSL85MPI0lgp/78vl95M39gpeBxePXyDIqx8geDP9kXfAzctuKdxBoR4JmOVM3NT7Fz7h+IEkC+g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/length": "^7.2.0", + "@turf/line-slice-along": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-intersect": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-7.2.0.tgz", + "integrity": "sha512-GhCJVEkc8EmggNi85EuVLoXF5T5jNVxmhIetwppiVyJzMrwkYAkZSYB3IBFYGUUB9qiNFnTwungVSsBV/S8ZiA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "sweepline-intersections": "^1.5.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-offset": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-7.2.0.tgz", + "integrity": "sha512-1+OkYueDCbnEWzbfBh3taVr+3SyM2bal5jfnSEuDiLA6jnlScgr8tn3INo+zwrUkPFZPPAejL1swVyO5TjUahw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-overlap": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-7.2.0.tgz", + "integrity": "sha512-NNn7/jg53+N10q2Kyt66bEDqN3101iW/1zA5FW7J6UbKApDFkByh+18YZq1of71kS6oUYplP86WkDp16LFpqqw==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-on-line": "^7.2.0", + "@turf/geojson-rbush": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/line-segment": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/nearest-point-on-line": "^7.2.0", + "@types/geojson": "^7946.0.10", + "fast-deep-equal": "^3.1.3", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-segment": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-7.2.0.tgz", + "integrity": "sha512-E162rmTF9XjVN4rINJCd15AdQGCBlNqeWN3V0YI1vOUpZFNT2ii4SqEMCcH2d+5EheHLL8BWVwZoOsvHZbvaWA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-7.2.0.tgz", + "integrity": "sha512-bHotzZIaU1GPV3RMwttYpDrmcvb3X2i1g/WUttPZWtKrEo2VVAkoYdeZ2aFwtogERYS4quFdJ/TDzAtquBC8WQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/nearest-point-on-line": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice-along": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-7.2.0.tgz", + "integrity": "sha512-4/gPgP0j5Rp+1prbhXqn7kIH/uZTmSgiubUnn67F8nb9zE+MhbRglhSlRYEZxAVkB7VrGwjyolCwvrROhjHp2A==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "^7.2.0", + "@turf/destination": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-split": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-7.2.0.tgz", + "integrity": "sha512-yJTZR+c8CwoKqdW/aIs+iLbuFwAa3Yan+EOADFQuXXIUGps3bJUXx/38rmowNoZbHyP1np1+OtrotyHu5uBsfQ==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/geojson-rbush": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/line-intersect": "^7.2.0", + "@turf/line-segment": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/nearest-point-on-line": "^7.2.0", + "@turf/square": "^7.2.0", + "@turf/truncate": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-to-polygon": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-7.2.0.tgz", + "integrity": "sha512-iKpJqc7EYc5NvlD4KaqrKKO6mXR7YWO/YwtW60E2FnsF/blnsy9OfAOcilYHgH3S/V/TT0VedC7DW7Kgjy2EIA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/mask": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-7.2.0.tgz", + "integrity": "sha512-ulJ6dQqXC0wrjIoqFViXuMUdIPX5Q6GPViZ3kGfeVijvlLM7kTFBsZiPQwALSr5nTQg4Ppf3FD0Jmg8IErPrgA==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.2.0.tgz", + "integrity": "sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/midpoint": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-7.2.0.tgz", + "integrity": "sha512-AMn5S9aSrbXdE+Q4Rj+T5nLdpfpn+mfzqIaEKkYI021HC0vb22HyhQHsQbSeX+AWcS4CjD1hFsYVcgKI+5qCfw==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "^7.2.0", + "@turf/destination": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/moran-index": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-7.2.0.tgz", + "integrity": "sha512-Aexh1EmXVPJhApr9grrd120vbalIthcIsQ3OAN2Tqwf+eExHXArJEJqGBo9IZiQbIpFJeftt/OvUvlI8BeO1bA==", + "license": "MIT", + "dependencies": { + "@turf/distance-weight": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-neighbor-analysis": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-neighbor-analysis/-/nearest-neighbor-analysis-7.2.0.tgz", + "integrity": "sha512-LmP/crXb7gilgsL0wL9hsygqc537W/a1W5r9XBKJT4SKdqjoXX5APJatJfd3nwXbRIqwDH0cDA9/YyFjBPlKnA==", + "license": "MIT", + "dependencies": { + "@turf/area": "^7.2.0", + "@turf/bbox": "^7.2.0", + "@turf/bbox-polygon": "^7.2.0", + "@turf/centroid": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/nearest-point": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-7.2.0.tgz", + "integrity": "sha512-0wmsqXZ8CGw4QKeZmS+NdjYTqCMC+HXZvM3XAQIU6k6laNLqjad2oS4nDrtcRs/nWDvcj1CR+Io7OiQ6sbpn5Q==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-on-line": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-7.2.0.tgz", + "integrity": "sha512-UOhAeoDPVewBQV+PWg1YTMQcYpJsIqfW5+EuZ5vJl60XwUa0+kqB/eVfSLNXmHENjKKIlEt9Oy9HIDF4VeWmXA==", + "license": "MIT", + "dependencies": { + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-to-line": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-7.2.0.tgz", + "integrity": "sha512-EorU7Qj30A7nAjh++KF/eTPDlzwuuV4neBz7tmSTB21HKuXZAR0upJsx6M2X1CSyGEgNsbFB0ivNKIvymRTKBw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/point-to-line-distance": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/planepoint": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-7.2.0.tgz", + "integrity": "sha512-8Vno01tvi5gThUEKBQ46CmlEKDAwVpkl7stOPFvJYlA1oywjAL4PsmgwjXgleZuFtXQUPBNgv5a42Pf438XP4g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-grid": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-7.2.0.tgz", + "integrity": "sha512-ai7lwBV2FREPW3XiUNohT4opC1hd6+F56qZe20xYhCTkTD9diWjXHiNudQPSmVAUjgMzQGasblQQqvOdL+bJ3Q==", + "license": "MIT", + "dependencies": { + "@turf/boolean-within": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-on-feature": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-7.2.0.tgz", + "integrity": "sha512-ksoYoLO9WtJ/qI8VI9ltF+2ZjLWrAjZNsCsu8F7nyGeCh4I8opjf4qVLytFG44XA2qI5yc6iXDpyv0sshvP82Q==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/center": "^7.2.0", + "@turf/explode": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/nearest-point": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-line-distance": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-7.2.0.tgz", + "integrity": "sha512-fB9Rdnb5w5+t76Gho2dYDkGe20eRrFk8CXi4v1+l1PC8YyLXO+x+l3TrtT8HzL/dVaZeepO6WUIsIw3ditTOPg==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/nearest-point-on-line": "^7.2.0", + "@turf/projection": "^7.2.0", + "@turf/rhumb-bearing": "^7.2.0", + "@turf/rhumb-distance": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-polygon-distance": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/point-to-polygon-distance/-/point-to-polygon-distance-7.2.0.tgz", + "integrity": "sha512-w+WYuINgTiFjoZemQwOaQSje/8Kq+uqJOynvx7+gleQPHyWQ3VtTodtV4LwzVzXz8Sf7Mngx1Jcp2SNai5CJYA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/point-to-line-distance": "^7.2.0", + "@turf/polygon-to-line": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/points-within-polygon": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-7.2.0.tgz", + "integrity": "sha512-jRKp8/mWNMzA+hKlQhxci97H5nOio9tp14R2SzpvkOt+cswxl+NqTEi1hDd2XetA7tjU0TSoNjEgVY8FfA0S6w==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-smooth": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-7.2.0.tgz", + "integrity": "sha512-KCp9wF2IEynvGXVhySR8oQ2razKP0zwg99K+fuClP21pSKCFjAPaihPEYq6e8uI/1J7ibjL5++6EMl+LrUTrLg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-tangents": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-7.2.0.tgz", + "integrity": "sha512-AHUUPmOjiQDrtP/ODXukHBlUG0C/9I1je7zz50OTfl2ZDOdEqFJQC3RyNELwq07grTXZvg5TS5wYx/Y7nsm47g==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/boolean-within": "^7.2.0", + "@turf/explode": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/nearest-point": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-to-line": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-7.2.0.tgz", + "integrity": "sha512-9jeTN3LiJ933I5sd4K0kwkcivOYXXm1emk0dHorwXeSFSHF+nlYesEW3Hd889wb9lZd7/SVLMUeX/h39mX+vCA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygonize": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-7.2.0.tgz", + "integrity": "sha512-U9v+lBhUPDv+nsg/VcScdiqCB59afO6CHDGrwIl2+5i6Ve+/KQKjpTV/R+NqoC1iMXAEq3brY6HY8Ukp/pUWng==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/envelope": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/projection": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-7.2.0.tgz", + "integrity": "sha512-/qke5vJScv8Mu7a+fU3RSChBRijE6EVuFHU3RYihMuYm04Vw8dBMIs0enEpoq0ke/IjSbleIrGQNZIMRX9EwZQ==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/quadrat-analysis": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/quadrat-analysis/-/quadrat-analysis-7.2.0.tgz", + "integrity": "sha512-fDQh3+ldYNxUqS6QYlvJ7GZLlCeDZR6tD3ikdYtOsSemwW1n/4gm2xcgWJqy3Y0uszBwxc13IGGY7NGEjHA+0w==", + "license": "MIT", + "dependencies": { + "@turf/area": "^7.2.0", + "@turf/bbox": "^7.2.0", + "@turf/bbox-polygon": "^7.2.0", + "@turf/centroid": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/point-grid": "^7.2.0", + "@turf/random": "^7.2.0", + "@turf/square-grid": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/random": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-7.2.0.tgz", + "integrity": "sha512-fNXs5mOeXsrirliw84S8UCNkpm4RMNbefPNsuCTfZEXhcr1MuHMzq4JWKb4FweMdN1Yx2l/xcytkO0s71cJ50w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rectangle-grid": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-7.2.0.tgz", + "integrity": "sha512-f0o5ifvy0Ml/nHDJzMNcuSk4h11aa3BfvQNnYQhLpuTQu03j/ICZNlzKTLxwjcUqvxADUifty7Z9CX5W6zky4A==", + "license": "MIT", + "dependencies": { + "@turf/boolean-intersects": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rewind": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-7.2.0.tgz", + "integrity": "sha512-SZpRAZiZsE22+HVz6pEID+ST25vOdpAMGk5NO1JeqzhpMALIkIGnkG+xnun2CfYHz7wv8/Z0ADiAvei9rkcQYA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-clockwise": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-bearing": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-7.2.0.tgz", + "integrity": "sha512-jbdexlrR8X2ZauUciHx3tRwG+BXoMXke4B8p8/IgDlAfIrVdzAxSQN89FMzIKnjJ/kdLjo9bFGvb92bu31Etug==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-destination": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-7.2.0.tgz", + "integrity": "sha512-U9OLgLAHlH4Wfx3fBZf3jvnkDjdTcfRan5eI7VPV1+fQWkOteATpzkiRjCvSYK575GljVwWBjkKca8LziGWitQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-distance": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-7.2.0.tgz", + "integrity": "sha512-NsijTPON1yOc9tirRPEQQuJ5aQi7pREsqchQquaYKbHNWsexZjcDi4wnw2kM3Si4XjmgynT+2f7aXH7FHarHzw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sample": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-7.2.0.tgz", + "integrity": "sha512-f+ZbcbQJ9glQ/F26re8LadxO0ORafy298EJZe6XtbctRTJrNus6UNAsl8+GYXFqMnXM22tbTAznnJX3ZiWNorA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sector": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-7.2.0.tgz", + "integrity": "sha512-zL06MjbbMG4DdpiNz+Q9Ax8jsCekt3R76uxeWShulAGkyDB5smdBOUDoRwxn05UX7l4kKv4Ucq2imQXhxKFd1w==", + "license": "MIT", + "dependencies": { + "@turf/circle": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/line-arc": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/shortest-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-7.2.0.tgz", + "integrity": "sha512-6fpx8feZ2jMSaeRaFdqFShGWkNb+veUOeyLFSHA/aRD9n/e9F2pWZoRbQWKbKTpcKFJ2FnDEqCZnh/GrcAsqWA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/bbox-polygon": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/clean-coords": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/transform-scale": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/simplify": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-7.2.0.tgz", + "integrity": "sha512-9YHIfSc8BXQfi5IvEMbCeQYqNch0UawIGwbboJaoV8rodhtk6kKV2wrpXdGqk/6Thg6/RWvChJFKVVTjVrULyQ==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-7.2.0.tgz", + "integrity": "sha512-9pMoAGFvqzCDOlO9IRSSBCGXKbl8EwMx6xRRBMKdZgpS0mZgfm9xiptMmx/t1m4qqHIlb/N+3MUF7iMBx6upcA==", + "license": "MIT", + "dependencies": { + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square-grid": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-7.2.0.tgz", + "integrity": "sha512-EmzGXa90hz+tiCOs9wX+Lak6pH0Vghb7QuX6KZej+pmWi3Yz7vdvQLmy/wuN048+wSkD5c8WUo/kTeNDe7GnmA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/rectangle-grid": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/standard-deviational-ellipse": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-7.2.0.tgz", + "integrity": "sha512-+uC0pR2nRjm90JvMXe/2xOCZsYV2II1ZZ2zmWcBWv6bcFXBspcxk2QfCC3k0bj6jDapELzoQgnn3cG5lbdQV2w==", + "license": "MIT", + "dependencies": { + "@turf/center-mean": "^7.2.0", + "@turf/ellipse": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/points-within-polygon": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tag": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-7.2.0.tgz", + "integrity": "sha512-TAFvsbp5TCBqXue8ui+CtcLsPZ6NPC88L8Ad6Hb/R6VAi21qe0U42WJHQYXzWmtThoTNwxi+oKSeFbRDsr0FIA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tesselate": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-7.2.0.tgz", + "integrity": "sha512-zHGcG85aOJJu1seCm+CYTJ3UempX4Xtyt669vFG6Hbr/Hc7ii6STQ2ysFr7lJwFtU9uyYhphVrrgwIqwglvI/Q==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "earcut": "^2.2.4", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tin": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-7.2.0.tgz", + "integrity": "sha512-y24Vt3oeE6ZXvyLJamP0Ke02rPlDGE9gF7OFADnR0mT+2uectb0UTIBC3kKzON80TEAlA3GXpKFkCW5Fo/O/Kg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-rotate": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-7.2.0.tgz", + "integrity": "sha512-EMCj0Zqy3cF9d3mGRqDlYnX2ZBXe3LgT+piDR0EuF5c5sjuKErcFcaBIsn/lg1gp4xCNZFinkZ3dsFfgGHf6fw==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/rhumb-bearing": "^7.2.0", + "@turf/rhumb-destination": "^7.2.0", + "@turf/rhumb-distance": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@tailwindcss/forms": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", - "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", - "dependencies": { - "mini-svg-data-uri": "^1.2.3" + "node_modules/@turf/transform-scale": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-7.2.0.tgz", + "integrity": "sha512-HYB+pw938eeI8s1/zSWFy6hq+t38fuUaBb0jJsZB1K9zQ1WjEYpPvKF/0//80zNPlyxLv3cOkeBucso3hzI07A==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "^7.2.0", + "@turf/center": "^7.2.0", + "@turf/centroid": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/rhumb-bearing": "^7.2.0", + "@turf/rhumb-destination": "^7.2.0", + "@turf/rhumb-distance": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@testing-library/dom": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", - "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" + "node_modules/@turf/transform-translate": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-7.2.0.tgz", + "integrity": "sha512-zAglR8MKCqkzDTjGMIQgbg/f+Q3XcKVzr9cELw5l9CrS1a0VTSDtBZLDm0kWx0ankwtam7ZmI2jXyuQWT8Gbug==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/rhumb-destination": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "engines": { - "node": ">=12" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "node_modules/@turf/triangle-grid": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-7.2.0.tgz", + "integrity": "sha512-4gcAqWKh9hg6PC5nNSb9VWyLgl821cwf9yR9yEzQhEFfwYL/pZONBWCO1cwVF23vSYMSMm+/TwqxH4emxaArfw==", + "license": "MIT", "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" + "@turf/distance": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/intersect": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "engines": { - "node": ">=8", - "npm": ">=6", - "yarn": ">=1" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@turf/truncate": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-7.2.0.tgz", + "integrity": "sha512-jyFzxYbPugK4XjV5V/k6Xr3taBjjvo210IbPHJXw0Zh7Y6sF+hGxeRVtSuZ9VP/6oRyqAOHKUrze+OOkPqBgUg==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" - }, - "engines": { - "node": ">=12" + "node_modules/@turf/turf": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-7.2.0.tgz", + "integrity": "sha512-G1kKBu4hYgoNoRJgnpJohNuS7bLnoWHZ2G/4wUMym5xOSiYah6carzdTEsMoTsauyi7ilByWHx5UHwbjjCVcBw==", + "license": "MIT", + "dependencies": { + "@turf/along": "^7.2.0", + "@turf/angle": "^7.2.0", + "@turf/area": "^7.2.0", + "@turf/bbox": "^7.2.0", + "@turf/bbox-clip": "^7.2.0", + "@turf/bbox-polygon": "^7.2.0", + "@turf/bearing": "^7.2.0", + "@turf/bezier-spline": "^7.2.0", + "@turf/boolean-clockwise": "^7.2.0", + "@turf/boolean-concave": "^7.2.0", + "@turf/boolean-contains": "^7.2.0", + "@turf/boolean-crosses": "^7.2.0", + "@turf/boolean-disjoint": "^7.2.0", + "@turf/boolean-equal": "^7.2.0", + "@turf/boolean-intersects": "^7.2.0", + "@turf/boolean-overlap": "^7.2.0", + "@turf/boolean-parallel": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/boolean-point-on-line": "^7.2.0", + "@turf/boolean-touches": "^7.2.0", + "@turf/boolean-valid": "^7.2.0", + "@turf/boolean-within": "^7.2.0", + "@turf/buffer": "^7.2.0", + "@turf/center": "^7.2.0", + "@turf/center-mean": "^7.2.0", + "@turf/center-median": "^7.2.0", + "@turf/center-of-mass": "^7.2.0", + "@turf/centroid": "^7.2.0", + "@turf/circle": "^7.2.0", + "@turf/clean-coords": "^7.2.0", + "@turf/clone": "^7.2.0", + "@turf/clusters": "^7.2.0", + "@turf/clusters-dbscan": "^7.2.0", + "@turf/clusters-kmeans": "^7.2.0", + "@turf/collect": "^7.2.0", + "@turf/combine": "^7.2.0", + "@turf/concave": "^7.2.0", + "@turf/convex": "^7.2.0", + "@turf/destination": "^7.2.0", + "@turf/difference": "^7.2.0", + "@turf/dissolve": "^7.2.0", + "@turf/distance": "^7.2.0", + "@turf/distance-weight": "^7.2.0", + "@turf/ellipse": "^7.2.0", + "@turf/envelope": "^7.2.0", + "@turf/explode": "^7.2.0", + "@turf/flatten": "^7.2.0", + "@turf/flip": "^7.2.0", + "@turf/geojson-rbush": "^7.2.0", + "@turf/great-circle": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/hex-grid": "^7.2.0", + "@turf/interpolate": "^7.2.0", + "@turf/intersect": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@turf/isobands": "^7.2.0", + "@turf/isolines": "^7.2.0", + "@turf/kinks": "^7.2.0", + "@turf/length": "^7.2.0", + "@turf/line-arc": "^7.2.0", + "@turf/line-chunk": "^7.2.0", + "@turf/line-intersect": "^7.2.0", + "@turf/line-offset": "^7.2.0", + "@turf/line-overlap": "^7.2.0", + "@turf/line-segment": "^7.2.0", + "@turf/line-slice": "^7.2.0", + "@turf/line-slice-along": "^7.2.0", + "@turf/line-split": "^7.2.0", + "@turf/line-to-polygon": "^7.2.0", + "@turf/mask": "^7.2.0", + "@turf/meta": "^7.2.0", + "@turf/midpoint": "^7.2.0", + "@turf/moran-index": "^7.2.0", + "@turf/nearest-neighbor-analysis": "^7.2.0", + "@turf/nearest-point": "^7.2.0", + "@turf/nearest-point-on-line": "^7.2.0", + "@turf/nearest-point-to-line": "^7.2.0", + "@turf/planepoint": "^7.2.0", + "@turf/point-grid": "^7.2.0", + "@turf/point-on-feature": "^7.2.0", + "@turf/point-to-line-distance": "^7.2.0", + "@turf/point-to-polygon-distance": "^7.2.0", + "@turf/points-within-polygon": "^7.2.0", + "@turf/polygon-smooth": "^7.2.0", + "@turf/polygon-tangents": "^7.2.0", + "@turf/polygon-to-line": "^7.2.0", + "@turf/polygonize": "^7.2.0", + "@turf/projection": "^7.2.0", + "@turf/quadrat-analysis": "^7.2.0", + "@turf/random": "^7.2.0", + "@turf/rectangle-grid": "^7.2.0", + "@turf/rewind": "^7.2.0", + "@turf/rhumb-bearing": "^7.2.0", + "@turf/rhumb-destination": "^7.2.0", + "@turf/rhumb-distance": "^7.2.0", + "@turf/sample": "^7.2.0", + "@turf/sector": "^7.2.0", + "@turf/shortest-path": "^7.2.0", + "@turf/simplify": "^7.2.0", + "@turf/square": "^7.2.0", + "@turf/square-grid": "^7.2.0", + "@turf/standard-deviational-ellipse": "^7.2.0", + "@turf/tag": "^7.2.0", + "@turf/tesselate": "^7.2.0", + "@turf/tin": "^7.2.0", + "@turf/transform-rotate": "^7.2.0", + "@turf/transform-scale": "^7.2.0", + "@turf/transform-translate": "^7.2.0", + "@turf/triangle-grid": "^7.2.0", + "@turf/truncate": "^7.2.0", + "@turf/union": "^7.2.0", + "@turf/unkink-polygon": "^7.2.0", + "@turf/voronoi": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@testing-library/user-event": { - "version": "14.4.3", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", - "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", - "engines": { - "node": ">=12", - "npm": ">=6" + "node_modules/@turf/union": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-7.2.0.tgz", + "integrity": "sha512-Xex/cfKSmH0RZRWSJl4RLlhSmEALVewywiEXcu0aIxNbuZGTcpNoI0h4oLFrE/fUd0iBGFg/EGLXRL3zTfpg6g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" + "node_modules/@turf/unkink-polygon": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-7.2.0.tgz", + "integrity": "sha512-dFPfzlIgkEr15z6oXVxTSWshWi51HeITGVFtl1GAKGMtiXJx1uMqnfRsvljqEjaQu/4AzG1QAp3b+EkSklQSiQ==", + "license": "MIT", + "dependencies": { + "@turf/area": "^7.2.0", + "@turf/boolean-point-in-polygon": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "engines": { - "node": ">=10.13.0" + "node_modules/@turf/voronoi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-7.2.0.tgz", + "integrity": "sha512-3K6N0LtJsWTXxPb/5N2qD9e8f4q8+tjTbGV3lE3v8x06iCnNlnuJnqM5NZNPpvgvCatecBkhClO3/3RndE61Fw==", + "license": "MIT", + "dependencies": { + "@turf/clone": "^7.2.0", + "@turf/helpers": "^7.2.0", + "@turf/invariant": "^7.2.0", + "@types/d3-voronoi": "^1.1.12", + "@types/geojson": "^7946.0.10", + "d3-voronoi": "1.1.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" } }, "node_modules/@types/aria-query": { @@ -4125,6 +6201,12 @@ "@types/node": "*" } }, + "node_modules/@types/d3-voronoi": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.12.tgz", + "integrity": "sha512-DauBl25PKZZ0WVJr42a6CNvI6efsdzofl9sajqZr2Gf5Gu733WkDdUGiPkUHXiUvYGzNNlFQde2wdZdfQPG+yw==", + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -4174,6 +6256,12 @@ "@types/range-parser": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.15", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz", + "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==", + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -4672,11 +6760,12 @@ } }, "node_modules/@usace/create-keycloak-auth-bundle": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@usace/create-keycloak-auth-bundle/-/create-keycloak-auth-bundle-0.2.1.tgz", - "integrity": "sha512-uBJvSQzZ/yRq/bstPITjkWSqooO3kNc46MghFMzZ/e2Bp2VxSgwrTqRhu0xM37Wi6eg9DHqHK/LrquZbvFoipQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@usace/create-keycloak-auth-bundle/-/create-keycloak-auth-bundle-2.0.1.tgz", + "integrity": "sha512-SLFRrkGdzbry2XxIGV01VUhujkXLIe5NySuMYXZ4PkrMj2mpNIBPCNB4vs/zyrl9nx2N7PdOvtWUJy6ArVORBg==", + "license": "MIT", "dependencies": { - "@usace/keycloak": "^0.2.1" + "@usace/keycloak": "^2.0.0" }, "peerDependencies": { "redux-bundler": "^28.0.3" @@ -4691,9 +6780,10 @@ } }, "node_modules/@usace/keycloak": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@usace/keycloak/-/keycloak-0.2.1.tgz", - "integrity": "sha512-UnWID0E176pwx5EhcYf2uM/6mIu1emoogfv6P/DFQ+c+rS/EvPs9n32BT2idzUqizvXHXQOjn28AOTdlygqvWA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@usace/keycloak/-/keycloak-2.0.0.tgz", + "integrity": "sha512-00CN0DBPStaA6ffkqhjNQrZX4dD3ua3fv+WDue5GeByc5h4MUL07mJyrfNEzdxO7cWKU9M9PpauZnIl3YH83kA==", + "license": "MIT" }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", @@ -5622,6 +7712,15 @@ "node": "*" } }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -5630,6 +7729,12 @@ "node": ">=8" } }, + "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/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -5795,6 +7900,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/but-unzip": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/but-unzip/-/but-unzip-0.1.4.tgz", + "integrity": "sha512-Q5/55MTk0PHjxtYyZBbhIVMJP0+FNc/AOKBrrnqaxnbJR4I7w+R4CMRNYMxUQrKmCLrih7D1p4/nwZHMn7IToA==", + "license": "Apache-2.0" + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -6255,6 +8366,24 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concaveman": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.1.tgz", + "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==", + "license": "ISC", + "dependencies": { + "point-in-polygon": "^1.1.0", + "rbush": "^3.0.1", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, + "node_modules/concaveman/node_modules/robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==", + "license": "Unlicense" + }, "node_modules/confusing-browser-globals": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", @@ -6807,9 +8936,10 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/d3": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz", - "integrity": "sha512-WXty7qOGSHb7HR7CfOzwN1Gw04MUOzN8qh9ZUsvwycIMb4DYMpY9xczZ6jUorGtO6bR9BPMPaueIKwiDxu9uiQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", "dependencies": { "d3-array": "3", "d3-axis": "3", @@ -7083,9 +9213,10 @@ } }, "node_modules/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" @@ -7161,6 +9292,12 @@ "d3-selection": "2 - 3" } }, + "node_modules/d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw==", + "license": "BSD-3-Clause" + }, "node_modules/d3-zoom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", @@ -7197,15 +9334,13 @@ } }, "node_modules/date-fns": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", - "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", - "engines": { - "node": ">=0.11" - }, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/debug": { @@ -7611,6 +9746,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "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/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8718,8 +10859,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.2.12", @@ -9208,6 +11348,39 @@ "node": ">=6.9.0" } }, + "node_modules/geojson-equality-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/geojson-equality-ts/-/geojson-equality-ts-1.0.2.tgz", + "integrity": "sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.14" + } + }, + "node_modules/geojson-polygon-self-intersections": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/geojson-polygon-self-intersections/-/geojson-polygon-self-intersections-1.2.1.tgz", + "integrity": "sha512-/QM1b5u2d172qQVO//9CGRa49jEmclKEsYOQmWP9ooEjj63tBM51m2805xsbxkzlEELQ2REgTf700gUhhlegxA==", + "license": "MIT", + "dependencies": { + "rbush": "^2.0.1" + } + }, + "node_modules/geojson-polygon-self-intersections/node_modules/quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==", + "license": "ISC" + }, + "node_modules/geojson-polygon-self-intersections/node_modules/rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "license": "MIT", + "dependencies": { + "quickselect": "^1.0.1" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9967,6 +12140,15 @@ "node": ">=12" } }, + "node_modules/interval-tree-1d": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.4.tgz", + "integrity": "sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==", + "license": "MIT", + "dependencies": { + "binary-search-bounds": "^2.0.0" + } + }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -12595,6 +14777,15 @@ "node": ">=0.10.0" } }, + "node_modules/jsts": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/jsts/-/jsts-2.7.1.tgz", + "integrity": "sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==", + "license": "(EDL-1.0 OR EPL-1.0)", + "engines": { + "node": ">= 12" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -12650,6 +14841,21 @@ "language-subtag-registry": "~0.3.2" } }, + "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" + }, + "node_modules/leaflet-easybutton": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/leaflet-easybutton/-/leaflet-easybutton-2.4.0.tgz", + "integrity": "sha512-O+qsQq4zTF6ds8VClnytobTH/MKalctlPpiA8L+bNKHP14J3lgJpvEd/jSpq9mHTI6qOzRAvbQX6wS6qNwThvg==", + "license": "MIT", + "dependencies": { + "leaflet": "^1.0.1" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -12836,6 +15042,12 @@ "tmpl": "1.0.5" } }, + "node_modules/marchingsquares": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/marchingsquares/-/marchingsquares-1.3.3.tgz", + "integrity": "sha512-gz6nNQoVK7Lkh2pZulrT4qd4347S/toG9RXH2pyzhLgkL5mLkBoqgv4EvAGXcV0ikDW72n/OQb3Xe8bGagQZCg==", + "license": "AGPL-3.0" + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -12897,6 +15109,12 @@ "node": ">= 0.6" } }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -13554,6 +15772,12 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "node_modules/parsedbf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parsedbf/-/parsedbf-2.0.0.tgz", + "integrity": "sha512-WNjKn/cwgGBkXqQLif+2VMEahcRHkBRU0/RfBWZ7Vj7snRNNW63yW1mVuuHRDyXTRxuGCzAHHBcr/Fn+U/bXjQ==", + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -13795,6 +16019,31 @@ "node": ">=4" } }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==", + "license": "MIT" + }, + "node_modules/point-in-polygon-hao": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.2.4.tgz", + "integrity": "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==", + "license": "MIT", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/polyclip-ts": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/polyclip-ts/-/polyclip-ts-0.16.8.tgz", + "integrity": "sha512-JPtKbDRuPEuAjuTdhR62Gph7Is2BS1Szx69CFOO3g71lpJDFo78k4tFyi+qFOMVPePEzdSKkpGU3NBXPHHjvKQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.1.0", + "splaytree-ts": "^1.0.2" + } + }, "node_modules/postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -15083,6 +17332,16 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "node_modules/proj4": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.15.0.tgz", + "integrity": "sha512-LqCNEcPdI03BrCHxPLj29vsd5afsm+0sV1H/O3nTDKrv8/LA01ea1z4QADDMjUqxSXWnrmmQDjqFm1J/uZ5RLw==", + "license": "MIT", + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.4.0" + } + }, "node_modules/promise": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", @@ -15218,6 +17477,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", @@ -15281,6 +17546,15 @@ "node": ">=0.10.0" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "license": "MIT", + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -15335,6 +17609,22 @@ "react-dom": "^16.9.0 || ^17 || ^18" } }, + "node_modules/react-datepicker/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -15407,6 +17697,20 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-leaflet": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", + "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^2.1.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/react-onclickoutside": { "version": "6.12.2", "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz", @@ -15643,7 +17947,8 @@ "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.15.1", @@ -15918,9 +18223,10 @@ } }, "node_modules/robust-predicates": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", - "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" }, "node_modules/rollup": { "version": "2.79.1", @@ -16369,6 +18675,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shpjs": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/shpjs/-/shpjs-6.1.0.tgz", + "integrity": "sha512-uaUpod7uIWetJK80yiiedZ3x4z9ZAPgDVT89N27+8F97Z8ZOqmu88P96I6CBC8N+YyERqdneZNT/wNFUEnzNpw==", + "license": "MIT", + "dependencies": { + "but-unzip": "^0.1.4", + "parsedbf": "^2.0.0", + "proj4": "^2.1.4" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -16394,6 +18711,12 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, + "node_modules/skmeans": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", + "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==", + "license": "MIT" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -16513,6 +18836,12 @@ "wbuf": "^1.7.3" } }, + "node_modules/splaytree-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/splaytree-ts/-/splaytree-ts-1.0.2.tgz", + "integrity": "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA==", + "license": "BDS-3-Clause" + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -16978,6 +19307,15 @@ "node": ">=4" } }, + "node_modules/sweepline-intersections": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sweepline-intersections/-/sweepline-intersections-1.5.0.tgz", + "integrity": "sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==", + "license": "MIT", + "dependencies": { + "tinyqueue": "^2.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -17088,6 +19426,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/terra-draw": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/terra-draw/-/terra-draw-1.0.0.tgz", + "integrity": "sha512-LMD5wLHHSfkXOX0eGjhUV/Pnxedn5MvsKBvGOP6txCY1D1LAIVnIx1g+trTXfBHUjogDJj/vmswsGMD/5LtNKQ==", + "license": "MIT" + }, + "node_modules/terra-draw-leaflet-adapter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/terra-draw-leaflet-adapter/-/terra-draw-leaflet-adapter-1.0.0.tgz", + "integrity": "sha512-4cMwJBvhXCUWg7dxg047KSoj6xCuJj7CMVIbV0+hu41apb2AgXDqM2J2K7wIFzVp0ZCJlgDUJq2eRxhvG+NyfA==", + "license": "MIT", + "peerDependencies": { + "leaflet": "^1.9.4", + "terra-draw": "^1.0.0" + } + }, "node_modules/terser": { "version": "5.19.4", "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.4.tgz", @@ -17178,6 +19532,12 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "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/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -17212,6 +19572,44 @@ "node": ">=0.6" } }, + "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/topojson-server": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.1.tgz", + "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==", + "license": "ISC", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, + "node_modules/topojson-server/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/tough-cookie": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", @@ -17288,10 +19686,10 @@ } }, "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/tsutils": { "version": "3.21.0", @@ -18151,6 +20549,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wkt-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.4.0.tgz", + "integrity": "sha512-qpwO7Ihds/YYDTi1aADFTI1Sm9YC/tTe3SHD24EeIlZxy7Ik6a1b4HOz7jAi0xdUAw487duqpo8OGu+Tf4nwlQ==", + "license": "MIT" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index adcd74a..bda08a9 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,47 @@ { "name": "cumulus-ui", - "version": "1.4.4", + "version": "1.5.0", "private": true, "dependencies": { + "@date-fns/utc": "^2.1.0", "@headlessui/react": "^1.4.3", "@heroicons/react": "^2.0.14", - "@observablehq/plot": "^0.4.1", + "@observablehq/plot": "^0.6.15", "@tailwindcss/forms": "^0.5.3", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", + "@turf/turf": "^7.2.0", "@usace/create-jwt-api-bundle": "^0.3.1", - "@usace/create-keycloak-auth-bundle": "^0.2.1", + "@usace/create-keycloak-auth-bundle": "^2.0.1", "@usace/create-rest-bundle": "^0.1.2", - "d3": "^7.3.0", - "date-fns": "^2.28.0", + "d3": "^7.9.0", + "date-fns": "^4.0.0", "internal-nav-helper": "^3.1.0", + "leaflet": "^1.9.4", + "leaflet-easybutton": "^2.4.0", "money-clip": "^3.0.5", + "proj4leaflet": "^1.0.2", "react": "^18.2.0", + "react-calendar": "^5.1.0", "react-colorful": "^5.5.1", - "react-datepicker": "^4.7.0", + "react-date-picker": "^11.0.0", + "react-datepicker": "^4.25.0", "react-dom": "^18.2.0", + "react-leaflet": "^4.0.0", "react-select": "^5.2.2", "redux-bundler": "^28.0.3", "redux-bundler-react": "^1.2.0", + "shpjs": "^6.1.0", + "terra-draw": "^1.14.0", + "terra-draw-leaflet-adapter": "^1.0.3", "web-vitals": "^2.1.4" }, + "overrides": { + "d3": { + "d3-scale-chromatic": "^3.1.0" + } + }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", @@ -60,4 +76,4 @@ "react-scripts": "5.0.1", "tailwindcss": "^3.0.18" } -} \ No newline at end of file +} diff --git a/public/login_gov.svg b/public/login_gov.svg new file mode 100644 index 0000000..55ef549 --- /dev/null +++ b/public/login_gov.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/marker-icon.png b/public/marker-icon.png new file mode 100644 index 0000000..950edf2 Binary files /dev/null and b/public/marker-icon.png differ diff --git a/public/marker-shadow.png b/public/marker-shadow.png new file mode 100644 index 0000000..9fd2979 Binary files /dev/null and b/public/marker-shadow.png differ diff --git a/src/App.js b/src/App.js index c599ef5..c7b5a28 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,8 @@ import Modal from './app-components/Modal'; import AppContainer from './app-container/app-container'; // import Notification from './app-components/Notification'; +import "leaflet/dist/leaflet.css"; + const App = () => { return ( <> diff --git a/src/app-bundles/download-bundle.js b/src/app-bundles/download-bundle.js index a55c0d8..df95c31 100644 --- a/src/app-bundles/download-bundle.js +++ b/src/app-bundles/download-bundle.js @@ -31,7 +31,7 @@ const downloadBundle = createRestBundle({ // const apiRoot = store.selectApiRoot(); const authToken = store.selectAuthToken(); - fetch(`${apiURL}/my_downloads`, { + fetch(`${apiURL}/downloads`, { method: 'POST', body: JSON.stringify(payload), headers: { @@ -65,8 +65,10 @@ const downloadBundle = createRestBundle({ } return downloads.filter( (d) => - d.status === 'INITIATED' && - d.progress < 100 && + // Continue polling if status is INITIATED OR progress < 100 (not both) + // This ensures we keep polling until we receive a response where BOTH + // status != 'INITIATED' AND progress == 100 + (d.status === 'INITIATED' || d.progress < 100) && // it is possible a stuck download ('INITIATED' and < 100 progress) could remain in the database. // When that happens this function will call the actionCreator doDownloadPoll // in an infinite loop forever. Only evaluate downloads from the past 8 hours. diff --git a/src/app-bundles/index.js b/src/app-bundles/index.js index 33d5311..3ba1e5c 100644 --- a/src/app-bundles/index.js +++ b/src/app-bundles/index.js @@ -28,6 +28,7 @@ import downloadMetricsBundle from './download-metrics-bundle'; import adminDownloadBundle from './admin-download-bundle'; import productIngestStatusBundle from './project-ingest-status'; import dssDatatypeBundle from './dss-datatype-bundle'; +import userRegionBundle from './user-regions-bundle'; const mockTokens = { ADMIN: @@ -60,18 +61,19 @@ export default composeBundles( adminDownloadBundle, productIngestStatusBundle, dssDatatypeBundle, + userRegionBundle, createAuthBundle({ name: 'auth', - host: process.env.REACT_APP_AUTH_HOST, + directGrantUrl: process.env.REACT_APP_AUTH_HOST, + browserFlowUrl: process.env.REACT_APP_AUTH_HOST.replace( + 'identityc', + 'identity' + ), realm: 'cwbi', client: 'cumulus', redirectUrl: process.env.REACT_APP_AUTH_REDIRECT_URL, refreshInterval: 120, sessionEndWarning: 600, - mock: - process.env.NODE_ENV === 'development' && mockUser && mockTokens[mockUser] - ? true - : false, mockToken: mockUser ? mockTokens[mockUser] : null, }), createJwtApiBundle({ @@ -87,9 +89,16 @@ export default composeBundles( const token_routes = { '/api/my_downloads': true, '/api/downloads': true, + '/api/user-regions': true, + '/api/user-regions/public': true, }; - if (urlObj.pathname && token_routes[urlObj.pathname]) { - return false; + // Check if the pathname starts with any of the token routes + if (urlObj.pathname) { + for (const route in token_routes) { + if (urlObj.pathname.startsWith(route)) { + return false; // Don't skip token (include it) + } + } } return true; } diff --git a/src/app-bundles/product-bundle.js b/src/app-bundles/product-bundle.js index a3ff470..21ae024 100644 --- a/src/app-bundles/product-bundle.js +++ b/src/app-bundles/product-bundle.js @@ -3,6 +3,8 @@ import { createSelector } from 'redux-bundler'; import { getMonth, getYear, isAfter, isBefore } from 'date-fns'; const apiUrl = process.env.REACT_APP_CUMULUS_API_URL; +const cumulusBegin = new Date('01-Jan-1980'); +const MAX_DATE = new Date('01-Jan-3000'); export default createRestBundle({ name: 'product', @@ -114,13 +116,16 @@ export default createRestBundle({ selectProductDateRangeFrom: createSelector( 'selectProductItemsArray', (items) => { - let out = new Date('01-Jan-3000'); + let out = MAX_DATE; items.forEach((p) => { if (p.productfile_count > 0) { const d = new Date(p.after); if (isBefore(d, out)) out = d; } }); + if (out.getTime() === MAX_DATE.getTime()) { + out = cumulusBegin; + } return out; } ), @@ -128,13 +133,16 @@ export default createRestBundle({ selectProductDateRangeTo: createSelector( 'selectProductItemsArray', (items) => { - let out = new Date('01-Jan-1980'); + let out = cumulusBegin; items.forEach((p) => { if (p.productfile_count > 0) { const d = new Date(p.before); if (isAfter(d, out)) out = d; } }); + if (out.getTime() === cumulusBegin.getTime()) { + out = new Date(); + } return out; } ), diff --git a/src/app-bundles/product-filter-bundle.js b/src/app-bundles/product-filter-bundle.js index 41c2644..97be96c 100644 --- a/src/app-bundles/product-filter-bundle.js +++ b/src/app-bundles/product-filter-bundle.js @@ -12,6 +12,7 @@ const productFilterBundle = { dateFrom: new Date(), dateTo: new Date(), tags: [], + requiredTags: [], // Tags that products MUST have (AND logic) parameters: [], }; @@ -45,6 +46,10 @@ const productFilterBundle = { return state.productFilter.tags; }, + selectProductFilterRequiredTags: (state) => { + return state.productFilter.requiredTags; + }, + selectProductFilterParameters: (state) => { return state.productFilter.parameters; }, @@ -56,6 +61,7 @@ const productFilterBundle = { 'selectProductFilterDateFrom', 'selectProductFilterDateTo', 'selectProductFilterTags', + 'selectProductFilterRequiredTags', 'selectProductFilterParameters', ( items, @@ -64,8 +70,14 @@ const productFilterBundle = { dateFrom, dateTo, tags, + requiredTags, parameters ) => { + // Safety check - if items is not available yet, return empty array + if (!items || !Array.isArray(items)) { + return []; + } + return items.filter((product) => { let pass = true; @@ -96,7 +108,17 @@ const productFilterBundle = { } } - // apply tag filters + // apply required tag filters (must have ALL of these tags - AND logic) + if (pass && requiredTags.length > 0) { + for (var r = 0; r < requiredTags.length; r++) { + if (product.tags.indexOf(requiredTags[r]) === -1) { + pass = false; + break; + } + } + } + + // apply tag filters (must have ANY of these tags - OR logic) if (pass && tags.length > 0) { let matchFound = false; for (var t = 0; t < product.tags.length; t++) { @@ -174,6 +196,17 @@ const productFilterBundle = { }); }, + doProductFilterSetRequiredTags: + (requiredTags) => + ({ dispatch }) => { + dispatch({ + type: 'PRODUCT_FILTER_SET_VALUE', + payload: { + requiredTags, + }, + }); + }, + doProductFilterSetParameters: (parameters) => ({ dispatch }) => { diff --git a/src/app-bundles/user-regions-bundle.js b/src/app-bundles/user-regions-bundle.js new file mode 100644 index 0000000..3f6e99d --- /dev/null +++ b/src/app-bundles/user-regions-bundle.js @@ -0,0 +1,196 @@ +import createRestBundle from '@usace/create-rest-bundle'; +import { createSelector } from 'redux-bundler'; + +const apiURL = process.env.REACT_APP_CUMULUS_API_URL; + +export default createRestBundle({ + name: 'userRegion', + uid: 'id', + prefetch: true, + staleAfter: 60000, // 1 minute + persist: false, + routeParam: '', + getTemplate: `${apiURL}/user-regions`, + putTemplate: `${apiURL}/user-regions/:item.id`, + postTemplate: `${apiURL}/user-regions`, + deleteTemplate: `${apiURL}/user-regions/:item.id`, + fetchActions: ['AUTH_LOGGED_IN', 'AUTH_UPDATED'], + forceFetchActions: ['USER_REGION_SAVE_FINISHED', 'USER_REGION_DELETE_FINISHED'], + urlParamSelectors: [], + sortBy: 'updated_at', + sortAsc: false, + + addons: { + // Override the save method to return response status + doUserRegionSave: (item) => ({ dispatch, store }) => { + const authToken = store.selectAuthToken(); + + dispatch({ type: 'USER_REGION_SAVE_START' }); + + return fetch(`${apiURL}/user-regions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + authToken, + }, + body: JSON.stringify(item), + }) + .then(async response => { + if (response.ok) { + const data = await response.json(); + dispatch({ + type: 'USER_REGION_SAVE_FINISHED', + payload: data + }); + return { success: true, data }; + } else { + const errorText = await response.text(); + dispatch({ + type: 'USER_REGION_SAVE_ERROR', + payload: { status: response.status, message: errorText } + }); + return { success: false, status: response.status, message: errorText }; + } + }) + .catch(error => { + dispatch({ + type: 'USER_REGION_SAVE_ERROR', + payload: error + }); + return { success: false, error }; + }); + }, + + // Update existing region using PUT + doUserRegionUpdate: (item) => ({ dispatch, store }) => { + const authToken = store.selectAuthToken(); + + dispatch({ type: 'USER_REGION_UPDATE_START' }); + + return fetch(`${apiURL}/user-regions/${item.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + authToken, + }, + body: JSON.stringify(item), + }) + .then(async response => { + if (response.ok) { + const data = await response.json(); + dispatch({ + type: 'USER_REGION_SAVE_FINISHED', + payload: data + }); + return { success: true, data }; + } else { + const errorText = await response.text(); + dispatch({ + type: 'USER_REGION_UPDATE_ERROR', + payload: { status: response.status, message: errorText } + }); + return { success: false, status: response.status, message: errorText }; + } + }) + .catch(error => { + dispatch({ + type: 'USER_REGION_UPDATE_ERROR', + payload: error + }); + return { success: false, error }; + }); + }, + + // Select public regions + selectUserRegionPublicItems: (state) => { + return state.userRegion.publicRegions || []; + }, + + // Fetch public regions + doUserRegionFetchPublic: () => ({ dispatch, store }) => { + const authToken = store.selectAuthToken(); + + dispatch({ type: 'USER_REGION_FETCH_PUBLIC_START' }); + + fetch(`${apiURL}/user-regions/public`, { + headers: { + 'Authorization': 'Bearer ' + authToken, + }, + }) + .then(response => response.json()) + .then(data => { + dispatch({ + type: 'USER_REGION_FETCH_PUBLIC_FINISHED', + payload: data + }); + }) + .catch(error => { + dispatch({ + type: 'USER_REGION_FETCH_PUBLIC_ERROR', + payload: error + }); + }); + }, + + // Search regions + doUserRegionSearch: (query) => ({ dispatch, store }) => { + const authToken = store.selectAuthToken(); + + dispatch({ type: 'USER_REGION_SEARCH_START' }); + + fetch(`${apiURL}/user-regions/search?q=${encodeURIComponent(query)}`, { + headers: { + 'Authorization': 'Bearer ' + authToken, + }, + }) + .then(response => response.json()) + .then(data => { + dispatch({ + type: 'USER_REGION_SEARCH_FINISHED', + payload: data + }); + }) + .catch(error => { + dispatch({ + type: 'USER_REGION_SEARCH_ERROR', + payload: error + }); + }); + }, + + // Combined selector for all available regions (user's + public) + selectUserRegionAllAvailable: createSelector( + 'selectUserRegionItems', + 'selectUserRegionPublicItems', + (userRegions, publicRegions) => { + // Combine and deduplicate + const combined = [...userRegions]; + publicRegions.forEach(pr => { + if (!userRegions.find(ur => ur.id === pr.id)) { + combined.push({ ...pr, isPublic: true }); + } + }); + return combined; + } + ), + }, + + reduceFurther: (state, { type, payload }) => { + switch (type) { + case 'USER_REGION_FETCH_PUBLIC_START': + return { ...state, fetchingPublic: true }; + case 'USER_REGION_FETCH_PUBLIC_FINISHED': + return { ...state, fetchingPublic: false, publicRegions: payload }; + case 'USER_REGION_FETCH_PUBLIC_ERROR': + return { ...state, fetchingPublic: false, publicError: payload }; + case 'USER_REGION_SEARCH_START': + return { ...state, searching: true }; + case 'USER_REGION_SEARCH_FINISHED': + return { ...state, searching: false, searchResults: payload }; + case 'USER_REGION_SEARCH_ERROR': + return { ...state, searching: false, searchError: payload }; + default: + return state; + } + }, +}); \ No newline at end of file diff --git a/src/app-components/Modal.js b/src/app-components/Modal.js index 638d3e6..0a43788 100644 --- a/src/app-components/Modal.js +++ b/src/app-components/Modal.js @@ -34,7 +34,7 @@ export default connect(
diff --git a/src/app-components/ProfileMenu.js b/src/app-components/ProfileMenu.js index c2b571c..0f3edb9 100644 --- a/src/app-components/ProfileMenu.js +++ b/src/app-components/ProfileMenu.js @@ -17,27 +17,46 @@ const ProfileMenu = connect( doAuthLogout, }) => { return !isLoggedIn ? ( - + | + + + + ) : (
diff --git a/src/app-components/cumulus-map/cumulus-map-base.js b/src/app-components/cumulus-map/cumulus-map-base.js new file mode 100644 index 0000000..9260c96 --- /dev/null +++ b/src/app-components/cumulus-map/cumulus-map-base.js @@ -0,0 +1,76 @@ +import { useEffect } from 'react'; +import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet'; +import { useMap } from 'react-leaflet/hooks'; + +// Component to handle map invalidation on resize +function MapInvalidator({ mapHeight }) { + const map = useMap(); + + useEffect(() => { + if (map && mapHeight) { + // Small delay to let DOM update + const timer = setTimeout(() => { + map.invalidateSize(); + }, 100); + + return () => clearTimeout(timer); + } + }, [map, mapHeight]); + + return null; +} + +/** + * Base Cumulus Map Container + * + * A reusable Leaflet map component that provides the core map functionality + * for all map modes in the application. + * + * @param {Object} props + * @param {React.ReactNode} props.children - Mode-specific overlays and controls + * @param {number} [props.mapHeight] - Height of the map for resizing + * @param {Array} [props.center=[37.1, -95.7]] - Map center coordinates [lat, lng] + * @param {number} [props.zoom=4] - Initial zoom level + * @param {Array} [props.locations=[]] - Optional markers to display + */ +export default function CumulusMapBase({ + children, + mapHeight, + center = [37.1, -95.7], + zoom = 4, + locations = [] +}) { + return ( +
+ + + + {/* Handle map invalidation when height changes */} + + + {/* Mode-specific overlays and controls */} + {children} + + {/* Optional location markers */} + {locations.map((loc, i) => ( + + +

{loc.name}

+

{loc.description}

+
+
+ ))} +
+
+ ); +} diff --git a/src/app-components/cumulus-map/cumulus-map.js b/src/app-components/cumulus-map/cumulus-map.js new file mode 100644 index 0000000..cf403e6 --- /dev/null +++ b/src/app-components/cumulus-map/cumulus-map.js @@ -0,0 +1,114 @@ +import { useRef, forwardRef } from 'react'; +import CumulusMapBase from './cumulus-map-base'; +import RegionDefineOverlay from './modes/region-define/region-define-overlay'; +import UsaLabelOverlay from './modes/usa-label/usa-label-overlay'; +import RegionDefineControls from './modes/region-define/region-define-controls'; +import UsaLabelControls from './modes/usa-label/usa-label-controls'; + +/** + * Stable wrapper component for rendering mode-specific overlays + * This component type never changes, only the internal component does + */ +const OverlayRenderer = forwardRef(({ mode, modeConfig, ...props }, ref) => { + const config = modeConfig[mode] || modeConfig['region-define']; + const Component = config.overlay; + return ; +}); +OverlayRenderer.displayName = 'OverlayRenderer'; + +/** + * Stable wrapper component for rendering mode-specific controls + * This component type never changes, only the internal component does + */ +const ControlsRenderer = ({ mode, modeConfig, children, ...props }) => { + const config = modeConfig[mode] || modeConfig['region-define']; + const Component = config.controls; + return {children}; +}; + +/** + * Cumulus Map - Main Map Controller + * + * A flexible map component that supports multiple operational modes. + * Modes can be switched by passing the `mode` prop. + * + * The base map persists across mode changes - only overlays and controls swap out. + * This prevents the map from reloading tiles when switching modes. + * + * Available modes: + * - 'region-define': Interactive region drawing and management + * - 'usa-label': Simple USA label display + * + * To add a new mode: + * 1. Create overlay component in modes//-overlay.js + * 2. Create controls component in modes//-controls.js + * 3. Import them and add to modeConfig below + * + * @param {Object} props + * @param {string} [props.mode='region-define'] - The map mode to display + * @param {number} [props.mapHeight] - Height of the map + * + * @example + * // Region define mode + * + * + * @example + * // USA label mode + * + */ +export default function CumulusMap({ + mode = 'region-define', + mapHeight, + ...props +}) { + const mapOverlayRef = useRef(null); + + // Map mode configurations - add new modes here + const modeConfig = { + 'region-define': { + overlay: RegionDefineOverlay, + controls: RegionDefineControls, + }, + 'usa-label': { + overlay: UsaLabelOverlay, + controls: UsaLabelControls, + }, + }; + + return ( +
+
+ {/* Persistent base map */} + + {/* Mode-specific overlay via stable wrapper */} + + + + {/* Mode-specific map overlay controls via stable wrapper */} + + {({ mapOverlay }) => mapOverlay} + +
+ + {/* Render footer controls below the map via stable wrapper */} + + {({ footer }) => footer} + +
+ ); +} diff --git a/src/app-components/cumulus-map/index.js b/src/app-components/cumulus-map/index.js new file mode 100644 index 0000000..0b8bb56 --- /dev/null +++ b/src/app-components/cumulus-map/index.js @@ -0,0 +1,16 @@ +/** + * Cumulus Map - Main Export + * + * This index file provides a clean way to import the map component: + * import CumulusMap from 'app-components/cumulus-map'; + */ +export { default } from './cumulus-map'; +export { default as CumulusMapBase } from './cumulus-map-base'; + +// Region Define Mode exports +export { default as RegionDefineOverlay } from './modes/region-define/region-define-overlay'; +export { default as RegionDefineControls } from './modes/region-define/region-define-controls'; + +// USA Label Mode exports +export { default as UsaLabelOverlay } from './modes/usa-label/usa-label-overlay'; +export { default as UsaLabelControls } from './modes/usa-label/usa-label-controls'; diff --git a/src/app-components/cumulus-map/modes/region-define/region-define-controls.js b/src/app-components/cumulus-map/modes/region-define/region-define-controls.js new file mode 100644 index 0000000..761b3dc --- /dev/null +++ b/src/app-components/cumulus-map/modes/region-define/region-define-controls.js @@ -0,0 +1,403 @@ +import { useState, useEffect, useCallback } from 'react'; +import { connect } from 'redux-bundler-react'; + +export default connect( + 'doUserRegionSave', + 'doUserRegionUpdate', + 'doUserRegionFetch', + 'doUserRegionDelete', + 'doUserRegionFetchPublic', + 'selectUserRegionAllAvailable', + function RegionDefineControls({ + children, + mapOverlayRef, + doUserRegionSave, + doUserRegionUpdate, + doUserRegionFetch, + doUserRegionDelete, + doUserRegionFetchPublic, + userRegionAllAvailable + }) { + // Unified region state + const [currentRegion, setCurrentRegion] = useState({ + source: null, // 'drawn' | 'api' | 'uploaded' + data: null, // GeoJSON FeatureCollection + name: '', // Region name + id: null, // ID if from API + isEditable: false // Can it be modified? + }); + + const [isSaving, setIsSaving] = useState(false); + const [saveStatus, setSaveStatus] = useState(null); // 'success' | 'error' | null + + // Clear save status after a delay + useEffect(() => { + if (saveStatus) { + const timer = setTimeout(() => setSaveStatus(null), 3000); + return () => clearTimeout(timer); + } + }, [saveStatus]); + + // Fetch regions on mount + useEffect(() => { + doUserRegionFetch(); + doUserRegionFetchPublic(); + }, [doUserRegionFetch, doUserRegionFetchPublic]); + + // Helper to create proper FeatureCollection + const createFeatureCollection = (geometry, properties = {}) => { + return { + type: 'FeatureCollection', + features: [{ + type: 'Feature', + geometry: geometry, + properties: properties + }] + }; + }; + + // Centralized action handler for all map interactions + const handleMapAction = useCallback((action) => { + + switch (action.type) { + case 'START_DRAW': + // Always clear and start fresh when starting to draw + if (mapOverlayRef.current) { + mapOverlayRef.current.clearAll(); + mapOverlayRef.current.startDrawing(); + } + setCurrentRegion({ + source: 'drawn', + data: null, + name: '', + id: null, + isEditable: true + }); + break; + + case 'DRAWN': + setCurrentRegion({ + source: 'drawn', + data: action.data, + name: '', + id: null, + isEditable: true + }); + break; + + case 'START_EDIT': + setCurrentRegion(prev => ({ + ...prev, + source: 'drawn', + isEditable: true + })); + break; + + case 'UPDATE': + setCurrentRegion(prev => ({ + ...prev, + data: action.data, + source: 'drawn', + isEditable: true + })); + break; + + case 'CANCEL_EDIT': + if (currentRegion.id) { + setCurrentRegion(prev => ({ + ...prev, + source: 'api', + isEditable: false + })); + } + break; + + case 'SELECT_API': + const region = userRegionAllAvailable.find(r => r.id === action.id); + if (region) { + const featureCollection = createFeatureCollection(region.geojson, { + name: region.name, + id: region.id + }); + setCurrentRegion({ + source: 'api', + data: featureCollection, + name: region.name, + id: region.id, + isEditable: false + }); + if (mapOverlayRef.current) { + mapOverlayRef.current.displayRegion(featureCollection); + } + } + break; + + case 'UPLOADED': + const uploadedFeatureCollection = action.data.type === 'FeatureCollection' + ? action.data + : createFeatureCollection(action.data.geometry || action.data); + + setCurrentRegion({ + source: 'uploaded', + data: uploadedFeatureCollection, + name: action.name || 'Uploaded Region', + id: null, + isEditable: true + }); + break; + + case 'DELETE': + setCurrentRegion({ + source: null, + data: null, + name: '', + id: null, + isEditable: false + }); + if (mapOverlayRef.current) { + mapOverlayRef.current.clearAll(); + } + break; + + case 'UPDATE_NAME': + setCurrentRegion(prev => ({ ...prev, name: action.name })); + break; + + case 'CLEAR_SELECTION': + setCurrentRegion({ + source: null, + data: null, + name: '', + id: null, + isEditable: false + }); + if (mapOverlayRef.current) { + mapOverlayRef.current.clearAll(); + } + break; + default: + break; + } + }, [userRegionAllAvailable, currentRegion.id, mapOverlayRef]); + + const handleSaveRegion = async () => { + if (!currentRegion.data || !currentRegion.name.trim()) { + setSaveStatus('error'); + return; + } + + const geometry = currentRegion.data.features[0]?.geometry; + if (!geometry) return; + + setIsSaving(true); + setSaveStatus(null); + + try { + const isUpdate = currentRegion.id !== null && currentRegion.id !== undefined; + const payload = { + name: currentRegion.name.trim(), + geojson: geometry, + is_public: false + }; + + if (isUpdate) { + payload.id = currentRegion.id; + } + + const result = isUpdate + ? await doUserRegionUpdate(payload) + : await doUserRegionSave(payload); + + if (result.success) { + const savedData = result.data; + + setCurrentRegion({ + source: 'api', + data: currentRegion.data, + name: savedData.name, + id: savedData.id, + isEditable: false + }); + setSaveStatus('success'); + + if (mapOverlayRef.current && currentRegion.data) { + mapOverlayRef.current.displayRegion(currentRegion.data); + } + + await doUserRegionFetch(); + await doUserRegionFetchPublic(); + } else { + setSaveStatus('error'); + } + } catch (error) { + setSaveStatus('error'); + } finally { + setIsSaving(false); + } + }; + + // Setup the onAction callback for the overlay + useEffect(() => { + if (mapOverlayRef.current && mapOverlayRef.current.setOnAction) { + mapOverlayRef.current.setOnAction(handleMapAction); + } + }, [mapOverlayRef, handleMapAction]); + + // Render using render prop pattern + return children({ + mapOverlay: ( +
+
+
+
+
+ Drawn Region (Web Mercator - ESPG 3857) +
+
+
+ Reprojected Region (CONUS Albers - ESPG5070) +
+
+
+
+ ), + footer: ( +
+
+
+
+ {userRegionAllAvailable && userRegionAllAvailable.length > 0 && ( + + )} + + {currentRegion.source === 'api' && currentRegion.id && !userRegionAllAvailable.find(r => r.id === currentRegion.id)?.isPublic && ( + + )} + + {currentRegion.data && (currentRegion.source === 'drawn' || currentRegion.source === 'uploaded') && ( + <> + handleMapAction({ type: 'UPDATE_NAME', name: e.target.value })} + placeholder='Enter region name' + className={`text-sm px-2 py-1 border rounded-md focus:ring-indigo-500 focus:border-indigo-500 ${ + saveStatus === 'error' && !currentRegion.name.trim() + ? 'border-red-500' + : 'border-gray-300' + }`} + /> + + + )} +
+ +
+ {saveStatus === 'success' ? ( +
+ + + + Region saved successfully! +
+ ) : saveStatus === 'error' ? ( +
+ + + + + {!currentRegion.name.trim() ? 'Please enter a region name' : 'Failed to save region'} + +
+ ) : ( +
+ {currentRegion.source === 'api' + ? `Selected: ${currentRegion.name}` + : currentRegion.source === 'drawn' + ? 'Draw region on map' + : currentRegion.source === 'uploaded' + ? 'Uploaded region' + : 'Draw or select a region' + } +
+ )} +
+
+
+
+ ) + }); +}); diff --git a/src/app-components/cumulus-map/modes/region-define/region-define-overlay.js b/src/app-components/cumulus-map/modes/region-define/region-define-overlay.js new file mode 100644 index 0000000..740cca9 --- /dev/null +++ b/src/app-components/cumulus-map/modes/region-define/region-define-overlay.js @@ -0,0 +1,580 @@ +import { useEffect, useRef, useCallback, forwardRef, useImperativeHandle } from 'react'; +import { useMap } from 'react-leaflet/hooks'; +import * as Leaflet from 'leaflet'; +import { + TerraDraw, + TerraDrawRectangleMode, + TerraDrawRenderMode, + TerraDrawSelectMode, +} from 'terra-draw'; +import { TerraDrawLeafletAdapter } from 'terra-draw-leaflet-adapter'; +import shp from 'shpjs'; +import * as turf from '@turf/turf'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { TrashIcon, ArrowUpTrayIcon, PencilSquareIcon } from '@heroicons/react/24/outline'; + +// Import EPSG:5070 conversion function from shared utilities +import { convertTo5070Rectangle } from '../../shared/epsg5070-utils'; + +// Configuration +const ENABLE_EPSG5070_CONVERSION = true; + +// Convert React icons to HTML strings for Leaflet.easyButton +const trashIcon = renderToStaticMarkup(); +const uploadIcon = renderToStaticMarkup(); +const editIcon = renderToStaticMarkup(); + +// Style constants for consistency +const STYLES = { + original: { + color: '#3b82f6', + weight: 2, + fillColor: '#3b82f6', + fillOpacity: 0.1, + }, + epsg5070: { + color: '#10b981', + weight: 3, + fillColor: '#10b981', + fillOpacity: 0.1, + dashArray: '10, 5', + }, + simplified: { + color: '#3b82f6', + weight: 2, + fillOpacity: 0.0, + dashArray: '5, 5', + }, + bbox: { + color: '#3b82f6', + weight: 3, + fillOpacity: 0.0, + } +}; + +// Helper to normalize feature IDs +const normalizeId = (id) => typeof id === 'string' ? id : id?.toString(); + +const RegionDefineOverlay = forwardRef(function ({ onAction }, ref) { + const draw = useRef(null); + const featureId = useRef(null); + const layerGroup = useRef(null); + const transformedLayer = useRef(null); // Layer for EPSG:5070 transformed shapes + const fileInput = useRef(null); + const map = useMap(); + + // Track whether we're editing an existing region vs creating a new one + const isEditingExisting = useRef(false); + + // Persistent storage for TerraDraw state across hot reloads + const persistentState = useRef({ + features: [], + mode: 'render' + }); + + // Helper function to display EPSG:5070 transformed shape + const displayEPSG5070Shape = useCallback((geometry) => { + if (!ENABLE_EPSG5070_CONVERSION || !geometry) return null; + + // Remove any existing transformed layer + if (transformedLayer.current) { + map.removeLayer(transformedLayer.current); + transformedLayer.current = null; + } + + // Convert to EPSG:5070 aligned rectangle + const aligned5070Geometry = convertTo5070Rectangle(geometry); + + // Add the transformed shape as a Leaflet layer + transformedLayer.current = Leaflet.geoJSON({ + type: 'Feature', + geometry: aligned5070Geometry, + properties: { + epsg5070_bounds: aligned5070Geometry.epsg5070_bounds, + is_epsg5070_transformed: true + } + }, { + style: STYLES.epsg5070, + interactive: false // Make it non-interactive so clicks pass through + }).addTo(map); + + return aligned5070Geometry; + }, [map]); + + const clearAll = useCallback(() => { + if (!draw.current) return; + + draw.current.clear(); + if (layerGroup.current) { + map.removeLayer(layerGroup.current); + layerGroup.current = null; + } + if (transformedLayer.current) { + map.removeLayer(transformedLayer.current); + transformedLayer.current = null; + } + featureId.current = null; + isEditingExisting.current = false; // Reset flag when clearing + + // Clear persistent state + persistentState.current = { + features: [], + mode: 'render' + }; + }, [map]); + + const startDrawing = useCallback(() => { + if (!draw.current) return; + clearAll(); + isEditingExisting.current = false; // Mark as creating new region + draw.current.setMode('rectangle'); + }, [clearAll]); + + const displayRegion = useCallback((regionData) => { + if (!draw.current) return; + + draw.current.setMode('render'); + clearAll(); + + if (!regionData?.features?.[0]) return; + + const feature = regionData.features[0]; + + // Transform feature to match TerraDraw's expected format + const terraDrawFeature = { + type: 'Feature', + id: feature.properties?.id || feature.id, // Use id from properties or top-level + geometry: feature.geometry, + properties: { + ...feature.properties, + mode: 'rectangle' // TerraDraw requires mode property + } + }; + + // Mark as editing an existing region + isEditingExisting.current = true; + + // Add to TerraDraw + const ids = draw.current.addFeatures([terraDrawFeature]); + + if (ids?.[0]) { + featureId.current = normalizeId(ids[0]); + } + + // Display the EPSG:5070 transformed shape + displayEPSG5070Shape(feature.geometry); + + // Update persistent state + persistentState.current = { + features: draw.current.getSnapshot(), + mode: 'render' + }; + }, [clearAll, displayEPSG5070Shape]); + + // Ref to store onAction callback + const onActionCallback = useRef(onAction); + + // Allow external setting of onAction + const setOnAction = useCallback((callback) => { + onActionCallback.current = callback; + }, []); + + // Use the callback ref instead of direct onAction prop + useEffect(() => { + onActionCallback.current = onAction; + }, [onAction]); + + useImperativeHandle( + ref, + () => { + return { + disableEditing, + displayRegion, + clearAll, + startDrawing, + setOnAction + }; + }, + [clearAll, displayRegion, startDrawing, setOnAction] + ); + + const disableEditing = () => { + if (draw.current) { + draw.current.setMode('render'); + } + }; + + const onChange = useCallback(() => { + const currentMode = draw.current.getMode(); + + // Handle changes in both rectangle (new) and select (edit) modes + if (currentMode !== 'rectangle' && currentMode !== 'select') return; + + const snapshot = draw.current.getSnapshot()[0]; + if (!snapshot) return; + + featureId.current = normalizeId(snapshot.id); + + // Update persistent state + persistentState.current = { + features: draw.current.getSnapshot(), + mode: currentMode + }; + + // Show EPSG:5070 preview while drawing/editing + displayEPSG5070Shape(snapshot.geometry); + + // Emit appropriate action based on whether we're editing or creating + const actionType = isEditingExisting.current ? 'UPDATE' : 'DRAWN'; + + onActionCallback.current?.({ + type: actionType, + data: { type: 'FeatureCollection', features: [snapshot] } + }); + }, [displayEPSG5070Shape]); + + const onCreate = useCallback((id) => { + const allFeatures = draw.current.getSnapshot(); + + // Keep only the newest feature if multiple exist + if (allFeatures.length > 1) { + draw.current.clear(); + const newestFeature = allFeatures.find(f => f.id === id); + if (newestFeature) { + const newIds = draw.current.addFeatures([newestFeature]); + id = newIds?.[0] ? normalizeId(newIds[0]) : id; + } + } + + const snapshot = draw.current.getSnapshot()[0]; + if (!snapshot?.geometry) return; + + // Display and store EPSG:5070 transformation + const aligned5070Geometry = displayEPSG5070Shape(snapshot.geometry); + if (aligned5070Geometry) { + snapshot.properties = { + ...snapshot.properties, + original_geometry: snapshot.geometry, + epsg5070_geometry: aligned5070Geometry, + epsg5070_bounds: aligned5070Geometry.epsg5070_bounds + }; + } + + draw.current.setMode('render'); + featureId.current = normalizeId(id); + + // Update persistent state + persistentState.current = { + features: draw.current.getSnapshot(), + mode: 'render' + }; + + // Emit appropriate action based on whether we're editing or creating + const actionType = isEditingExisting.current ? 'UPDATE' : 'DRAWN'; + + onActionCallback.current?.({ + type: actionType, + data: { type: 'FeatureCollection', features: [snapshot] } + }); + }, [displayEPSG5070Shape]); + + const onEdit = useCallback(() => { + if (onActionCallback.current) { + isEditingExisting.current = false; // Reset flag when starting a new draw + onActionCallback.current({ type: 'START_DRAW' }); + } + }, []); + + const onDelete = useCallback(() => { + if (onActionCallback.current) { + onActionCallback.current({ type: 'DELETE' }); + } + }, []); + + // Store callback refs to avoid re-initializing TerraDraw when they change + const onChangeRef = useRef(onChange); + const onCreateRef = useRef(onCreate); + const onEditRef = useRef(onEdit); + const onDeleteRef = useRef(onDelete); + + // Update refs when callbacks change + useEffect(() => { + onChangeRef.current = onChange; + onCreateRef.current = onCreate; + onEditRef.current = onEdit; + onDeleteRef.current = onDelete; + }, [onChange, onCreate, onEdit, onDelete]); + + // Initialize TerraDraw only once on mount + useEffect(() => { + // Clean up any existing layers on the map before initialization + // This ensures a clean slate on page refresh + if (transformedLayer.current) { + map.removeLayer(transformedLayer.current); + transformedLayer.current = null; + } + if (layerGroup.current) { + map.removeLayer(layerGroup.current); + layerGroup.current = null; + } + + // Use persistent state (which survives hot reloads via ref) + const { features: preservedFeatures } = persistentState.current; + const preservedFeatureId = featureId.current; + + const terraDraw = new TerraDraw({ + adapter: new TerraDrawLeafletAdapter({ + lib: Leaflet, + map, + }), + modes: [ + new TerraDrawRenderMode({ + modeName: 'render', + cursors: { + hover: 'grab', + }, + }), + new TerraDrawRectangleMode({ + cursors: { + start: 'crosshair', + }, + }), + new TerraDrawSelectMode({ + flags: { + rectangle: { + feature: { + draggable: true, + coordinates: { + resizable: 'opposite', + }, + }, + }, + }, + }), + ], + }); + + terraDraw.start(); + + // Set draw.current immediately so it's available for all functions + draw.current = terraDraw; + + // Restore preserved features if they exist (for hot reload support) + if (preservedFeatures && preservedFeatures.length > 0) { + terraDraw.addFeatures(preservedFeatures); + terraDraw.setMode("render"); + + // Re-display the EPSG:5070 shape (creates a new layer) + displayEPSG5070Shape(preservedFeatures[0].geometry); + + // Restore the feature ID + featureId.current = preservedFeatureId; + + } else { + terraDraw.setMode('render'); // Start in render mode, parent will control when to draw + } + + // Use refs for callbacks to avoid re-initializing when they change + terraDraw.on('change', (...args) => onChangeRef.current(...args)); + terraDraw.on('finish', (...args) => onCreateRef.current(...args)); + + // Add double-click handler to enable dragging + const dblClickHandler = (e) => { + // Check if we have a feature and are in render mode + if (draw.current && draw.current.getMode() === 'render') { + const features = draw.current.getSnapshot(); + if (features && features.length > 0) { + // Get the first feature's ID + const featureToSelect = features[0].id || features[0]; + // Switch to select mode and select the feature + draw.current.setMode('select'); + draw.current.selectFeature(featureToSelect); + + // Notify parent that editing has started on existing region + if (onActionCallback.current) { + onActionCallback.current({ type: 'START_EDIT' }); + } + + // Prevent map zoom on double-click + e.originalEvent.preventDefault(); + e.originalEvent.stopPropagation(); + } + } + }; + map.on('dblclick', dblClickHandler); + + // Add Escape key handler to exit select mode + const keydownHandler = (e) => { + if (e.key === 'Escape' && draw.current && draw.current.getMode() === 'select') { + draw.current.setMode('render'); + + // Notify parent that editing was cancelled + if (onActionCallback.current) { + onActionCallback.current({ type: 'CANCEL_EDIT' }); + } + } + }; + map.getContainer().addEventListener('keydown', keydownHandler); + + // Create buttons and store references for cleanup, using refs for callbacks + const editButton = Leaflet.easyButton({ + states: [{ + stateName: 'edit', + icon: editIcon, + title: 'Draw new region', + onClick: () => onEditRef.current() + }] + }).addTo(map); + + const deleteButton = Leaflet.easyButton({ + states: [{ + stateName: 'delete', + icon: trashIcon, + title: 'Delete region', + onClick: () => onDeleteRef.current() + }] + }).addTo(map); + + const uploadButton = Leaflet.easyButton({ + states: [{ + stateName: 'upload', + icon: uploadIcon, + title: 'Upload shapefile zip', + onClick: () => fileInput.current?.click() + }] + }).addTo(map); + + // Cleanup function + return () => { + // Remove buttons from map + if (editButton) map.removeControl(editButton); + if (deleteButton) map.removeControl(deleteButton); + if (uploadButton) map.removeControl(uploadButton); + + // Remove event listeners + map.off('dblclick', dblClickHandler); + map.getContainer().removeEventListener('keydown', keydownHandler); + + // Clean up all layers + if (layerGroup.current) { + map.removeLayer(layerGroup.current); + layerGroup.current = null; + } + if (transformedLayer.current) { + map.removeLayer(transformedLayer.current); + transformedLayer.current = null; + } + + // Stop and clean up TerraDraw + if (terraDraw) { + terraDraw.stop(); + } + }; + }, [map, displayEPSG5070Shape]); // Include displayEPSG5070Shape to fix ESLint warning + + + const onFileUpload = async (e) => { + const file = e.target.files[0]; + if (!file) return; + + try { + const data = await file.arrayBuffer(); + const geojson = await shp(data); + const simplified = turf.convex(geojson); + const bboxRectangle = turf.bboxPolygon(turf.bbox(geojson)); + + // Set to render mode before clearing + draw.current.setMode('render'); + clearAll(); + + // Create layer group with all visualizations + const group = new Leaflet.LayerGroup(); + group.addLayer(Leaflet.geoJSON(geojson, { style: STYLES.original })); + group.addLayer(Leaflet.geoJSON(simplified, { style: STYLES.simplified })); + // group.addLayer(Leaflet.geoJSON(bboxRectangle, { style: STYLES.bbox })); + group.addTo(map); + layerGroup.current = group; + + // Extract bbox bounds and round to 8 decimal places (TerraDraw rejects excessive precision) + const roundCoord = (num) => Math.round(num * 100000000) / 100000000; + const minLon = roundCoord(bboxRectangle.bbox[0]); + const minLat = roundCoord(bboxRectangle.bbox[1]); + const maxLon = roundCoord(bboxRectangle.bbox[2]); + const maxLat = roundCoord(bboxRectangle.bbox[3]); + + // TerraDraw rectangle expects coordinates in this order: + // top-left → bottom-left → bottom-right → top-right → close (top-left) + const rectangleCoords = [ + [ + [minLon, maxLat], // top-left + [minLon, minLat], // bottom-left + [maxLon, minLat], // bottom-right + [maxLon, maxLat], // top-right + [minLon, maxLat] // close (top-left) + ] + ]; + + const geometry = { + type: 'Polygon', + coordinates: rectangleCoords + } + + // Transform bbox rectangle to match TerraDraw's expected format + // Let TerraDraw generate the ID + const terraDrawFeature = { + type: 'Feature', + geometry: geometry, + properties: { + mode: 'rectangle' + } + }; + + // Add to TerraDraw + try { + const ids = draw.current.addFeatures([terraDrawFeature]); + + if (ids?.[0]) { + featureId.current = normalizeId(ids[0]); + } + + // Mark as editing existing (uploaded) region + isEditingExisting.current = true; + } catch (error) { + console.error('Error adding feature to TerraDraw:', error); + } + + // Update persistent state + persistentState.current = { + features: draw.current.getSnapshot(), + mode: 'render' + }; + + // Display EPSG:5070 transformation + displayEPSG5070Shape(bboxRectangle.geometry); + + // Emit the simplified shape + onActionCallback.current?.({ + type: 'UPLOADED', + data: geometry, + name: file.name.replace(/\.[^/.]+$/, '') + }); + } catch (error) { + console.error('Error uploading file:', error); + } + + e.target.value = ''; // Reset for re-selection + }; + + return ( +
+ +
+ ); +}); + +export default RegionDefineOverlay; diff --git a/src/app-components/cumulus-map/modes/usa-label/usa-label-controls.js b/src/app-components/cumulus-map/modes/usa-label/usa-label-controls.js new file mode 100644 index 0000000..b5f3701 --- /dev/null +++ b/src/app-components/cumulus-map/modes/usa-label/usa-label-controls.js @@ -0,0 +1,17 @@ +/** + * USA Label Controls + * + * Simple controls component for the USA label mode. + */ +export default function UsaLabelControls({ children }) { + return children({ + mapOverlay: null, + footer: ( +
+
+ USA Label Mode - Displaying center of continental United States +
+
+ ) + }); +} diff --git a/src/app-components/cumulus-map/modes/usa-label/usa-label-overlay.js b/src/app-components/cumulus-map/modes/usa-label/usa-label-overlay.js new file mode 100644 index 0000000..dbf7863 --- /dev/null +++ b/src/app-components/cumulus-map/modes/usa-label/usa-label-overlay.js @@ -0,0 +1,39 @@ +import { useEffect } from 'react'; +import { useMap } from 'react-leaflet/hooks'; +import * as Leaflet from 'leaflet'; + +/** + * USA Label Overlay + * + * Simple overlay that displays "USA" text in the middle of the continental United States. + */ +export default function UsaLabelOverlay() { + const map = useMap(); + + useEffect(() => { + // Continental US center approximately + const usCenter = [39.8283, -98.5795]; + + // Create a custom div icon with "USA" text + const usaIcon = Leaflet.divIcon({ + className: 'usa-label-marker', + html: '
USA
', + iconSize: [100, 50], + iconAnchor: [50, 25] + }); + + // Add marker to map + const marker = Leaflet.marker(usCenter, { + icon: usaIcon, + interactive: false, + keyboard: false + }).addTo(map); + + // Cleanup on unmount + return () => { + map.removeLayer(marker); + }; + }, [map]); + + return null; +} diff --git a/src/app-components/cumulus-map/shared/epsg5070-utils.js b/src/app-components/cumulus-map/shared/epsg5070-utils.js new file mode 100644 index 0000000..8afb0a7 --- /dev/null +++ b/src/app-components/cumulus-map/shared/epsg5070-utils.js @@ -0,0 +1,144 @@ +import proj4 from 'proj4'; + +// Define EPSG:4326 (WGS84) - standard lat/lon +proj4.defs('EPSG:4326', '+proj=longlat +datum=WGS84 +no_defs'); + +// Define EPSG:5070 (NAD83 / Conus Albers) for coordinate transformation +proj4.defs( + 'EPSG:5070', + '+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=23 ' + + '+lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs' +); + +// Helper functions to transform coordinates +export const transform4326to5070 = (lon, lat) => { + return proj4('EPSG:4326', 'EPSG:5070', [lon, lat]); +}; + +export const transform5070to4326 = (x, y) => { + return proj4('EPSG:5070', 'EPSG:4326', [x, y]); +}; + +// Create a rectangle in EPSG:5070 and convert to 4326 +export const create5070Rectangle = (centerLon, centerLat, widthMeters, heightMeters) => { + // Transform center to EPSG:5070 + const [centerX, centerY] = transform4326to5070(centerLon, centerLat); + + // Create rectangle corners in EPSG:5070 (perfect rectangle in meters) + const halfWidth = widthMeters / 2; + const halfHeight = heightMeters / 2; + const corners5070 = [ + [centerX - halfWidth, centerY - halfHeight], // SW + [centerX + halfWidth, centerY - halfHeight], // SE + [centerX + halfWidth, centerY + halfHeight], // NE + [centerX - halfWidth, centerY + halfHeight], // NW + [centerX - halfWidth, centerY - halfHeight], // Close polygon + ]; + + // Transform back to EPSG:4326 for display/storage + const corners4326 = corners5070.map(([x, y]) => { + const [lon, lat] = transform5070to4326(x, y); + return [lon, lat]; + }); + + return { + type: 'Polygon', + coordinates: [corners4326] + }; +}; + +// Helper function to interpolate points along a line +const interpolatePoints = (start, end, numPoints) => { + const points = []; + for (let i = 0; i <= numPoints; i++) { + const t = i / numPoints; + points.push([ + start[0] + (end[0] - start[0]) * t, + start[1] + (end[1] - start[1]) * t + ]); + } + return points; +}; + +// Convert any polygon drawn in 4326 to an aligned rectangle in 5070 +export const convertTo5070Rectangle = (geometry4326, pointsPerSide = 20) => { + + // Get all coordinates from the polygon (excluding the closing point) + let coords = geometry4326.coordinates[0]; + + // Remove the closing point if it exists + if (coords.length > 4 && + coords[0][0] === coords[coords.length - 1][0] && + coords[0][1] === coords[coords.length - 1][1]) { + coords = coords.slice(0, -1); + } + + // Transform all points to EPSG:5070 + const coords5070 = coords.map(([lon, lat]) => { + const result = transform4326to5070(lon, lat); + return result; + }); + + // Find the bounding box in EPSG:5070 (this will be a perfect rectangle) + const xCoords = coords5070.map(c => c[0]).filter(x => !isNaN(x)); + const yCoords = coords5070.map(c => c[1]).filter(y => !isNaN(y)); + + if (xCoords.length === 0 || yCoords.length === 0) { + return geometry4326; // Return original if transformation fails + } + + const minX = Math.min(...xCoords); + const maxX = Math.max(...xCoords); + const minY = Math.min(...yCoords); + const maxY = Math.max(...yCoords); + + // Create a dense set of points along each edge of the rectangle in EPSG:5070 + // This will create the curved appearance when transformed back to 4326 + const rectangle5070Points = []; + + // Bottom edge (SW to SE) + const bottomEdge = interpolatePoints([minX, minY], [maxX, minY], pointsPerSide); + rectangle5070Points.push(...bottomEdge.slice(0, -1)); // Exclude last point to avoid duplication + + // Right edge (SE to NE) + const rightEdge = interpolatePoints([maxX, minY], [maxX, maxY], pointsPerSide); + rectangle5070Points.push(...rightEdge.slice(0, -1)); + + // Top edge (NE to NW) + const topEdge = interpolatePoints([maxX, maxY], [minX, maxY], pointsPerSide); + rectangle5070Points.push(...topEdge.slice(0, -1)); + + // Left edge (NW to SW) + const leftEdge = interpolatePoints([minX, maxY], [minX, minY], pointsPerSide); + rectangle5070Points.push(...leftEdge.slice(0, -1)); + + // Close the polygon by adding the first point at the end + rectangle5070Points.push([minX, minY]); + + // Transform all points back to EPSG:4326 + const rectangle4326 = rectangle5070Points.map(([x, y]) => { + const [lon, lat] = transform5070to4326(x, y); + + + // Validate the result + if (isNaN(lon) || isNaN(lat) || !isFinite(lon) || !isFinite(lat)) { + // Return a fallback valid coordinate + return [0, 0]; + } + + + return [lon, lat]; + }); + + + return { + type: 'Polygon', + coordinates: [rectangle4326], + // Store the original EPSG:5070 bounds for reference + epsg5070_bounds: { + minX, maxX, minY, maxY, + width: maxX - minX, + height: maxY - minY + } + }; +}; \ No newline at end of file diff --git a/src/app-components/modals/availability-modal.js b/src/app-components/modals/availability-modal.js new file mode 100644 index 0000000..5d7fdc9 --- /dev/null +++ b/src/app-components/modals/availability-modal.js @@ -0,0 +1,252 @@ +import { useEffect, useState, useCallback } from 'react'; +import { connect } from 'redux-bundler-react'; +import { Switch } from '@headlessui/react'; +import { format, addDays, subDays } from 'date-fns'; +import { UTCDate } from '@date-fns/utc'; +import { + CheckCircleIcon, + ChevronLeftIcon, + ChevronRightIcon, + XCircleIcon, +} from '@heroicons/react/24/solid'; +import { + CloudIcon, + ExclamationTriangleIcon, + XMarkIcon, +} from '@heroicons/react/24/outline'; + +const CUMULUS_API_URL = process.env.REACT_APP_CUMULUS_API_URL; + +/** + * Display the number of successful, and missing, file uploads in badge components. + * + * @param {Object[]} files - A list of product files. + * @param {bool} files[].is_available - Whether or not the file has been uploaded. + * @returns JSX.Element + */ +const fileBadges = (files) => { + // Generate a list containing the failed (or missing) files. + const failed = files.filter((f) => !f.is_available); + + return ( +
+ {/* We always display the number of successfully uploaded files. */} + + + {files.filter((f) => f.is_available).length} Files + + + {/* If there are missing files, we display those in a separate badge. */} + {failed.length ? ( + + + {failed.length} Failed + + ) : null} +
+ ); +}; + +/** + * Indicate whether or not the file has been successfully uploaded. + * + * @param {Object} file - A product file. + * @param file.is_available - Whether or not the file has been uploaded. + * @returns JSX.Element + */ +const fileStatus = (file) => { + return file.is_available ? ( +

+ Available + +

+ ) : ( +

+ Missing + +

+ ); +}; + +/** + * Display a list of product files. + * + * @param {Object[]} files - A list of product files. + * @param {string} files[].datetime - The time of the file upload. + * @param {bool} files[].is_available - Whether or not the file has been uploaded. + * @param {bool} missingOnly - Flag used to only display files that have not been uploaded. + * @returns JSX.Element + */ +const fileList = (files, missingOnly = false) => { + // Generate a list of files that should be displayed. If `missingOnly` is true, + // or not specified, all files will be displayed. + const filteredFiles = files.filter((f) => !missingOnly || !f.is_available); + + return ( +
    + {filteredFiles.map((file, index) => ( +
  • + {/* Display the start of the time interval in the user's local time zone. */} +

    + {format(new Date(file.datetime), 'MMM dd HH:mm')} + + {/* We also display UTC time. */} + + {format(new UTCDate(file.datetime), 'HH:mm')} + (UTC) + +

    + + {/* Add icon/text with the file availability status. */} + {fileStatus(file)} +
  • + ))} +
+ ); +}; + +export default connect('doModalClose', ({ doModalClose, product, date }) => { + const [currDate, setCurrDate] = useState(null); + const [files, setFiles] = useState([]); + const [showMissing, setShowMissing] = useState(false); + + /** + * Make a request for the file availability data. + * @date {datetime} - The date for the data request. + */ + const fetchData = useCallback( + (date) => { + // Update the current date. + setCurrDate(date); + + // Fetch the availability data and then update the files list. + const safeDate = date instanceof Date ? date : new Date(date); + const url = `${CUMULUS_API_URL}/products/${ + product.id + }/file-availability?date=${safeDate.toISOString()}`; + + fetch(url) + .then((response) => response.json()) + .then(setFiles); + }, + [product.id] + ); + + // Initialize the component. + useEffect(() => { + fetchData(date); + }, [date, fetchData]); + + // Display the modal header. + const header = () => { + return ( +
+
+ {/* Display the product name */} +

+ Product Availability + + ({product.name}) + +

+ + {/* Display the current date. */} +

+ {currDate && ( + {format(new UTCDate(currDate), 'EEEE, MMMM do, yyyy')} + )} +

+ + {/* Add badges with the file counts. */} + {fileBadges(files)} +
+
+ ); + }; + + // Display UI for fetching the previous and next day's data. + const paginationControls = () => { + return ( +
+ {/* Add button for retrieving the previous day's availability. */} + + + {/* Add button for retrieving the next day's availability. */} + +
+ ); + }; + + // Display a toggle button for limiting displayed results to only missing files. + const showMissingToggle = () => { + return ( +
+ + {/* */} + + + Show missing only + + + + {/* */} + + + +
+ ); + }; + + // Display the modal footer. + const footer = () => { + return ( +
+ {/* Add a button to close the modal. */} + +
+ ); + }; + + return ( +
+ {header()} + {paginationControls()} + {showMissingToggle()} + {fileList(files, showMissing)} + {footer()} +
+ ); +}); diff --git a/src/app-pages/admin/downloads/download-table.js b/src/app-pages/admin/downloads/download-table.js index 29a25f7..ad68896 100644 --- a/src/app-pages/admin/downloads/download-table.js +++ b/src/app-pages/admin/downloads/download-table.js @@ -92,7 +92,7 @@ const DownloadTable = connect( return ( item.clip_name || item.watershed_name || 'Custom Region' + }, { key: 'product_id', render: ({ product_id }) => { diff --git a/src/app-pages/downloads/downloads-table/downloads-table-row.js b/src/app-pages/downloads/downloads-table/downloads-table-row.js index 99cfbf7..595f419 100644 --- a/src/app-pages/downloads/downloads-table/downloads-table-row.js +++ b/src/app-pages/downloads/downloads-table/downloads-table-row.js @@ -46,7 +46,7 @@ export default connect(function DownloadsTableRow({ item, productsById }) { return (
- {item.watershed_name} + {item.clip_name || item.watershed_name || 'Custom Region'} diff --git a/src/app-pages/downloads/downloads-table/downloads-table.js b/src/app-pages/downloads/downloads-table/downloads-table.js index 0049223..5f72325 100644 --- a/src/app-pages/downloads/downloads-table/downloads-table.js +++ b/src/app-pages/downloads/downloads-table/downloads-table.js @@ -40,7 +40,7 @@ export default connect( scope='col' className='px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider' > - Watershed + Region { - setFrom(min); - setTo(max); - }, [min, max]); + setFrom(getUnixTime(startOfDay(minDate))); + setTo(getUnixTime(startOfDay(maxDate))); + }, [minDate, maxDate]); useEffect(() => { onChange({ from: fromUnixTime(from), to: fromUnixTime(to) }); @@ -48,56 +59,55 @@ export default function DateRangeSlider({ const barRef = useRef(); const labelRefFrom = useRef(); const labelRefTo = useRef(); + const [barWidth, setBarWidth] = useState( + barRef.current ? barRef.current.offsetWidth : 0 + ); + const [labelWidthFrom, setLabelWidthFrom] = useState( + labelRefFrom.current ? labelRefFrom.current.offsetWidth : 0 + ); + const [labelWidthTo, setLabelWidthTo] = useState( + labelRefTo.current ? labelRefTo.current.offsetWidth : 0 + ); - let barWidth = 0; - if (barRef.current) { - barWidth = barRef.current.offsetWidth; - } + const posPx = (value, min, max, containerPx, subtractPx = 0, addPx = 0) => + ((value - min) / (max - min)) * (containerPx - subtractPx) + addPx; - let labelWidthFrom = 0; - if (labelRefFrom.current) { - labelWidthFrom = labelRefFrom.current.offsetWidth; - } + const [offsetFrom, setOffsetFrom] = useState( + posPx(from, min, max, barWidth, labelWidthFrom) + ); + const [offsetTo, setOffsetTo] = useState( + posPx(to, min, max, barWidth, labelWidthTo) + ); + const [rangeBarFrom, setRangeBarFrom] = useState( + posPx(from, min, max, barWidth, 14, 7) + ); + const [rangeBarTo, setRangeBarTo] = useState( + posPx(to, min, max, barWidth, 14, 7) + ); - let labelWidthTo = 0; - if (labelRefTo.current) { - labelWidthTo = labelRefTo.current.offsetWidth; - } + useEffect(() => { + if (!barRef.current) return; - const offsetFrom = ((from - min) / (max - min)) * (barWidth - labelWidthFrom); - const offsetTo = ((to - min) / (max - min)) * (barWidth - labelWidthTo); + const resizeObserver = new ResizeObserver(() => { + setBarWidth(barRef.current.offsetWidth); + setLabelWidthFrom(labelRefFrom.current.offsetWidth); + setLabelWidthTo(labelRefTo.current.offsetWidth); + }); - const rangeBarFrom = ((from - min) / (max - min)) * (barWidth - 12) + 6; - const rangeBarTo = ((to - min) / (max - min)) * (barWidth - 12) + 6; + resizeObserver.observe(barRef.current); - // handle range drag - const [firstClientX, setfirstClientX] = useState(null); - const handleRangeDrag = (e) => { - if (!firstClientX) { - setfirstClientX(e.clientX); - } else { - // time increment per pixel - const ratio = (max - min) / barWidth; - // change in pixels - const deltaX = e.clientX - firstClientX; - // change in time increment - const deltaT = Math.round(deltaX * ratio); - console.log(duration_to_all_units({ seconds: deltaT })); - // new times - const newFrom = from + deltaT; - const newTo = to + deltaT; - if (newFrom >= min && newTo <= max) { - setFrom(newFrom); - setTo(newTo); - } else { - console.log(Math.round(newFrom) - min, max - Math.round(newTo)); - } - } - }; + // Cleanup observer on component unmount + return () => { + resizeObserver.disconnect(); + }; + }, []); - const handleDragEnd = (e) => { - setfirstClientX(null); - }; + useEffect(() => { + setOffsetFrom(posPx(from, min, max, barWidth, labelWidthFrom)); + setOffsetTo(posPx(to, min, max, barWidth, labelWidthTo)); + setRangeBarFrom(posPx(from, min, max, barWidth, 14, 7)); + setRangeBarTo(posPx(to, min, max, barWidth, 14, 7)); + }, [barWidth, labelWidthFrom, labelWidthTo, min, max, from, to]); return (
@@ -108,16 +118,28 @@ export default function DateRangeSlider({ left: `${rangeBarFrom}px`, right: `${barWidth - rangeBarTo}px`, }} - draggable - onDrag={handleRangeDrag} - onDragEnd={handleDragEnd} >
{ - setFrom(e.target.value); + const raw = Number(e.target.value); + const clamped = Math.min(raw, to); + setFrom(clamped); }} /> { - setTo(e.target.value); + const raw = Number(e.target.value); + const clamped = Math.max(raw, from); + setTo(clamped); }} /> diff --git a/src/app-pages/products/download-modal.js b/src/app-pages/products/download-modal.js index 0460a53..3f3f093 100644 --- a/src/app-pages/products/download-modal.js +++ b/src/app-pages/products/download-modal.js @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { connect } from 'redux-bundler-react'; import DatePicker from 'react-datepicker'; import { @@ -25,6 +25,9 @@ export default connect( 'doModalClose', 'doDownloadRequest', 'doUpdateUrl', + 'doUserRegionFetch', + 'doUserRegionFetchPublic', + 'selectUserRegionAllAvailable', ({ productSelectProducts: products, productSelectSelected: selectedProducts, @@ -36,6 +39,9 @@ export default connect( doModalClose, doDownloadRequest, doUpdateUrl, + doUserRegionFetch, + doUserRegionFetchPublic, + userRegionAllAvailable, }) => { const toggleSelected = (checked, id) => { if (checked) { @@ -57,11 +63,36 @@ export default connect( const [selectedDistrict, setSelectedDistrict] = useState(''); const [selectedWatershed, setSelectedWatershed] = useState(''); const [selectedFormat, setSelectedFormat] = useState('dss7'); + const [useCustomRegion, setUseCustomRegion] = useState(false); + const [customRegion, setCustomRegion] = useState(null); + const [customRegionName, setCustomRegionName] = useState('Custom Region'); + const [selectedSavedRegion, setSelectedSavedRegion] = useState(''); + + // Load regions from API on mount + useEffect(() => { + doUserRegionFetch(); + doUserRegionFetchPublic(); + }, [doUserRegionFetch, doUserRegionFetchPublic]); + + // Auto-select first valid region if available + useEffect(() => { + if (userRegionAllAvailable && userRegionAllAvailable.length > 0 && !selectedSavedRegion) { + const validRegions = userRegionAllAvailable.filter(region => region && region.id && region.name); + if (validRegions.length > 0) { + const firstRegion = validRegions[0]; + setSelectedSavedRegion(firstRegion.id); + setCustomRegion(firstRegion.geojson); + setCustomRegionName(firstRegion.name); + } + } + }, [userRegionAllAvailable, selectedSavedRegion]); let formReady = true; let errMsg = null; if (formReady && selectedProducts.length < 1) formReady = false; - if (formReady && !selectedWatershed) formReady = false; + // Either watershed or custom region must be selected + if (formReady && !useCustomRegion && !selectedWatershed) formReady = false; + if (formReady && useCustomRegion && (!customRegion || selectedSavedRegion === '')) formReady = false; if (formReady && differenceInHours(end, start) > 4380) { formReady = false; errMsg = 'Time window too large. Limit is 6 months max (4380 hours).'; @@ -70,14 +101,26 @@ export default connect( const handleFormSubmit = () => { if (formReady) { + const requestData = { + datetime_start: start, + datetime_end: end, + product_id: selectedProducts, + format: selectedFormat, + }; + + if (useCustomRegion && customRegion) { + // Use custom region for download + requestData.clip_geojson = customRegion; + requestData.clip_region_name = customRegionName; + } else { + // Use watershed for download + requestData.watershed_id = selectedWatershed; + } + + console.log('Submitting download request:', requestData); + doDownloadRequest( - { - datetime_start: start, - datetime_end: end, - watershed_id: selectedWatershed, - product_id: selectedProducts, - format: selectedFormat, - }, + requestData, (err) => { if (err) { console.log('need to put this in the UI to tell the user', err); @@ -113,79 +156,169 @@ export default connect(
- Watershed + Download Area +
+
+ setUseCustomRegion(false)} + className='focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300' + /> + +
+
+ setUseCustomRegion(true)} + disabled={!userRegionAllAvailable || userRegionAllAvailable.length === 0} + className='focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300' + /> + +
+
-
-
- - -
-
- - { + setSelectedDistrict(e.target.value); + setSelectedWatershed(''); + }} + autoComplete='district-name' + className='mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm' + > + {' '} + + {districts.map((district) => { return ( - ); })} - + +
+
+ + +
-
+ ) : ( +
+
+ + +
+ + {selectedSavedRegion !== '' && ( +
+ + setCustomRegionName(e.target.value)} + className='mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md' + placeholder='Enter a name for this download' + /> +
+ )} + +
+ To define new regions, go to the Products page and click "Define Custom Download Region". +
+
+ )}
diff --git a/src/app-pages/products/product-details/index.js b/src/app-pages/products/product-details/index.js index 252257f..c587bc1 100644 --- a/src/app-pages/products/product-details/index.js +++ b/src/app-pages/products/product-details/index.js @@ -106,7 +106,7 @@ export default connect(
- +
diff --git a/src/app-pages/products/product-details/product-availability.js b/src/app-pages/products/product-details/product-availability.js index 7fa53a9..4451e77 100644 --- a/src/app-pages/products/product-details/product-availability.js +++ b/src/app-pages/products/product-details/product-availability.js @@ -1,13 +1,27 @@ -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { connect } from 'redux-bundler-react'; import HeatMap from './water-year-heatmap'; import ButtonGroup from '../../../app-components/button-group/button-group'; import ButtonGroupButton from '../../../app-components/button-group/button-group-button'; +import AvailabilityModal from '../../../app-components/modals/availability-modal.js'; export default connect( 'selectProductavailabilityByWaterYearByRoute', - ({ productavailabilityByWaterYearByRoute: productAvailability }) => { + 'doModalOpen', + ({ + product, + productavailabilityByWaterYearByRoute: productAvailability, + doModalOpen, + }) => { const [sortDesc, setSortDesc] = useState(true); + + const handleHeatMapClick = useCallback( + (date) => { + doModalOpen(AvailabilityModal, { product, date }); + }, + [doModalOpen, product] + ); + return (
@@ -58,6 +72,7 @@ export default connect( width={'100%'} year={year} data={Object.values(productAvailability[year])} + onclick={handleHeatMapClick} /> ); })} diff --git a/src/app-pages/products/product-details/water-year-heatmap.js b/src/app-pages/products/product-details/water-year-heatmap.js index aaba731..fcb0163 100644 --- a/src/app-pages/products/product-details/water-year-heatmap.js +++ b/src/app-pages/products/product-details/water-year-heatmap.js @@ -7,7 +7,7 @@ import { mergeRefs } from '../../../utils'; // width to height ratio to make our cells square const ratio = 640 / 112; -export default function WaterYearHeatMap({ year = 2022, data }) { +export default function WaterYearHeatMap({ year = 2022, data, onclick }) { const ticks = useMemo(() => { const waterYearStart = new Date(`10-01-${year - 1}`); const waterYearEnd = new Date(`10-01-${year}`); @@ -52,11 +52,30 @@ export default function WaterYearHeatMap({ year = 2022, data }) { x: (d) => d.x, y: (d) => d.y, fill: (d) => (d.count ? d.count : -1), - title: (d, i) => { - return `${d.date.toLocaleDateString()} - ${d.count} files`; - }, - text: (d) => d.count, inset: 0.6, + channels: { + date: (d) => d.date.toLocaleDateString(), + files: 'count', + }, + tip: { + format: { + x: false, + y: false, + fill: false, + date: true, + files: true, + }, + }, + render(index, scales, values, dimensions, context, next) { + const g = next(index, scales, values, dimensions, context); + for (let i = 0; i < index.length; i++) { + g.childNodes[i].onclick = () => { + let date = values.channels.date.value[i]; + onclick(date); + }; + } + return g; + }, }), // Possibly turn this on via config? // Plot.text(data, { @@ -68,7 +87,7 @@ export default function WaterYearHeatMap({ year = 2022, data }) { }); elRef.current.append(Chart); return () => Chart.remove(); - }, [elRef, data, ticks, width]); + }, [elRef, data, ticks, width, onclick]); return (
diff --git a/src/app-pages/products/products-map/map.js b/src/app-pages/products/products-map/map.js deleted file mode 100644 index cafc5a3..0000000 --- a/src/app-pages/products/products-map/map.js +++ /dev/null @@ -1,19 +0,0 @@ -import { connect } from 'redux-bundler-react'; - -export default connect(function Map() { - return ( -
-
-
-

- Coming Soon -

-

- View product footprints and create spatial queries for downloads - here soon. -

-
-
-
- ); -}); diff --git a/src/app-pages/products/products-table/products-table-row.js b/src/app-pages/products/products-table/products-table-row.js index 27512ac..ec956ec 100644 --- a/src/app-pages/products/products-table/products-table-row.js +++ b/src/app-pages/products/products-table/products-table-row.js @@ -135,6 +135,14 @@ export default connect( addSuffix: true, })}
+ {product.last_forecast_version != null ? ( +
+ Latest Forecast:{' '} + {formatDistanceToNow(new Date(product.last_forecast_version), { + addSuffix: true, + })} +
+ ) : null} ) : (
Coming Soon
diff --git a/src/app-pages/products/products.js b/src/app-pages/products/products.js index 9d43873..c7b8d10 100644 --- a/src/app-pages/products/products.js +++ b/src/app-pages/products/products.js @@ -1,14 +1,26 @@ -import { useCallback, useState } from 'react'; +import { useCallback, useState, useRef, useEffect } from 'react'; +import { + ChevronDoubleLeftIcon, + ChevronDoubleRightIcon, + ChevronLeftIcon, + ChevronRightIcon, + ArrowPathIcon, +} from '@heroicons/react/24/outline'; +import { CalendarIcon } from '@heroicons/react/24/solid'; import { connect } from 'redux-bundler-react'; import DateRangeSlider from './date-range-slider'; import ProductsTable from './products-table/products-table'; -import ProductsMap from './products-map/map'; +import CumulusMap from '../../app-components/cumulus-map'; import ButtonGroup from '../../app-components/button-group/button-group'; import ButtonGroupButton from '../../app-components/button-group/button-group-button'; import FilterPanel from './filter-panel'; import TagFilter from './tag-filter'; import ParameterFilter from './parameter-filter'; import DownloadModal from './download-modal'; +import DatePicker from 'react-date-picker'; +import 'react-date-picker/dist/DatePicker.css'; +import 'react-calendar/dist/Calendar.css'; +import './DatePickerOverrides.css'; export default connect( 'selectAuthIsLoggedIn', @@ -18,11 +30,13 @@ export default connect( 'selectProductDateRangeFrom', 'selectProductDateRangeTo', 'selectProductSelectSelected', + 'selectProductFilterRequiredTags', 'doModalOpen', 'doProductFilterSetFilterString', 'doProductFilterSetDateFrom', 'doProductFilterSetDateTo', 'doProductFilterSetApplyDateFilter', + 'doProductFilterSetRequiredTags', ({ authIsLoggedIn, productFilterResults: products, @@ -31,17 +45,71 @@ export default connect( productDateRangeFrom: rangeFrom, productDateRangeTo: rangeTo, productSelectSelected: selectedProducts, + productFilterRequiredTags: requiredTags, doModalOpen, doProductFilterSetFilterString: setFilterString, doProductFilterSetDateFrom: setFilterDateFrom, doProductFilterSetDateTo: setFilterDateTo, doProductFilterSetApplyDateFilter: setApplyDateFilter, + doProductFilterSetRequiredTags: setRequiredTags, }) => { + // Tab state - 'primary' or 'all' + const [activeTab, setActiveTab] = useState('primary'); + // show / hide the filter panel const [filtersActive, setFiltersActive] = useState(false); // could use a boolean here, but just in case we'll use a string key so we could add more options later - const [activeView, setActiveView] = useState('table'); + // const [activeView, setActiveView] = useState('table'); + const activeView = 'table'; + + // Show/hide region definition map + const [showRegionMap, setShowRegionMap] = useState(false); + const [mapHeight, setMapHeight] = useState(500); + const mapContainerRef = useRef(null); + const isResizing = useRef(false); + + // Handle mouse down on resize handle + const handleMouseDown = useCallback((e) => { + isResizing.current = true; + e.preventDefault(); + }, []); + + // Handle mouse move for resizing + useEffect(() => { + const handleMouseMove = (e) => { + if (!isResizing.current || !mapContainerRef.current) return; + + const containerTop = mapContainerRef.current.getBoundingClientRect().top; + const newHeight = e.clientY - containerTop; + + // Constrain height between min and max + const constrainedHeight = Math.min(Math.max(newHeight, 400), window.innerHeight * 0.9); + setMapHeight(constrainedHeight); + }; + + const handleMouseUp = () => { + isResizing.current = false; + }; + + if (showRegionMap) { + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + + return () => { + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + }; + } + }, [showRegionMap]); + + const [startDate, setStartDate] = useState(rangeFrom); + const [endDate, setEndDate] = useState(rangeTo); + + useEffect(() => { + setStartDate(rangeFrom); + setEndDate(rangeTo); + }, [rangeFrom, rangeTo]); const dateUpdateCallback = useCallback( (e) => { @@ -50,12 +118,38 @@ export default connect( }, [setFilterDateFrom, setFilterDateTo] ); + // open the download modal const handleDownloadClick = useCallback(() => { doModalOpen(DownloadModal); }, [doModalOpen]); + // Handle tab switching and apply tag filter accordingly + const handleTabChange = useCallback((tab) => { + setActiveTab(tab); + const primarySourceTagId = '8a7f4e6b-3c2d-4a9f-b1e5-9d8c7a6f5e4d'; + + if (tab === 'primary') { + // Require "primary source" tag (exclusive filter) + if (!requiredTags.includes(primarySourceTagId)) { + setRequiredTags([...requiredTags, primarySourceTagId]); + } + } else { + // Remove "primary source" from required tags to show all sources + setRequiredTags(requiredTags.filter(tag => tag !== primarySourceTagId)); + } + }, [setRequiredTags, requiredTags]); + + // Apply the initial filter when component mounts + useEffect(() => { + const primarySourceTagId = '8a7f4e6b-3c2d-4a9f-b1e5-9d8c7a6f5e4d'; + // Only initialize once + if (activeTab === 'primary' && requiredTags.length === 0) { + setRequiredTags([primarySourceTagId]); + } + }, [activeTab, requiredTags, setRequiredTags]); + return ( <>
@@ -83,24 +177,6 @@ export default connect( - - { - setActiveView('table'); - }} - > - Table - - { - setActiveView('map'); - }} - > - Map - -
-
+
+ +
+ +
{applyDateFilter ? ( -
+
+ } + calendarProps={{ + prevLabel: , + nextLabel: , + prev2Label: , + next2Label: , + }} + /> + } + calendarProps={{ + prevLabel: , + nextLabel: , + prev2Label: , + next2Label: , + }} + /> + +
+ ) : null} + + {applyDateFilter ? ( +
) : null} + + {showRegionMap ? ( +
+
+ + {/* Resize handle */} +
+
+
+
+
+ ) : null} + + + {/* Tab Navigation */} +
+ +
@@ -233,7 +422,10 @@ export default connect( {activeView === 'table' ? ( ) : ( - + )}
diff --git a/src/index.css b/src/index.css index a35b8f5..dc8311f 100644 --- a/src/index.css +++ b/src/index.css @@ -9,3 +9,35 @@ .thumb-pointer-events-auto::-webkit-slider-thumb { pointer-events: auto; } + +.leaflet-container { + width: 100%; + min-height: 400px; + height: 100%; +} + +.map-container { + height: 100%; + width: 100%; + position: relative; +} + +.map-container .leaflet-container { + height: 100%; + width: 100%; +} + +/* Custom cursor for resize handle */ +.cursor-ns-resize { + cursor: ns-resize !important; +} + +.leaflet-marker-pane { + z-index: 999 !important; +} + +.easy-button-button .button-state { + display: flex !important; + justify-content: center; + align-items: center; +} diff --git a/src/index.js b/src/index.js index 5027b74..19d432a 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,9 @@ import getStore from './app-bundles'; import App from './App'; import cache from './cache'; +import 'leaflet-easybutton/src/easy-button.js'; +import 'leaflet-easybutton/src/easy-button.css'; + const root = ReactDOM.createRoot(document.getElementById('root')); cache.getAll().then((initialData) => {