From 660434a6f8675771ac83f9956cc4d4267daf3979 Mon Sep 17 00:00:00 2001 From: TULCHINSKI LIRAN Date: Wed, 30 Sep 2026 10:31:12 +0300 Subject: [PATCH 1/2] feat: extract tileset.json from 3tz and validate footprint (MAPCO-11837) Add tileset validation to the ingestion flow for both 3tz and folder inputs: read tileset.json, extract the model polygon from its bounding volume, and require the footprint to intersect and cover the model above a configurable threshold. TilesetReader reads tileset.json without a zip dependency: for a 3tz it reads the first local file entry directly via a file handle (real 3tz archives break yauzl's central-directory parsing), guarding against data-descriptor/ZIP64 entries whose size is absent from the local header; for a folder it reads the file from disk. Polygon extraction (region/sphere bounding volumes) lives in @map-colonies/3d-shared. Coverage is checked with turf against validation.percentageLimit (default 10), wired through schema, config, custom-environment-variables and helm. Unit and integration tests included. --- config/custom-environment-variables.json | 6 + config/default.json | 3 + helm/templates/configmap.yaml | 1 + helm/values.yaml | 3 + package-lock.json | 2982 +++++++++++++++++ package.json | 1 + src/common/config.ts | 16 +- src/tileset/tilesetReader.ts | 108 + src/validator/validationManager.ts | 58 +- tests/helpers/metadata.ts | 8 +- tests/helpers/tilesets/folder/tileset.json | 35 + tests/helpers/tilesets/region.3tz | Bin 0 -> 1362 bytes tests/integration/record/record.spec.ts | 7 + tests/unit/tileset/tilesetReader.spec.ts | 77 + .../unit/validator/validationManager.spec.ts | 53 +- 15 files changed, 3351 insertions(+), 7 deletions(-) create mode 100644 src/tileset/tilesetReader.ts create mode 100644 tests/helpers/tilesets/folder/tileset.json create mode 100644 tests/helpers/tilesets/region.3tz create mode 100644 tests/unit/tileset/tilesetReader.spec.ts diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json index 6c1060f..30bd2cd 100644 --- a/config/custom-environment-variables.json +++ b/config/custom-environment-variables.json @@ -21,6 +21,12 @@ "taskType": "TASK_DELETE_TYPE" } }, + "validation": { + "percentageLimit": { + "__name": "VALIDATION_PERCENTAGE_LIMIT", + "__format": "number" + } + }, "provider": "PROVIDER_FROM", "NFS": { "pvPath": "PV_SOURCE_PATH" diff --git a/config/default.json b/config/default.json index 319977f..8de50f9 100644 --- a/config/default.json +++ b/config/default.json @@ -52,6 +52,9 @@ "taskType": "deleteModel" } }, + "validation": { + "percentageLimit": 10 + }, "provider": "NFS", "NFS": { "pvPath": "/app/models" diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml index f150bf7..9b572c3 100644 --- a/helm/templates/configmap.yaml +++ b/helm/templates/configmap.yaml @@ -45,6 +45,7 @@ data: JOB_DELETE_TYPE: {{ .delete.jobType | quote }} TASK_DELETE_TYPE: {{ .delete.taskType | quote }} {{- end }} + VALIDATION_PERCENTAGE_LIMIT: {{ .Values.validation.percentageLimit | quote }} PROVIDER_FROM: {{ .Values.provider | quote }} {{- if eq .Values.provider "NFS" }} PV_SOURCE_PATH: {{ .Values.NFS.pvPath | quote }} diff --git a/helm/values.yaml b/helm/values.yaml index c3cea6d..e08d90b 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -94,6 +94,9 @@ jobManager: jobType: '' taskType: '' +validation: + percentageLimit: 10 + provider: 'NFS' NFS: pvPath: '' diff --git a/package-lock.json b/package-lock.json index f23dc6e..08ceef5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@map-colonies/tracing": "^1.0.0", "@map-colonies/tracing-utils": "^2.0.0", "@opentelemetry/api": "^1.9.1", + "@turf/turf": "^7.4.0", "axios": "^1.20.0", "compression": "^1.8.0", "express": "^4.21.2", @@ -76,8 +77,10 @@ "license": "ISC", "dependencies": { "@map-colonies/types": "^1.4.0", + "@turf/turf": "^7.4.0", "geojson": "^0.5.0", "mime-types": "^2.1.35", + "proj4": "^2.22.0", "zod": "^3.24.1" }, "devDependencies": { @@ -90,6 +93,7 @@ "@types/geojson": "^7946.0.14", "@types/jest": "^29.4.0", "@types/node": "^20.0.0", + "@types/proj4": "^2.5.6", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "commitlint": "^19.6.1", @@ -4759,6 +4763,2754 @@ "dev": true, "license": "MIT" }, + "node_modules/@turf/along": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-7.4.0.tgz", + "integrity": "sha512-uWh26sph1bJm3eO1toaoq9fbPRNxtbQlW/u0m04n7m6uFyGrUu4qE+8DTDQeF+cHGWbMQft4zshHZy0rz4Nyog==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.4.0", + "@turf/destination": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/along/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/angle": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-7.4.0.tgz", + "integrity": "sha512-QcylOejONtVUQVG25/GznC2URvYLta6HCDLPB12wT/AEsG3pEKvUh5RGJDcFwk8WKfKPRp9C24kzsbp1Nwp9XQ==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/rhumb-bearing": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/angle/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/area": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.4.0.tgz", + "integrity": "sha512-B7q5f6QwKIxxbl4/L59lAYA+FF7h2hv6st96PuKue0TGCYGSxAG/lV8LHSPUSOuOrLywN4jytJcKK3vOFBs7Nw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/area/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/bbox": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.4.0.tgz", + "integrity": "sha512-yOX9lALc2GmjYUIE99+nRj7eZA7eDtx7Ixu/hrgIo+YqHvL1reaPVTC9rQWMHMm0pH0/WHjzk8uOpAHjptrudQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-clip": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-7.4.0.tgz", + "integrity": "sha512-Vxi8XznT/vhLHfqE9C1IMw0wi73+6EP3Bn51Vm0v9AWLUEv1zKPqR9/a4cj+s7f3diPE1g238rS48+BB5nYKSQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-clip/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/bbox-polygon": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-7.4.0.tgz", + "integrity": "sha512-hF0ylAOhzwk9i7m/PCmTtg+TP5AnFb9B+bMXgEPIYd/C3BOVet1YaA8nF6ViN5bNF0GSkWFYivvclSgWbtZyqg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-polygon/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/bbox/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/bearing": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-7.4.0.tgz", + "integrity": "sha512-utMyjTU5U3QfHWLRIKUAvJerCMmC6NLI7X6cmL6PA5+KYdzul/ymxL6CQrwJugrf9zt4Hnzvipwz0j5H1Yo87w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bearing/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/bezier-spline": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-7.4.0.tgz", + "integrity": "sha512-ctzTB46Mo5umz658IFh2C9zB8fs/FtqDOjcozAcUm7TUs6h4hdrXsCMRLb4a9NIX7ekPB84xQ6ZrISXwI+6/yQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bezier-spline/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-clockwise": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-7.4.0.tgz", + "integrity": "sha512-3wO+W9P63JZMK1Ud5ER0kKXpiZaeXkOR5Omq5USKlPcopwZ6TP/mJjt8lUYYWsB2WGDXuUhlYmSVec43z3wwow==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-clockwise/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-concave": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-concave/-/boolean-concave-7.4.0.tgz", + "integrity": "sha512-uSu01JxLsivXIyT8R7fN7ujyjyP0A8qTLaC/YlA2FSKDjpW1876KbWua203R9JxqZ4hgNUPCE7dhk9uTRXuZIg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-concave/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-contains": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-7.4.0.tgz", + "integrity": "sha512-vR8sfdKhSlDy6Jd6UAI2Bt27ZAU287G8lhLieIGfxKCpoUa2V8v3y0I9FH+ZpFen07I33aysHIZ4dMH92hBO3Q==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/boolean-point-on-line": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/line-split": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-contains/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-crosses": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-7.4.0.tgz", + "integrity": "sha512-athvZ8yB04BcuT53plyRJnn4jMmbiwlGk/pdxj28JZtsKipi+Ti6xmWDklFnrXpGfeaSHpPvKVt7XRduTB/qVA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-equal": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/line-intersect": "7.4.0", + "@turf/polygon-to-line": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-crosses/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-disjoint": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-7.4.0.tgz", + "integrity": "sha512-B6nXGFHa1+m181BZ5MOKa1l7/otws/NonUpOd0CKWTuBUzrlc6e60z/kRp5ceRYmE2LFOH5ZZxItnrsWgKc2VA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/line-intersect": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/polygon-to-line": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-disjoint/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-equal": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-7.4.0.tgz", + "integrity": "sha512-OZWSaWvG7tZn90/7sBclZxpLyjrbxh8oNfOqrmdJigTY/XzAyjCE+jaweVTY8NGRkytC53kkDO0Q2DPZptpUcw==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.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-equal/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-intersects": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-7.4.0.tgz", + "integrity": "sha512-tV30WLBgLh89uGYpWsKjU29eMhdyhSh2Sc1IKmv1W0YFHKwp6oDm2eRajMzMgdtN8cl6Jyb8oFCmw4LbMMuWAQ==", + "license": "MIT", + "dependencies": { + "@turf/boolean-disjoint": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-intersects/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-overlap": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-7.4.0.tgz", + "integrity": "sha512-WcwQC042FnqDbZJCTm67NoQqV3bWz3C5M89b39MJtEPDWbO6kWwgMBt3Rm9w6gwrYljPE43vVB4pLiaSdNgKSQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/line-intersect": "7.4.0", + "@turf/line-overlap": "7.4.0", + "@turf/meta": "7.4.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-overlap/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-parallel": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-7.4.0.tgz", + "integrity": "sha512-EUjTkvbd1zumtXwP3vEQPsjdlny9UjDkWRzNrZjBpc0MVY9g0clSslMBgHbcJAtOz4lP3+6fqOgwOkjA6XJBLQ==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/line-segment": "7.4.0", + "@turf/rhumb-bearing": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-parallel/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.4.0.tgz", + "integrity": "sha512-GQrHc6WSDHhf32GB/b9AGyOeC05AQ3nGN7ooWTrlCYefKn7X9/2wG9BNexF47AuyDq5jPdOvITVR26BLFWV+mA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.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-in-polygon/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-point-on-line": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-7.4.0.tgz", + "integrity": "sha512-5i5UBeZf/LuzgavkIj8L8L5/8SYWItWzQQcE9ymFZr60lV5lM8eFvQDfzAz2R3WQFCrxXExPqNvAVZOKGUyvoA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-on-line/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-touches": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-touches/-/boolean-touches-7.4.0.tgz", + "integrity": "sha512-ociH8gYuQ/jpTIFDAr3krbh5lMWjfbJXG1diGdmV03smutui/LhnMF8AvqQ46/nzo1IgD0640gbdzkQn91090g==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/boolean-point-on-line": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-touches/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-valid": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-valid/-/boolean-valid-7.4.0.tgz", + "integrity": "sha512-Gop9r5YA3Asz80bPDCs3PjO4YPY5fyE55wlQDqLBSr6ZxbOaJXIoKrMgaBb1RBcM+9BFYnBlnpekMG3C8Gscrg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/boolean-crosses": "7.4.0", + "@turf/boolean-disjoint": "7.4.0", + "@turf/boolean-overlap": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/boolean-point-on-line": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/line-intersect": "7.4.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-valid/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/boolean-within": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-7.4.0.tgz", + "integrity": "sha512-wld+Og3HZlSe+QcovDY4JKLDkEqOx6gW52jkvAWIvbr4fDB/HUV95zX6SY78reJwm6hNwmnuX3nSriWooLU4HA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-contains": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-within/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/buffer": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-7.4.0.tgz", + "integrity": "sha512-ewYjC7NmmP+BZ8JcjX1TglzYH1FCMWAh5L08j0iAP4lbn3/AutVjjbwIXedZZy5He1OfgmXf+rIBcNXdeEYceg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/center": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/jsts": "^2.7.1", + "@turf/meta": "7.4.0", + "@turf/projection": "7.4.0", + "@types/geojson": "^7946.0.10", + "d3-geo": "^2.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/buffer/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/center": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-7.4.0.tgz", + "integrity": "sha512-JVuTaIdnQtmTV8eQfVxqza1okKiaFzgVeIA6Xoy/u6aBMxg2+oBpS6GcHZkEqR8OapPWValB1AYYqimUSiyOXw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-mean": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-7.4.0.tgz", + "integrity": "sha512-M4PG7NPiy5OoxueLkgw/OIe3driLic37xSsfs6KSjg7u/ZNBysymThy9a6lYUlygS/Z1xhqSfjOsOcxHtxKTzA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-mean/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/center-median": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-7.4.0.tgz", + "integrity": "sha512-R2ap2S789WRFzbk54gv3hSN0vnT+djRL0nqultWmvhYBJE2FsN5Z0libZNetP+VyOV/mg5AF8CFLbkmnmqJg3Q==", + "license": "MIT", + "dependencies": { + "@turf/center-mean": "7.4.0", + "@turf/centroid": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-median/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/center-of-mass": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-7.4.0.tgz", + "integrity": "sha512-wpa22gBJvwYmvh+I3idMds1mtSEYXqLFTu9s9U7TZTy2HrglIIe+6iz0zSm4is58W7TNXQQlfeCmyvURU2+kdQ==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "7.4.0", + "@turf/convex": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-of-mass/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/center/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/centroid": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.4.0.tgz", + "integrity": "sha512-WglCFe+TnMqeYa/LcbUp37NgIF0zHTNYmlxwUm40fyyzyLsYeDq4dgFWLTYpvRNqb5oIMV6xsBjSf+vCPtVJ6w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/circle": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-7.4.0.tgz", + "integrity": "sha512-H7dXQP7WRpIE/UwZFFGYoSewsali5rjk9yzHPNZ03Bc9F11PWA3H+lwznEWmi/wLMMx91uMU+NkyGtJUeQr9cg==", + "license": "MIT", + "dependencies": { + "@turf/destination": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/circle/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/clean-coords": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-7.4.0.tgz", + "integrity": "sha512-xl3lVPey/FyVc3Z+drAWtVuVtvt737RHKGUMGyuskmKYW0JUeFDgqSR0OnrDfxXWb58Im/65BgPd1B+TUA9WfA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-on-line": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clean-coords/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/clone": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.4.0.tgz", + "integrity": "sha512-IfYnuil7XYJauy3crzIYEr26QkmBiTgFdGfYwUUe3S6dawX+lyb3vhuaYyssEPcCJq83g0G7hwxZy7141Mmzjg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clone/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/clusters": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-7.4.0.tgz", + "integrity": "sha512-ZY8ZXONy88EnsHCTdaAILkZjkR3gd8xYx4Sc8TpRFuRTDzI2abzU6ET9zezMd+U8QWs7//QJctcfapzCpXBXQg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-dbscan": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-7.4.0.tgz", + "integrity": "sha512-6uKPUvL7VNx3MueOqgGTx/pnGe5p5rgy/tA/6HOgRFLlDu0oYUTUdNg2JE0ylYOc0HW5hxCcmsRR0lz6fxw1KA==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-dbscan/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/clusters-kmeans": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-7.4.0.tgz", + "integrity": "sha512-fGSapN0nNzQ8NdKSsVgwPUQ5vkwlVK4M/zGN91jY29VcKb6ua8CotH7DNGKutCByr0c7WBefhUGeYVeZEgd9tQ==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "skmeans": "0.9.7", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-kmeans/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/clusters/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/collect": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-7.4.0.tgz", + "integrity": "sha512-vQ0NH9SCUacKtQGValYxF8Q1mv4g4vLdWSwuVjIDbsH7NTgGUCFQwKXeHuGqh3U9r2PCQ+3ZbsMK61ok8U75fg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/collect/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/combine": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-7.4.0.tgz", + "integrity": "sha512-G3tEctgA3Kqz9T3PjF3lQdMUodQxOOLLBy8s5Y//8qyx7IRJqOazxdCaZRo2lq7C8eEdoUud6PIAXLz0gHue4w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/combine/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/concave": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-7.4.0.tgz", + "integrity": "sha512-EzCHDEbGRVbo9vR/HJriC8U7mpxgTYgyluBDNzcaT7kX53eH7/eS1U7rp/Q36e5RXMmlAWBknqtVseGMvKE5tw==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/tin": "7.4.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/concave/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/convex": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-7.4.0.tgz", + "integrity": "sha512-MKKAKBMZAQJLicdJSck0rPkwvf/Yz4IFl672iNq9ZoofQxgAqQSEGtBGy4Zgnr5edDoIexzLRAoqm6woyx4Lzw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "concaveman": "^1.2.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/convex/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/destination": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-7.4.0.tgz", + "integrity": "sha512-+jFpOUtzCiN8l9ZOvr66rxi/UxizphXaBDldaw9NfxlX/HcMn3ZDzC8IiCHiNXAilVh4WpCH0qqmH9ykOgdBKA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/destination/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/difference": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-7.4.0.tgz", + "integrity": "sha512-4zV7Xi3CwUdyNsDzrPty08PiUeYYVBdbCnNVlSf8sk5Z7SL7seA42RabRG8ByTzcrbLATeDU4oTEthVhOGPlyw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/difference/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/directional-mean": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/directional-mean/-/directional-mean-7.4.0.tgz", + "integrity": "sha512-TEgKLz71yLVAvjP9ozRAtB1gk7ziSq+a7LGePkMWLMxLtoJxb7eapAqcDk/QqRUBUJ4Dw/41qtRRwkVX0hQE/A==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.4.0", + "@turf/centroid": "7.4.0", + "@turf/destination": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/length": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/directional-mean/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/dissolve": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-7.4.0.tgz", + "integrity": "sha512-UHx+XiReD+qHXN0dKOyOxlB+NF0L1PkUHlERCPZcYNqd4JWl2uqOjMEBYxlVeBj6SO3yQPTM9kvWXPE3HqLanQ==", + "license": "MIT", + "dependencies": { + "@turf/flatten": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.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/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/distance": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-7.4.0.tgz", + "integrity": "sha512-ODkopQDG1m/U6Mx7OMmShncaCvneomwX3lZY1CDA7x40bhDdTTRYP/n/6LicZVyIO4/oK+aXkov4NOtWYthA2g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance-weight": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-7.4.0.tgz", + "integrity": "sha512-HUS57zzrSJAUUUlpEZYAsjK+BuCisgRf5/9pW5uW9TfegSbysA0T0C800GYqZcSPIpv22zVF5LE/hFnnu8WjHA==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance-weight/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/distance/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/ellipse": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-7.4.0.tgz", + "integrity": "sha512-m5s/fwYUbNN1MW/nW33sZ/jD58lMU1kGP6muU4inGAP7OxKbS15yz+Qg2aXsfyKcb0z19AFjhnCLQGWHU0sl1w==", + "license": "MIT", + "dependencies": { + "@turf/destination": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/transform-rotate": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/ellipse/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/envelope": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-7.4.0.tgz", + "integrity": "sha512-BNpwP70dFElOrYAVxLvjd9kMnxdowJB0ojsTs2OTvaC8Hp+qfrVTzTK5ZZ6Dzix/UpgdRlC5cDrEmbXTdvDpjA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/bbox-polygon": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/envelope/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/explode": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-7.4.0.tgz", + "integrity": "sha512-kTWoryCTAiBwFvyJYNQWLJCaFx4bSRpyCjJWmuLoBEQRh9Fi4JLDEug+6JZxygMxCMWGXBH8/R3ICl+soLPFAg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/explode/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/flatten": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-7.4.0.tgz", + "integrity": "sha512-yrXpvXtmPlXIIoRuonQ7z+Uj47Sw9KYlH9oh9NBb3QRfz53LN+OTNndkGeXPaI00BDXxO/aZ7wEGmzuoCaDn1w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flatten/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/flip": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-7.4.0.tgz", + "integrity": "sha512-SlLwkHlY0MzFXmn/3pFNT7JILsEZer+KzLIxVxebiu6nd3aQaIOqGvxLHVFRbIXYd48UrORg1L40sWsw8z1Egg==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flip/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/geojson-rbush": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/geojson-rbush/-/geojson-rbush-7.4.0.tgz", + "integrity": "sha512-o+debVj+6KQ2p0VebH+FgcTig2jUuG6mWMgLYvb34c7cOHoEuY6ICvKRKtHn/XcHa6eyoSk0TvnLEJAfFTQDMg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/geojson-rbush/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/great-circle": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-7.4.0.tgz", + "integrity": "sha512-dV8qrH9rpvtUsqrXgUKWojCCJTdgAKgX6eyE+zm9GUtzq0pYUxG2JXVIxWBT65ucQPigBSbS5hxx6lAOpoD/Ew==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "arc": "^0.2.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/great-circle/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/helpers": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.4.0.tgz", + "integrity": "sha512-7PAwLZqOdRzTI5g9bHvUwlloAXPDH/mlajtryk0tw4ZwGMtmXsAyF4QundsAMfy4u48Fyd3AUqMXY0SMxqJGWg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/hex-grid": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-7.4.0.tgz", + "integrity": "sha512-IQ35KzK2N9KZ7W0xVeZlDVcXURE2h+GD0CHru/LlWW7hkjVLNYvWjm+ZlMA141As038+4XremSGNzHAZxp3xsQ==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/intersect": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/hex-grid/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/interpolate": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-7.4.0.tgz", + "integrity": "sha512-QRY0i2CW5ZhEhvDa1HDCO5gjun25HSjo4X7/Q2gC8BkjwpL12Ogo883BGAlt5Qq0JZq5uVFU9Ae7W7yVjfIPfw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/centroid": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/hex-grid": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/point-grid": "7.4.0", + "@turf/square-grid": "7.4.0", + "@turf/triangle-grid": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/interpolate/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/intersect": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-7.4.0.tgz", + "integrity": "sha512-n/wbZfDPoM1JKwrql8mHBoS9cdRWUC7/VE9IBZOttVPv8Guo0B6+Mzc2Z2e1PdIkSP/1R2yH6lxPpWMuuBfVEA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/intersect/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/invariant": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.4.0.tgz", + "integrity": "sha512-OAsc3qdNx+tRqzWmMNFMnVlWWACIqnYqIejZKvb2oKkKPYJJrURPXZ6OdrGD58ljJMolLoqwIZGSx3VndaEjxg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/isobands": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-7.4.0.tgz", + "integrity": "sha512-ClOkFMwcgQmRCespoQavY4AA+gH5qkbcQgrGsKn12VrgbresgA2hBM9bVMNDaEmDauN7RdDBoNcNhkakhD/ZOQ==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.4.0", + "@turf/bbox": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/explode": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isobands/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/isolines": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-7.4.0.tgz", + "integrity": "sha512-Pu0i03/GqAcYT4yJ9fj3NWa4Z/c4Viv3Duhzua05XLJ9c70wWJmb+eniLg2l+qiuVKi4ob0Xl2AKkgkKYiezgg==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isolines/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@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.4.0", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-7.4.0.tgz", + "integrity": "sha512-M2deZpJq45WASRru5MmGwDJkVIj+JL/rcS6LLZBQyhSna47pxOQIVDC/tIx6xyFezxXw3lhX9VsoRst3irDdAQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/kinks/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/length": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-7.4.0.tgz", + "integrity": "sha512-vQsJLuL7uy8m6HAMuJJKoiOHYY1de1Hd6mY/fM90qfu2RyIrkoF8J4bRUb72XZNCVnvob/eHpUMdhZeoUhSyfQ==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/length/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-arc": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-7.4.0.tgz", + "integrity": "sha512-fkqfm3S1nRIBoKoK3LfpjDVGswyOw1cRb0W1qV6Na02ccrCJfJ7P9C4SkKZz2iakZioFCot4emraWno3B9YsYg==", + "license": "MIT", + "dependencies": { + "@turf/circle": "7.4.0", + "@turf/destination": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-arc/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-chunk": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-7.4.0.tgz", + "integrity": "sha512-vGytKbvTc+PtRimeEJez8nGoIxuu5uQiqSrK7DD7JB4ULLnBtABAMEr0EaEQQ2fyvE1zkTsbWSQPoFlQ3M52cQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/length": "7.4.0", + "@turf/line-slice-along": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-chunk/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-intersect": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-7.4.0.tgz", + "integrity": "sha512-5qNmEJ/0tQEaUUKSt2zIZGIS6eYGn3T7Z32yN6+Py0ANs8ln3dQNWaDyOEMms/qVY7BFvSaO+8iBmD8vlHdNcA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-intersect/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-offset": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-7.4.0.tgz", + "integrity": "sha512-vAF3tNvHfo6efcrDwhjSL7UgAt2QjPyCKhIWO0g5xvuGMw9v3DZDErla3fVNT/b+DG01UmNmYQWvMzlUWcTLoQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-offset/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-overlap": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-7.4.0.tgz", + "integrity": "sha512-40ZFjTk103uXFF5MfkhqzhowvBNozMcm18yR24Z38BbLC+4BLUNUslNDqyXStGZPXQ1X0MH3LeE2TbIpw9wKRA==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/boolean-point-on-line": "7.4.0", + "@turf/geojson-rbush": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/line-segment": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/nearest-point-on-line": "7.4.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-overlap/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-segment": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-7.4.0.tgz", + "integrity": "sha512-v4Gnxpj6XqWbl4d/aVZHYwvSFzaTGGBW4eQQ9Ozqcgt+bvignOMdtVogFDHSWKDj9Gr2tbS2jYp57c8OTa+Olw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-segment/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-slice": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-7.4.0.tgz", + "integrity": "sha512-urpVBcce/Z2Gprhv6cRniiaOr0L2UVjBqzEpp3esrW20iu25aDXor43d2KAqAP3Yhlv+vNMFlAJAGPcX/nTSzA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/nearest-point-on-line": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice-along": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-7.4.0.tgz", + "integrity": "sha512-N3rBS/E/N2v2udmglBU1XTfWIQlxkSDuzGCjbGnWUMX2ClVgJvptQXI1zQ29aIeOTbcqKNiSbEyqBg8e+fugPw==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.4.0", + "@turf/destination": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice-along/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-slice/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-split": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-7.4.0.tgz", + "integrity": "sha512-bAij7u19sFVdYoAp3bQnu/cVzlORxM/W3FM+WeFN/VvGkZ6YiAtcPq/lLP4WwK3WxthTcavED3wbd32iXGpzZw==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/geojson-rbush": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/line-intersect": "7.4.0", + "@turf/line-segment": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/nearest-point-on-line": "7.4.0", + "@turf/truncate": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-split/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/line-to-polygon": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-7.4.0.tgz", + "integrity": "sha512-wUsYNTP+tN+2J0+tzakKVHdo6Y1Pe8Ne1rJUuCxZRumwOFyML6/aIQvQaXdbAjN0gmfuoZTCv78RDSZBtsAPAQ==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-to-polygon/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/mask": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-7.4.0.tgz", + "integrity": "sha512-NVED/D8XcTHJTi080l9ezH3f+BzFbnBdVa0s5G9Q+sZQcrabSS5BOL1kdvQhu1LS49QhAGYNBtcfKB9e0hvwJg==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/mask/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/meta": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.4.0.tgz", + "integrity": "sha512-3cLUvlEyDuSnMSzrjhaLAEiYR8xhbfWyTVQlrlEw40xL81d4KF4PqUWbjTXKpXZStdYbet2GCurl79KMypNw6g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/midpoint": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-7.4.0.tgz", + "integrity": "sha512-+poTGThn9F2tveuUDjg1z5TE3QG8zp/IHVqpzkFwFLWX3xpeys0mev9H3LqlbzRU7waawZJ/lsqYlHg3mIWHJw==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.4.0", + "@turf/destination": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/midpoint/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/moran-index": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-7.4.0.tgz", + "integrity": "sha512-lZqEUOhpmV2Fngjv6O16Pl3zbDlRUudEeuv/iinkT5adesOAKqngIrg4wYOT0kkNaSdaqNqbKyJGZ8mAlP4Lig==", + "license": "MIT", + "dependencies": { + "@turf/distance-weight": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/moran-index/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/nearest-neighbor-analysis": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-neighbor-analysis/-/nearest-neighbor-analysis-7.4.0.tgz", + "integrity": "sha512-WBZbuH+f7/ezEdBGMToT2yYzONdYqhE79yxHTvoJMk1+o0eakg4DY+/owM5FWYgB3GnJCp260//Uz1D/q6wFlg==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.4.0", + "@turf/bbox": "7.4.0", + "@turf/bbox-polygon": "7.4.0", + "@turf/centroid": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/nearest-point": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-neighbor-analysis/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/nearest-point": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-7.4.0.tgz", + "integrity": "sha512-rH+M5H/rWiOXStiHkV3Cs5OS+Tg/531Tonr7tm2pUjVy1RRLmDqSzKn+iFs3S6ybvjSK2pJa1BbTXjYlc8Pv3Q==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.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.4.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-7.4.0.tgz", + "integrity": "sha512-eKTKf/qODIsPankYMZrNfI3LjLaVtQvYV/1hcj9kH/Zr3GfLUszevjhTRDZtMgTFrv8+awW3+MyhmN+PJGuLdQ==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-on-line/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/nearest-point-to-line": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-7.4.0.tgz", + "integrity": "sha512-PQjS+7/OIjib5HGHtrtDuIH0B6ifT8b4b8ADmcvKcKEMaOk5wjc+Z40oPc0n5OwRoddvbd2K2yV59+fh1cXxeA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/point-to-line-distance": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-to-line/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/nearest-point/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/planepoint": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-7.4.0.tgz", + "integrity": "sha512-4TQxrbFpbEjIEv9C+5MO5BbnL/Ay2wrJlLxaiIbJ61LaFFuMx6ZCWu1rBYMDo72Ik5+oK4Pik1J5e8rKW3YGRg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/planepoint/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/point-grid": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-7.4.0.tgz", + "integrity": "sha512-Lx7M6JkLHT8A3hPc+811rALohOJ9mJBjAh+NQz9Mj/QdFmnhzGa8aia4F4agy3uSyJP/7EFXUXlPRK4Yc6/4tw==", + "license": "MIT", + "dependencies": { + "@turf/boolean-within": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-grid/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/point-on-feature": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-7.4.0.tgz", + "integrity": "sha512-H7YamrW510QJLnhWIT9oecm1siwhwvb4SZrZSqu6hUOS+pFxumJjUYogE+B6kL+Ymtui2+h6cOtOHJnrNrKaEA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/center": "7.4.0", + "@turf/explode": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/nearest-point": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-on-feature/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/point-to-line-distance": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-7.4.0.tgz", + "integrity": "sha512-2Egagkj3BK6SrcUxMBJ40PHhW/numknLsJQhlv4kl3d3t83HChhUtH2mXnhqcdi97V3jXSRV1SZIhZKc8TrwOg==", + "license": "MIT", + "dependencies": { + "@turf/bearing": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/nearest-point-on-line": "7.4.0", + "@turf/projection": "7.4.0", + "@turf/rhumb-bearing": "7.4.0", + "@turf/rhumb-distance": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-line-distance/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/point-to-polygon-distance": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/point-to-polygon-distance/-/point-to-polygon-distance-7.4.0.tgz", + "integrity": "sha512-37h+HzsMgXMu9MJhfQJk11aJ8ZSK4Db/kccvrIyRFv+SX7bCjFON6P4FC4+4PVACajK9VJ52h+KRufaCZqlxdA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/point-to-line-distance": "7.4.0", + "@turf/polygon-to-line": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-polygon-distance/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/points-within-polygon": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-7.4.0.tgz", + "integrity": "sha512-8TtUvUeM7USPD11rEPPR/C+aADov1G8wxmgP8ExE2KUDCg4X6cnYTxpWgOHe3uijpv4QRHGhiz12xNmB4ysyyA==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/points-within-polygon/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/polygon-smooth": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-7.4.0.tgz", + "integrity": "sha512-UxcEo+OrHbl4+YWuhiIT2lLXvA6rohNdbRBqnZY/X9uQ41JYhh5gMYwlQxbsLTgdYA2pE6wJO52PDHvpqsPu4g==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-smooth/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/polygon-tangents": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-7.4.0.tgz", + "integrity": "sha512-QXKxDhRIDeW/+FQ8Eh6gQMPJFpyf6iXal6tdbL7+5jw9Zpm8DTFVvcPzERBZ3gey6P3CSqP/GD1zZT8gQ+mZng==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/boolean-within": "7.4.0", + "@turf/explode": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/nearest-point": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-tangents/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/polygon-to-line": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-7.4.0.tgz", + "integrity": "sha512-LCHZ9bojUj99et83ZLGpSR/PFx48c0tcjtfuI1nn66a6Q+EGy7GO4NsHv4rWZsC8Lljy5ldbJ3dd8+WGnAk7dw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-to-line/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/polygonize": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-7.4.0.tgz", + "integrity": "sha512-SwEazUYz4KFXllNNXGUt2rP6XpPBfNEOnx+1uP0rcioIsNocFfgwfCUcgHXYM+GZhG0JTD7jQB9RXeNGb6hNIg==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/envelope": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygonize/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/projection": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-7.4.0.tgz", + "integrity": "sha512-ZS932qM+18V1UBvNlrNXUCD7mh9CzQqZzw/ElVtb0kAk6N00FYQ9bo26Cca08UZKXh3rZqG/e4eCK74JNGS4Zg==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/projection/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/quadrat-analysis": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/quadrat-analysis/-/quadrat-analysis-7.4.0.tgz", + "integrity": "sha512-p+Af0+LpwnsVQuGSL1BWuLxgMjd6dYaSs+PxwJ2Y05nSXlsIFpWy+/T55r0Sfun7SoypclKURRCmDy3Q/RVdJw==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.4.0", + "@turf/bbox": "7.4.0", + "@turf/bbox-polygon": "7.4.0", + "@turf/centroid": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/point-grid": "7.4.0", + "@turf/random": "7.4.0", + "@turf/square-grid": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/quadrat-analysis/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/random": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-7.4.0.tgz", + "integrity": "sha512-ttoPPPQppLYww3ZSeOSlbZlaH4Ki4Mxrdu7wMuDWNrINEhrkr/PuqWyIDISTpILsSARqT0Zzfjwdp0ptkUyakw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/random/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/rectangle-grid": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-7.4.0.tgz", + "integrity": "sha512-JeRq5pgmn543DmZU6x4nSX+KVNRGEz3iJQIgbKgSMnzZeiJxfMStINvASz7mz6yky2EwUHvQOBRRlhgCqWViuQ==", + "license": "MIT", + "dependencies": { + "@turf/boolean-intersects": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rectangle-grid/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/rewind": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-7.4.0.tgz", + "integrity": "sha512-Q2qOcLzymxXyIAYXM9BZQT09L63EhzgneCBMka0dPf9jVShxMeA3oF1kY20ChWkZvTGOAbM7YU9hghphbCxo2w==", + "license": "MIT", + "dependencies": { + "@turf/boolean-clockwise": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rewind/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/rhumb-bearing": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-7.4.0.tgz", + "integrity": "sha512-y5omgg5SqHwz46fZlra2/70z5eOKXSp/ZzRNIDYKJVYF5oNT2YbG9Z6lj4Az51bjqJQESXUoR6wRmFnpliIp0w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-bearing/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/rhumb-destination": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-7.4.0.tgz", + "integrity": "sha512-3x4dNLheJJIK/6daH9U7dDkFRSq3wqvDIdqgSxsqgFHoNEI6xDACKGrx47JFWVbg/zh9osG5JSv96e70Y4P5XA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-destination/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/rhumb-distance": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-7.4.0.tgz", + "integrity": "sha512-29pFCftS/yxEzrYWB777I23Mob+yw9WAECJNw+uabbJi6fwFphJfqWApMXY2ZnudVSJFCGJbByVAHVDTvOGG+w==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-distance/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/sample": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-7.4.0.tgz", + "integrity": "sha512-RfU8+bp5RzxxDq5ldKk8qR+E/ADboOhlcVjFu2p99SVivlnbkMBNXlFRAwdmaDeyqI6bTuwymc7DsCxE0ZA1Uw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sample/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/sector": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-7.4.0.tgz", + "integrity": "sha512-BGTbeT/LHW+6H43by4EQfAQaql94rOg5sxIN1T9/PeQR13KV6CnCVzxEmMzPVEZvy7VDfssv9eMerY0WGkjfYg==", + "license": "MIT", + "dependencies": { + "@turf/circle": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/line-arc": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sector/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/shortest-path": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-7.4.0.tgz", + "integrity": "sha512-Bq/4/yN+CFWo0TcxXKO/VW7/BB5EgrHj9lwP4Kbk00i43flF0EpnlABWoO/MDUVFBPAkBCyWUgcCUW5MZbid6w==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/bbox-polygon": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/clean-coords": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/transform-scale": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/shortest-path/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/simplify": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-7.4.0.tgz", + "integrity": "sha512-HTolHFuXLReKHJoh3iz92+WpF5EsuCmmZR7fNv3hWnA4hZfkuEzbGB/WZqRdQpvDs99IUS8QIc6BAp6FHzQbBQ==", + "license": "MIT", + "dependencies": { + "@turf/clean-coords": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/simplify/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/square": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-7.4.0.tgz", + "integrity": "sha512-XjL7taeIZdh3iIS1vertswqa+dgQEgBlByfInjTj3uDDjrpHYG13Yxg6EHuOW/y1SeTWp2fTAKLRQFexC+kwGw==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square-grid": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-7.4.0.tgz", + "integrity": "sha512-jq4pxYs+FQ8Y1m3RhpTBYvSLzSwj75hurT53T1jXboEYIyeFGtGdlOy2o3yx/2cxUK+AP1KtKrQGX5v7sdpzVg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/rectangle-grid": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square-grid/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/square/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/standard-deviational-ellipse": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-7.4.0.tgz", + "integrity": "sha512-02Erb1A1BARE0MKxLMxsSL3BVHZWZrW/edZISvqZUF1eBOc7FMpIy6Pu55COkFkN3YYOQHysrc4COAZdYEjcwg==", + "license": "MIT", + "dependencies": { + "@turf/center-mean": "7.4.0", + "@turf/ellipse": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/points-within-polygon": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/standard-deviational-ellipse/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/tag": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-7.4.0.tgz", + "integrity": "sha512-IBd4Lz01FWyTnyxmzcyK3DypMkd7vMzptZIQFlkC/LDeMnPMnmT1oJD7wMdZCJIV1YGVC5abpXoNwY3xnj/eTw==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tag/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/tesselate": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-7.4.0.tgz", + "integrity": "sha512-73Jj0+9GPUYoegut9J1ex9kVYe+WAIV+tBm6EXXtaZzmobIp8fPsAvspyTzXMQvPtlUsf/St+L0W4KFwWe2KrQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/earcut": "^2.1.4", + "@types/geojson": "^7946.0.10", + "earcut": "^2.2.4", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tesselate/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/tin": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-7.4.0.tgz", + "integrity": "sha512-54uBeNKIP8GufZCdeMUMy33e0blR4w0JoE6sYO8I6ZPshNRbVbI24ck+kREVMjzNyeuhL+l7DUrNEn/pcDgBRg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tin/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/transform-rotate": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-7.4.0.tgz", + "integrity": "sha512-JsXtvydU23rJdOlS5u9Fpdi6mPtbRCo8/6y3QSjkg37EubFTd357M+lX8+lQ+LHjs8hXxvp9LKBZwTujlqsDcw==", + "license": "MIT", + "dependencies": { + "@turf/centroid": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/rhumb-bearing": "7.4.0", + "@turf/rhumb-destination": "7.4.0", + "@turf/rhumb-distance": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-rotate/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/transform-scale": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-7.4.0.tgz", + "integrity": "sha512-lPsvppxBfK+5z9fC1R3F/7y4113cwZ7hLG9dR97EUYaUhphQveVShrFK18S8+AXxs0J4PnDm3HptTfuJyD7bMQ==", + "license": "MIT", + "dependencies": { + "@turf/bbox": "7.4.0", + "@turf/center": "7.4.0", + "@turf/centroid": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/rhumb-bearing": "7.4.0", + "@turf/rhumb-destination": "7.4.0", + "@turf/rhumb-distance": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-scale/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/transform-translate": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-7.4.0.tgz", + "integrity": "sha512-tMHS7lovcHRpKtM0A9w1Y337ZM279YALzPmEvbLA/1Piy6Ik2osecxGB90T+HaobYkWVWhiQ70XP48f49qFc8g==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/rhumb-destination": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-translate/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/triangle-grid": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-7.4.0.tgz", + "integrity": "sha512-3aPtajJH67JkuEe+9WAEecRHBs9Q6lK0KrJIhs8QDx4VJ4nGjRazomz+UuxbhBRtn37lQ/cIxWn0nH9JYFembw==", + "license": "MIT", + "dependencies": { + "@turf/distance": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/intersect": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/triangle-grid/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/truncate": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-7.4.0.tgz", + "integrity": "sha512-eTXO62l1mD++mKlhUqLNHGcgCQvhxXJGB/EUL1KSzpVIeMYtgcmItAneWdHW+gEpmHfjeDjBuvtbZWA23r7Eig==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/truncate/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/turf": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-7.4.0.tgz", + "integrity": "sha512-enR/94yESzZb4t7lj/DSqk6oMdsSfPhMnXHn+f4umpdtZuMmFSbB/mPWxMFoyWCD0z+luwPwtJ7kdtHO5nGi9w==", + "license": "MIT", + "dependencies": { + "@turf/along": "7.4.0", + "@turf/angle": "7.4.0", + "@turf/area": "7.4.0", + "@turf/bbox": "7.4.0", + "@turf/bbox-clip": "7.4.0", + "@turf/bbox-polygon": "7.4.0", + "@turf/bearing": "7.4.0", + "@turf/bezier-spline": "7.4.0", + "@turf/boolean-clockwise": "7.4.0", + "@turf/boolean-concave": "7.4.0", + "@turf/boolean-contains": "7.4.0", + "@turf/boolean-crosses": "7.4.0", + "@turf/boolean-disjoint": "7.4.0", + "@turf/boolean-equal": "7.4.0", + "@turf/boolean-intersects": "7.4.0", + "@turf/boolean-overlap": "7.4.0", + "@turf/boolean-parallel": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/boolean-point-on-line": "7.4.0", + "@turf/boolean-touches": "7.4.0", + "@turf/boolean-valid": "7.4.0", + "@turf/boolean-within": "7.4.0", + "@turf/buffer": "7.4.0", + "@turf/center": "7.4.0", + "@turf/center-mean": "7.4.0", + "@turf/center-median": "7.4.0", + "@turf/center-of-mass": "7.4.0", + "@turf/centroid": "7.4.0", + "@turf/circle": "7.4.0", + "@turf/clean-coords": "7.4.0", + "@turf/clone": "7.4.0", + "@turf/clusters": "7.4.0", + "@turf/clusters-dbscan": "7.4.0", + "@turf/clusters-kmeans": "7.4.0", + "@turf/collect": "7.4.0", + "@turf/combine": "7.4.0", + "@turf/concave": "7.4.0", + "@turf/convex": "7.4.0", + "@turf/destination": "7.4.0", + "@turf/difference": "7.4.0", + "@turf/directional-mean": "7.4.0", + "@turf/dissolve": "7.4.0", + "@turf/distance": "7.4.0", + "@turf/distance-weight": "7.4.0", + "@turf/ellipse": "7.4.0", + "@turf/envelope": "7.4.0", + "@turf/explode": "7.4.0", + "@turf/flatten": "7.4.0", + "@turf/flip": "7.4.0", + "@turf/geojson-rbush": "7.4.0", + "@turf/great-circle": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/hex-grid": "7.4.0", + "@turf/interpolate": "7.4.0", + "@turf/intersect": "7.4.0", + "@turf/invariant": "7.4.0", + "@turf/isobands": "7.4.0", + "@turf/isolines": "7.4.0", + "@turf/kinks": "7.4.0", + "@turf/length": "7.4.0", + "@turf/line-arc": "7.4.0", + "@turf/line-chunk": "7.4.0", + "@turf/line-intersect": "7.4.0", + "@turf/line-offset": "7.4.0", + "@turf/line-overlap": "7.4.0", + "@turf/line-segment": "7.4.0", + "@turf/line-slice": "7.4.0", + "@turf/line-slice-along": "7.4.0", + "@turf/line-split": "7.4.0", + "@turf/line-to-polygon": "7.4.0", + "@turf/mask": "7.4.0", + "@turf/meta": "7.4.0", + "@turf/midpoint": "7.4.0", + "@turf/moran-index": "7.4.0", + "@turf/nearest-neighbor-analysis": "7.4.0", + "@turf/nearest-point": "7.4.0", + "@turf/nearest-point-on-line": "7.4.0", + "@turf/nearest-point-to-line": "7.4.0", + "@turf/planepoint": "7.4.0", + "@turf/point-grid": "7.4.0", + "@turf/point-on-feature": "7.4.0", + "@turf/point-to-line-distance": "7.4.0", + "@turf/point-to-polygon-distance": "7.4.0", + "@turf/points-within-polygon": "7.4.0", + "@turf/polygon-smooth": "7.4.0", + "@turf/polygon-tangents": "7.4.0", + "@turf/polygon-to-line": "7.4.0", + "@turf/polygonize": "7.4.0", + "@turf/projection": "7.4.0", + "@turf/quadrat-analysis": "7.4.0", + "@turf/random": "7.4.0", + "@turf/rectangle-grid": "7.4.0", + "@turf/rewind": "7.4.0", + "@turf/rhumb-bearing": "7.4.0", + "@turf/rhumb-destination": "7.4.0", + "@turf/rhumb-distance": "7.4.0", + "@turf/sample": "7.4.0", + "@turf/sector": "7.4.0", + "@turf/shortest-path": "7.4.0", + "@turf/simplify": "7.4.0", + "@turf/square": "7.4.0", + "@turf/square-grid": "7.4.0", + "@turf/standard-deviational-ellipse": "7.4.0", + "@turf/tag": "7.4.0", + "@turf/tesselate": "7.4.0", + "@turf/tin": "7.4.0", + "@turf/transform-rotate": "7.4.0", + "@turf/transform-scale": "7.4.0", + "@turf/transform-translate": "7.4.0", + "@turf/triangle-grid": "7.4.0", + "@turf/truncate": "7.4.0", + "@turf/union": "7.4.0", + "@turf/unkink-polygon": "7.4.0", + "@turf/voronoi": "7.4.0", + "@types/geojson": "^7946.0.10", + "@types/kdbush": "^3.0.5", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/turf/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/union": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-7.4.0.tgz", + "integrity": "sha512-SYVtsCW9IznTyKlcGWxssRrf90p/hdpch1UeIcZc31pkSVdmeLOyuzPx0ef3UPmaF//fgXAphMph6JWA/FpKbA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/union/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/unkink-polygon": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-7.4.0.tgz", + "integrity": "sha512-GAQdiRmfR3Q64LoOqorr2Ec9eKoaVssSE1Rq34ZVTGmf2H49vjOPTXfTBp9ZVva4apVUkNLPPv3r4VaUHmABog==", + "license": "MIT", + "dependencies": { + "@turf/area": "7.4.0", + "@turf/boolean-point-in-polygon": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/meta": "7.4.0", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/unkink-polygon/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@turf/voronoi": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-7.4.0.tgz", + "integrity": "sha512-Uw/gyd0vuRonM/zCzgKqsb4Pt8gLl3oMPPscGVtmtyH0vsdlaZuaimJFC9Xdo9sWkLxn8eokhFnkJ3MZSl2UTA==", + "license": "MIT", + "dependencies": { + "@turf/clone": "7.4.0", + "@turf/helpers": "7.4.0", + "@turf/invariant": "7.4.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/@turf/voronoi/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", @@ -4848,6 +7600,12 @@ "dev": true, "license": "MIT" }, + "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/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -4855,6 +7613,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/earcut": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz", + "integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==", + "license": "MIT" + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -4893,6 +7657,12 @@ "@types/send": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -4932,6 +7702,12 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, + "node_modules/@types/kdbush": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/kdbush/-/kdbush-3.0.5.tgz", + "integrity": "sha512-tdJz7jaWFu4nR+8b2B+CdPZ6811ighYylWsu2hpsivapzW058yP0KdfZuNY89IiRe5jbKvBGXN3LQdN2KPXVdQ==", + "license": "MIT" + }, "node_modules/@types/lodash": { "version": "4.17.24", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", @@ -6060,6 +8836,14 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/arc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/arc/-/arc-0.2.0.tgz", + "integrity": "sha512-8NFOo126uYKQJyXNSLY/jSklgfLQL+XWAcPXGo876JwEQ8nSOPXWNI3TV2jLZMN8QEw8uksJ1ZwS4npjBca8MA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -6776,6 +9560,18 @@ "typedarray": "^0.0.6" } }, + "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/config": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/config/-/config-3.3.12.tgz", @@ -7100,6 +9896,30 @@ "node": ">= 8" } }, + "node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-geo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-2.0.2.tgz", + "integrity": "sha512-8pM1WGMLGFuhq9S+FpPURxic+gKzjluCD/CHTuUF3mXMeiCo0i6R0tO1s4+GArRFde96SLcW/kOFRjoAosPsFA==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^2.5.0" + } + }, + "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/date-fns": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", @@ -7238,6 +10058,12 @@ "node": ">= 0.4" } }, + "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", @@ -8304,6 +11130,39 @@ "node": ">=14" } }, + "node_modules/geojson-equality-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/geojson-equality-ts/-/geojson-equality-ts-1.0.2.tgz", + "integrity": "sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.14" + } + }, + "node_modules/geojson-polygon-self-intersections": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/geojson-polygon-self-intersections/-/geojson-polygon-self-intersections-1.2.2.tgz", + "integrity": "sha512-6XRNF4CsRHYmR9z5YuIk5f/aOototnDf0dgMqYGcS7y1l57ttt6MAIAxl3rXyas6lq1HEbTuLMh4PgvO+OV42w==", + "license": "MIT", + "dependencies": { + "rbush": "^2.0.1" + } + }, + "node_modules/geojson-polygon-self-intersections/node_modules/quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==", + "license": "ISC" + }, + "node_modules/geojson-polygon-self-intersections/node_modules/rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "license": "MIT", + "dependencies": { + "quickselect": "^1.0.1" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -8757,6 +11616,12 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -9160,6 +12025,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/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10980,6 +13854,37 @@ "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/point-in-polygon-hao/node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/polyclip-ts": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/polyclip-ts/-/polyclip-ts-0.16.8.tgz", + "integrity": "sha512-JPtKbDRuPEuAjuTdhR62Gph7Is2BS1Szx69CFOO3g71lpJDFo78k4tFyi+qFOMVPePEzdSKkpGU3NBXPHHjvKQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.1.0", + "splaytree-ts": "^1.0.2" + } + }, "node_modules/postcss": { "version": "8.5.19", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", @@ -11296,6 +14201,12 @@ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "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/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -11320,6 +14231,15 @@ "node": ">= 0.8" } }, + "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-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -11532,6 +14452,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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/rolldown": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", @@ -11910,6 +14836,12 @@ "node": ">=18" } }, + "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", @@ -11958,6 +14890,12 @@ "source-map": "^0.6.0" } }, + "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/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -12359,6 +15297,12 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", + "license": "ISC" + }, "node_modules/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", @@ -12391,6 +15335,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/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", diff --git a/package.json b/package.json index 4d9db38..76b94a0 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@map-colonies/tracing": "^1.0.0", "@map-colonies/tracing-utils": "^2.0.0", "@opentelemetry/api": "^1.9.1", + "@turf/turf": "^7.4.0", "axios": "^1.20.0", "compression": "^1.8.0", "express": "^4.21.2", diff --git a/src/common/config.ts b/src/common/config.ts index 7dfad2a..8d3292d 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -24,6 +24,10 @@ interface JobManagerConfig { }; } +interface ValidationConfig { + percentageLimit: number; +} + interface NFSConfig { pvPath: string; } @@ -44,6 +48,7 @@ type ProviderSource = 'NFS' | 'S3'; type OpsTriggerConfigType = commonBoilerplateV3Type & { externalServices: ExternalServicesConfig; jobManager: JobManagerConfig; + validation: ValidationConfig; provider: ProviderSource; // eslint-disable-next-line @typescript-eslint/naming-convention NFS: NFSConfig; @@ -60,9 +65,16 @@ const opsTriggerConfigSchema = { { $ref: commonBoilerplateV3.$id }, { type: 'object', - required: ['externalServices', 'jobManager', 'provider'], + required: ['externalServices', 'jobManager', 'validation', 'provider'], properties: { provider: { type: 'string', enum: ['NFS', 'S3'] }, + validation: { + type: 'object', + required: ['percentageLimit'], + properties: { + percentageLimit: { type: 'number' }, + }, + }, // eslint-disable-next-line @typescript-eslint/naming-convention NFS: { type: 'object', @@ -145,4 +157,4 @@ function getConfig(): ConfigType { } export { getConfig, initConfig }; -export type { ConfigType, LookupTablesConfig, ExternalServicesConfig, JobManagerConfig, NFSConfig, S3Config, ProviderSource }; +export type { ConfigType, LookupTablesConfig, ExternalServicesConfig, JobManagerConfig, ValidationConfig, NFSConfig, S3Config, ProviderSource }; diff --git a/src/tileset/tilesetReader.ts b/src/tileset/tilesetReader.ts new file mode 100644 index 0000000..365e616 --- /dev/null +++ b/src/tileset/tilesetReader.ts @@ -0,0 +1,108 @@ +import { readFile, open } from 'node:fs/promises'; +import { inflateRawSync } from 'node:zlib'; +import { inject, injectable } from 'tsyringe'; +import type { Logger } from '@map-colonies/js-logger'; +import { StatusCodes } from 'http-status-codes'; +import { SERVICES } from '@common/constants'; +import { AppError } from '@common/appError'; +import { is3tz, buildModelFilePath } from '@common/util'; +import type { LogContext } from '@common/interfaces'; + +const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50; +const GENERAL_PURPOSE_FLAG_OFFSET = 6; +const COMPRESSION_METHOD_OFFSET = 8; +const COMPRESSED_SIZE_OFFSET = 18; +const NAME_LENGTH_OFFSET = 26; +const EXTRA_LENGTH_OFFSET = 28; +const HEADER_FIXED_LENGTH = 30; +const COMPRESSION_STORED = 0; +const COMPRESSION_DEFLATED = 8; +const DATA_DESCRIPTOR_FLAG = 0x08; +const ZIP64_SIZE_SENTINEL = 0xffffffff; + +export const ERROR_TILESET_NOT_FIRST_ENTRY = 'tileset file is not the first entry of the 3tz archive'; +export const ERROR_TILESET_UNSUPPORTED_COMPRESSION = 'tileset entry uses an unsupported compression method'; +export const ERROR_TILESET_UNSUPPORTED_ENCODING = 'tileset entry size is not available in the local header (data descriptor or ZIP64)'; +export const ERROR_TILESET_READ_FAILED = 'failed reading the tileset file'; + +@injectable() +export class TilesetReader { + private readonly logContext: LogContext; + + public constructor(@inject(SERVICES.LOGGER) private readonly logger: Logger) { + this.logContext = { + fileName: __filename, + class: TilesetReader.name, + }; + } + + public async readTilesetJson(modelPath: string, tilesetFilename: string): Promise { + return is3tz(modelPath) ? this.readFromArchive(modelPath, tilesetFilename) : this.readFromFolder(modelPath, tilesetFilename); + } + + private async readFromFolder(modelPath: string, tilesetFilename: string): Promise { + const logContext = { ...this.logContext, function: this.readFromFolder.name }; + const path = buildModelFilePath(modelPath, tilesetFilename); + try { + return await readFile(path, 'utf-8'); + } catch (err) { + this.logger.error({ msg: 'failed reading tileset from folder', logContext, path, err }); + throw new AppError('tileset', StatusCodes.INTERNAL_SERVER_ERROR, ERROR_TILESET_READ_FAILED, false); + } + } + + private async readFromArchive(archivePath: string, tilesetFilename: string): Promise { + const logContext = { ...this.logContext, function: this.readFromArchive.name }; + let handle; + try { + handle = await open(archivePath, 'r'); + } catch (err) { + this.logger.error({ msg: 'failed opening 3tz archive', logContext, archivePath, err }); + throw new AppError('tileset', StatusCodes.INTERNAL_SERVER_ERROR, ERROR_TILESET_READ_FAILED, false); + } + + try { + const header = Buffer.alloc(HEADER_FIXED_LENGTH); + await handle.read(header, 0, HEADER_FIXED_LENGTH, 0); + + if (header.readUInt32LE(0) !== LOCAL_FILE_HEADER_SIGNATURE) { + throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_TILESET_NOT_FIRST_ENTRY, true); + } + + const flags = header.readUInt16LE(GENERAL_PURPOSE_FLAG_OFFSET); + const method = header.readUInt16LE(COMPRESSION_METHOD_OFFSET); + const compressedSize = header.readUInt32LE(COMPRESSED_SIZE_OFFSET); + const nameLength = header.readUInt16LE(NAME_LENGTH_OFFSET); + const extraLength = header.readUInt16LE(EXTRA_LENGTH_OFFSET); + + if ((flags & DATA_DESCRIPTOR_FLAG) !== 0 || compressedSize === 0 || compressedSize === ZIP64_SIZE_SENTINEL) { + throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_TILESET_UNSUPPORTED_ENCODING, true); + } + + const nameBuffer = Buffer.alloc(nameLength); + await handle.read(nameBuffer, 0, nameLength, HEADER_FIXED_LENGTH); + if (nameBuffer.toString('utf-8') !== tilesetFilename) { + this.logger.error({ + msg: 'first 3tz entry is not the expected tileset file', + logContext, + entryName: nameBuffer.toString('utf-8'), + tilesetFilename, + }); + throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_TILESET_NOT_FIRST_ENTRY, true); + } + + const raw = Buffer.alloc(compressedSize); + await handle.read(raw, 0, compressedSize, HEADER_FIXED_LENGTH + nameLength + extraLength); + + if (method === COMPRESSION_STORED) { + return raw.toString('utf-8'); + } + if (method === COMPRESSION_DEFLATED) { + return inflateRawSync(raw).toString('utf-8'); + } + throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_TILESET_UNSUPPORTED_COMPRESSION, true); + } finally { + await handle.close(); + } + } +} diff --git a/src/validator/validationManager.ts b/src/validator/validationManager.ts index 07edbf0..821af7d 100644 --- a/src/validator/validationManager.ts +++ b/src/validator/validationManager.ts @@ -1,18 +1,24 @@ import type { Logger } from '@map-colonies/js-logger'; import { StatusCodes } from 'http-status-codes'; import { inject, injectable } from 'tsyringe'; -import { new3DLayerMetadataSchema, geometrySchema } from '@map-colonies/3d-shared'; +import { new3DLayerMetadataSchema, geometrySchema, calculatePolygonFromTileset, type TileSetJson } from '@map-colonies/3d-shared'; +import { area, feature, featureCollection, intersect, union } from '@turf/turf'; +import type { Feature, MultiPolygon, Polygon } from 'geojson'; import { SERVICES } from '@common/constants'; import type { LogContext } from '@common/interfaces'; import { AppError } from '@common/appError'; import { buildModelFilePath } from '@common/util'; +import type { ConfigType } from '@common/config'; import { LookupTablesCall } from '../externalServices/lookupTables/lookupTablesCall'; import { CatalogCall } from '../externalServices/catalog/catalogCall'; import { JobnikClient } from '../externalServices/jobnik/jobnikClient'; +import { TilesetReader } from '../tileset/tilesetReader'; import type { Record3D } from '../externalServices/catalog/interfaces'; import type { Provider } from '../providers/interfaces'; import type { IngestionPayload } from '../record/models/recordManager'; +const FULL_COVERAGE_PERCENT = 100; + const BLOCKED_DELETE_PRODUCT_TYPE = 'QuantizedMeshDTMBest'; const RECORD_STATUS_UNPUBLISHED = 'UNPUBLISHED'; @@ -26,18 +32,25 @@ export const ERROR_DELETE_RECORD_NOT_FOUND = "recordId doesn't match exactly one export const ERROR_DELETE_PRODUCT_TYPE = 'Cannot delete a record whose productType is "QuantizedMeshDTMBest"'; export const ERROR_DELETE_STATUS = 'Cannot delete a record whose productStatus is not "UNPUBLISHED"'; export const ERROR_FILE_NOT_FOUND = 'The model files do not exist in the agreed storage'; +export const ERROR_TILESET_INVALID = 'The tileset file is not a valid 3DTiles tileset'; +export const ERROR_FOOTPRINT_FAR_FROM_MODEL = 'Invalid footprint! The footprint does not intersect the tileset model'; +export const ERROR_FOOTPRINT_COVERAGE = 'The footprint coverage of the tileset model is below the required threshold'; @injectable() export class ValidationManager { private readonly logContext: LogContext; + private readonly percentageLimit: number; public constructor( + @inject(SERVICES.CONFIG) private readonly config: ConfigType, @inject(SERVICES.LOGGER) private readonly logger: Logger, @inject(LookupTablesCall) private readonly lookupTables: LookupTablesCall, @inject(CatalogCall) private readonly catalog: CatalogCall, @inject(JobnikClient) private readonly jobnik: JobnikClient, + @inject(TilesetReader) private readonly tilesetReader: TilesetReader, @inject(SERVICES.PROVIDER) private readonly provider: Provider ) { + this.percentageLimit = this.config.get('validation.percentageLimit'); this.logContext = { fileName: __filename, class: ValidationManager.name, @@ -66,6 +79,7 @@ export class ValidationManager { await this.validateProductNameUnique(parsed.data.productName); await this.validateProductNameNotInFlight(parsed.data.productName); await this.validateFileExists(payload.modelPath, payload.tilesetFilename); + await this.validateTileset(payload, footprintResult.data); } public async validateDelete(recordId: string): Promise { @@ -118,6 +132,48 @@ export class ValidationManager { } } + private async validateTileset(payload: IngestionPayload, footprint: Polygon | MultiPolygon): Promise { + const logContext = { ...this.logContext, function: this.validateTileset.name }; + const content = await this.tilesetReader.readTilesetJson(payload.modelPath, payload.tilesetFilename); + + let modelPolygon: Polygon; + try { + const tilesetJson = JSON.parse(content) as TileSetJson; + modelPolygon = calculatePolygonFromTileset(tilesetJson); + } catch (err) { + const message = err instanceof Error ? err.message : ERROR_TILESET_INVALID; + this.logger.error({ msg: 'tileset polygon extraction failed', logContext, modelPath: payload.modelPath, err }); + throw new AppError('badRequest', StatusCodes.BAD_REQUEST, message, true); + } + + this.validateFootprintCoverage(footprint, modelPolygon); + } + + private validateFootprintCoverage(footprint: Polygon | MultiPolygon, modelPolygon: Polygon): void { + const logContext = { ...this.logContext, function: this.validateFootprintCoverage.name }; + const footprintFeature = feature(footprint); + const modelFeature: Feature = feature(modelPolygon); + + const intersection = intersect(featureCollection([footprintFeature, modelFeature])); + if (intersection === null) { + throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_FOOTPRINT_FAR_FROM_MODEL, true); + } + + const combined = union(featureCollection([footprintFeature, modelFeature])); + const combinedArea = combined === null ? 0 : area(combined); + const coverage = combinedArea === 0 ? 0 : (FULL_COVERAGE_PERCENT * area(footprintFeature)) / combinedArea; + this.logger.debug({ msg: 'footprint coverage validation', logContext, coverage, percentageLimit: this.percentageLimit }); + + if (coverage < this.percentageLimit) { + throw new AppError( + 'badRequest', + StatusCodes.BAD_REQUEST, + `${ERROR_FOOTPRINT_COVERAGE}. coverage: ${coverage}%, minimum: ${this.percentageLimit}%`, + true + ); + } + } + private validateDates(start: unknown, end: unknown): void { if (start === undefined || end === undefined) { throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_METADATA_MISSING_DATE, true); diff --git a/tests/helpers/metadata.ts b/tests/helpers/metadata.ts index 44d1527..c319f46 100644 --- a/tests/helpers/metadata.ts +++ b/tests/helpers/metadata.ts @@ -17,9 +17,11 @@ export const buildValidMetadata = (): Record => ({ type: 'Polygon', coordinates: [ [ - [34.45, 31.48], - [34.46, 31.48], - [34.45, 31.48], + [35.17, 32.9], + [35.18, 32.9], + [35.18, 32.91], + [35.17, 32.91], + [35.17, 32.9], ], ], }, diff --git a/tests/helpers/tilesets/folder/tileset.json b/tests/helpers/tilesets/folder/tileset.json new file mode 100644 index 0000000..c9d2f7d --- /dev/null +++ b/tests/helpers/tilesets/folder/tileset.json @@ -0,0 +1,35 @@ +{ + "asset": { "version": "1.1" }, + "geometricError": 500.0, + "root": { + "boundingVolume": { "region": [0.613745031463806, 0.5741784173210945, 0.614094097314205, 0.5744402167088937, -10.0, 200.0] }, + "geometricError": 500.0, + "refine": "ADD", + "children": [ + { + "boundingVolume": { "region": [0.613745031463806, 0.5741784173210945, 0.614094097314205, 0.5744402167088937, -10.0, 200.0] }, + "geometricError": 500.0, + "refine": "ADD", + "content": { "uri": "./terrain/tileset.json" } + }, + { + "boundingVolume": { "region": [0.613745031463806, 0.5741784173210945, 0.614094097314205, 0.5744402167088937, -10.0, 200.0] }, + "geometricError": 500.0, + "refine": "ADD", + "content": { "uri": "./buildings/tileset.json" } + }, + { + "boundingVolume": { "region": [0.613745031463806, 0.5741784173210945, 0.614094097314205, 0.5744402167088937, -10.0, 200.0] }, + "geometricError": 500.0, + "refine": "ADD", + "content": { "uri": "./roof_furniture/tileset.json" } + }, + { + "boundingVolume": { "region": [0.613745031463806, 0.5741784173210945, 0.614094097314205, 0.5744402167088937, -10.0, 200.0] }, + "geometricError": 500.0, + "refine": "ADD", + "content": { "uri": "./trees/tileset.json" } + } + ] + } +} diff --git a/tests/helpers/tilesets/region.3tz b/tests/helpers/tilesets/region.3tz new file mode 100644 index 0000000000000000000000000000000000000000..88714e6eb669b9f3c5130603a69efabb11843b99 GIT binary patch literal 1362 zcmds1KTiTN6h8q?BqRsd7(x`df;G@4y*n`>#26>P00 z2p0!GfV+vSU&GhCLB!||y}b10_4j(eUtjxLjdUifsd0CH(7~t8x8sbaDa%1DWFs2T z(6|f)pQ3I6K04H==!(jK2@XCY20^pDj)qi>X((C0CZ&+DQpVW8upos&#}^Z0kq+dG>O< z`_Oij?SNHGKfb+n?$c{hd3pGF`mEhI>+@*ueLmR+L_AuJjZ`81cNa@gy7qXhyv6sh eoViTkJM#uQ;u4)!V{=OZQt(Y+eg%l8WxoInrCgi< literal 0 HcmV?d00001 diff --git a/tests/integration/record/record.spec.ts b/tests/integration/record/record.spec.ts index f640d2c..dcdbf34 100644 --- a/tests/integration/record/record.spec.ts +++ b/tests/integration/record/record.spec.ts @@ -1,3 +1,5 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { jsLogger } from '@map-colonies/js-logger'; import { describe, beforeEach, it, expect, beforeAll, vi } from 'vitest'; import { trace } from '@opentelemetry/api'; @@ -10,8 +12,11 @@ import { initConfig } from '@src/common/config'; import { LookupTablesCall } from '@src/externalServices/lookupTables/lookupTablesCall'; import { CatalogCall } from '@src/externalServices/catalog/catalogCall'; import { JobnikClient } from '@src/externalServices/jobnik/jobnikClient'; +import { TilesetReader } from '@src/tileset/tilesetReader'; import { buildValidMetadata } from '@tests/helpers/metadata'; +const regionTilesetJson = readFileSync(join(__dirname, '../../helpers/tilesets/folder/tileset.json'), 'utf-8'); + const deletableRecord = { id: 'rec-1', productId: 'p-1', @@ -32,6 +37,7 @@ const jobnikStub = { createDeleteJob: vi.fn().mockResolvedValue({ jobId: 'del-1', status: 'PENDING' }), hasInFlightIngestionJob: vi.fn().mockResolvedValue(false), } as unknown as JobnikClient; +const tilesetReaderStub = { readTilesetJson: vi.fn().mockResolvedValue(regionTilesetJson) } as unknown as TilesetReader; const providerStub = { fileExists: vi.fn().mockResolvedValue(true) }; const validMetadata = buildValidMetadata(); @@ -57,6 +63,7 @@ describe('record', function () { { token: LookupTablesCall, provider: { useValue: lookupStub } }, { token: CatalogCall, provider: { useValue: catalogStub } }, { token: JobnikClient, provider: { useValue: jobnikStub } }, + { token: TilesetReader, provider: { useValue: tilesetReaderStub } }, { token: SERVICES.PROVIDER, provider: { useValue: providerStub } }, ], useChild: true, diff --git a/tests/unit/tileset/tilesetReader.spec.ts b/tests/unit/tileset/tilesetReader.spec.ts new file mode 100644 index 0000000..42cb6fb --- /dev/null +++ b/tests/unit/tileset/tilesetReader.spec.ts @@ -0,0 +1,77 @@ +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { writeFile, mkdtemp, rm } from 'node:fs/promises'; +import { describe, it, expect, beforeEach } from 'vitest'; +import { jsLogger } from '@map-colonies/js-logger'; +import { + TilesetReader, + ERROR_TILESET_NOT_FIRST_ENTRY, + ERROR_TILESET_READ_FAILED, + ERROR_TILESET_UNSUPPORTED_ENCODING, +} from '@src/tileset/tilesetReader'; +import { AppError } from '@src/common/appError'; + +const fixtures = join(__dirname, '../../helpers/tilesets'); + +const buildLocalHeader = (name: string, flags: number): Buffer => { + const nameBuffer = Buffer.from(name, 'utf-8'); + const header = Buffer.alloc(30 + nameBuffer.length); + header.writeUInt32LE(0x04034b50, 0); + header.writeUInt16LE(flags, 6); + header.writeUInt16LE(0, 8); + header.writeUInt32LE(0, 18); + header.writeUInt16LE(nameBuffer.length, 26); + header.writeUInt16LE(0, 28); + nameBuffer.copy(header, 30); + return header; +}; + +describe('TilesetReader', function () { + let reader: TilesetReader; + + beforeEach(async function () { + reader = new TilesetReader(await jsLogger({ enabled: false })); + }); + + it('should read tileset.json from the first entry of a 3tz archive.', async function () { + const content = await reader.readTilesetJson(join(fixtures, 'region.3tz'), 'tileset.json'); + const parsed = JSON.parse(content) as { root: { boundingVolume: { region?: number[] } } }; + + expect(parsed.root.boundingVolume.region).toBeDefined(); + }); + + it('should read tileset.json from a folder input.', async function () { + const content = await reader.readTilesetJson(join(fixtures, 'folder'), 'tileset.json'); + const parsed = JSON.parse(content) as { root: { boundingVolume: { region?: number[] } } }; + + expect(parsed.root.boundingVolume.region).toBeDefined(); + }); + + it('should throw a 400 when the requested entry is not the first entry of the archive.', async function () { + await expect(reader.readTilesetJson(join(fixtures, 'region.3tz'), 'other.json')).rejects.toThrow(ERROR_TILESET_NOT_FIRST_ENTRY); + }); + + it('should throw a 400 when the archive entry uses a data descriptor (size absent from local header).', async function () { + const dir = await mkdtemp(join(tmpdir(), 'tileset-reader-')); + const archive = join(dir, 'descriptor.3tz'); + await writeFile(archive, buildLocalHeader('tileset.json', 0x08)); + + try { + await expect(reader.readTilesetJson(archive, 'tileset.json')).rejects.toThrow(ERROR_TILESET_UNSUPPORTED_ENCODING); + } finally { + await rm(dir, { recursive: true, force: true }); + } + }); + + it('should throw when the folder tileset file is missing.', async function () { + let thrown: unknown; + try { + await reader.readTilesetJson(join(fixtures, 'folder'), 'missing.json'); + } catch (err) { + thrown = err; + } + + expect(thrown).toBeInstanceOf(AppError); + expect((thrown as AppError).message).toBe(ERROR_TILESET_READ_FAILED); + }); +}); diff --git a/tests/unit/validator/validationManager.spec.ts b/tests/unit/validator/validationManager.spec.ts index d88ae0c..01c965f 100644 --- a/tests/unit/validator/validationManager.spec.ts +++ b/tests/unit/validator/validationManager.spec.ts @@ -1,3 +1,5 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { jsLogger } from '@map-colonies/js-logger'; import { describe, it, expect, beforeEach, vi } from 'vitest'; import { StatusCodes } from 'http-status-codes'; @@ -12,16 +14,23 @@ import { ERROR_DELETE_STATUS, ERROR_FILE_NOT_FOUND, ERROR_PRODUCT_NAME_IN_FLIGHT, + ERROR_FOOTPRINT_FAR_FROM_MODEL, } from '@src/validator/validationManager'; import { AppError } from '@src/common/appError'; +import type { ConfigType } from '@src/common/config'; import type { LookupTablesCall } from '@src/externalServices/lookupTables/lookupTablesCall'; import type { CatalogCall } from '@src/externalServices/catalog/catalogCall'; import type { JobnikClient } from '@src/externalServices/jobnik/jobnikClient'; +import type { TilesetReader } from '@src/tileset/tilesetReader'; import type { Provider } from '@src/providers/interfaces'; import type { IngestionPayload } from '@src/record/models/recordManager'; import { buildValidMetadata } from '@tests/helpers/metadata'; +const regionTilesetJson = readFileSync(join(__dirname, '../../helpers/tilesets/folder/tileset.json'), 'utf-8'); +const boxTilesetJson = JSON.stringify({ root: { boundingVolume: { box: [0, 0, 0, 100, 0, 0, 0, 100, 0, 0, 0, 100] } } }); + const lookupStub = { getClassifications: vi.fn().mockResolvedValue(['abc123']) } as unknown as LookupTablesCall; +const configStub = { get: vi.fn().mockReturnValue(10) } as unknown as ConfigType; const ingest = (metadata: Record): IngestionPayload => ({ modelPath: '/shared/models/afula', @@ -33,13 +42,23 @@ describe('ValidationManager', function () { let validator: ValidationManager; let catalogStub: CatalogCall; let jobnikStub: JobnikClient; + let tilesetReaderStub: TilesetReader; let providerStub: Provider; beforeEach(async function () { catalogStub = { findRecords: vi.fn().mockResolvedValue([]) } as unknown as CatalogCall; jobnikStub = { hasInFlightIngestionJob: vi.fn().mockResolvedValue(false) } as unknown as JobnikClient; + tilesetReaderStub = { readTilesetJson: vi.fn().mockResolvedValue(regionTilesetJson) } as unknown as TilesetReader; providerStub = { fileExists: vi.fn().mockResolvedValue(true) }; - validator = new ValidationManager(await jsLogger({ enabled: false }), lookupStub, catalogStub, jobnikStub, providerStub); + validator = new ValidationManager( + configStub, + await jsLogger({ enabled: false }), + lookupStub, + catalogStub, + jobnikStub, + tilesetReaderStub, + providerStub + ); }); it('should pass a fully valid ingestion payload', async function () { @@ -125,6 +144,38 @@ describe('ValidationManager', function () { await expect(validator.validateIngestion(ingest(buildValidMetadata()))).rejects.toThrow(ERROR_FILE_NOT_FOUND); }); + it('should throw 400 when the footprint does not intersect the tileset model', async function () { + const metadata = buildValidMetadata(); + metadata.footprint = { + type: 'Polygon', + coordinates: [ + [ + [10, 10], + [10.01, 10], + [10.01, 10.01], + [10, 10.01], + [10, 10], + ], + ], + }; + + await expect(validator.validateIngestion(ingest(metadata))).rejects.toThrow(ERROR_FOOTPRINT_FAR_FROM_MODEL); + }); + + it('should throw 400 when the tileset bounding volume is an unsupported box', async function () { + (tilesetReaderStub.readTilesetJson as ReturnType).mockResolvedValueOnce(boxTilesetJson); + + let thrown: unknown; + try { + await validator.validateIngestion(ingest(buildValidMetadata())); + } catch (err) { + thrown = err; + } + + expect(thrown).toBeInstanceOf(AppError); + expect((thrown as AppError).status).toBe(StatusCodes.BAD_REQUEST); + }); + describe('validateDelete', function () { it('should return the record when it is deletable', async function () { const record = { id: 'rec-1', productType: '3DPhotoRealistic', productStatus: 'UNPUBLISHED' }; From 69d89f28ff13e12002b7f836c20698326bdae73e Mon Sep 17 00:00:00 2001 From: TULCHINSKI LIRAN Date: Wed, 30 Sep 2026 13:07:17 +0300 Subject: [PATCH 2/2] refactor: gateway-style tileset validation result (MAPCO-11837) Return ValidationResponse {isValid, message} from the tileset polygon extraction and footprint-intersection checks (mirroring 3d-gateway), route every failure through a single rejectValidation() that logs the outcome and throws AppError so the message reaches the client. Move ValidationResponse to common/interfaces. Match 3d-gateway's coverage/far-from-model messages and add a coverage-below-threshold test. --- src/common/interfaces.ts | 5 ++ src/validator/validationManager.ts | 86 ++++++++++++------- .../unit/validator/validationManager.spec.ts | 27 ++++++ 3 files changed, 88 insertions(+), 30 deletions(-) diff --git a/src/common/interfaces.ts b/src/common/interfaces.ts index 1f49da0..6220fde 100644 --- a/src/common/interfaces.ts +++ b/src/common/interfaces.ts @@ -8,3 +8,8 @@ export interface LogContext { class: string; function?: string; } + +export interface ValidationResponse { + isValid: boolean; + message?: string; +} diff --git a/src/validator/validationManager.ts b/src/validator/validationManager.ts index 821af7d..3dfdbf6 100644 --- a/src/validator/validationManager.ts +++ b/src/validator/validationManager.ts @@ -5,7 +5,7 @@ import { new3DLayerMetadataSchema, geometrySchema, calculatePolygonFromTileset, import { area, feature, featureCollection, intersect, union } from '@turf/turf'; import type { Feature, MultiPolygon, Polygon } from 'geojson'; import { SERVICES } from '@common/constants'; -import type { LogContext } from '@common/interfaces'; +import type { LogContext, ValidationResponse } from '@common/interfaces'; import { AppError } from '@common/appError'; import { buildModelFilePath } from '@common/util'; import type { ConfigType } from '@common/config'; @@ -33,8 +33,8 @@ export const ERROR_DELETE_PRODUCT_TYPE = 'Cannot delete a record whose productTy export const ERROR_DELETE_STATUS = 'Cannot delete a record whose productStatus is not "UNPUBLISHED"'; export const ERROR_FILE_NOT_FOUND = 'The model files do not exist in the agreed storage'; export const ERROR_TILESET_INVALID = 'The tileset file is not a valid 3DTiles tileset'; -export const ERROR_FOOTPRINT_FAR_FROM_MODEL = 'Invalid footprint! The footprint does not intersect the tileset model'; -export const ERROR_FOOTPRINT_COVERAGE = 'The footprint coverage of the tileset model is below the required threshold'; +export const ERROR_FOOTPRINT_FAR_FROM_MODEL = "Wrong footprint! footprint's coordinates is not even close to the model!"; +export const ERROR_INTERSECTION_FAILED = 'An error caused during the validation of the intersection'; @injectable() export class ValidationManager { @@ -95,6 +95,7 @@ export class ValidationManager { if (record.productType === BLOCKED_DELETE_PRODUCT_TYPE) { throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_DELETE_PRODUCT_TYPE, true); } + if (record.productStatus !== RECORD_STATUS_UNPUBLISHED) { throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_DELETE_STATUS, true); } @@ -107,6 +108,7 @@ export class ValidationManager { const path = buildModelFilePath(modelPath, tilesetFilename); const exists = await this.provider.fileExists(path); this.logger.debug({ msg: 'file existence validation', logContext, path, exists }); + if (!exists) { throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_FILE_NOT_FOUND, true); } @@ -133,45 +135,69 @@ export class ValidationManager { } private async validateTileset(payload: IngestionPayload, footprint: Polygon | MultiPolygon): Promise { - const logContext = { ...this.logContext, function: this.validateTileset.name }; const content = await this.tilesetReader.readTilesetJson(payload.modelPath, payload.tilesetFilename); - let modelPolygon: Polygon; + const { modelPolygon, response: polygonResponse } = this.getTilesetModelPolygon(content); + if (!polygonResponse.isValid || modelPolygon === undefined) { + this.rejectValidation(polygonResponse); + } + + const intersectionResponse = this.isFootprintAndModelIntersects(footprint, modelPolygon); + if (!intersectionResponse.isValid) { + this.rejectValidation(intersectionResponse); + } + } + + private getTilesetModelPolygon(content: string): { modelPolygon?: Polygon; response: ValidationResponse } { + const logContext = { ...this.logContext, function: this.getTilesetModelPolygon.name }; try { const tilesetJson = JSON.parse(content) as TileSetJson; - modelPolygon = calculatePolygonFromTileset(tilesetJson); + const modelPolygon = calculatePolygonFromTileset(tilesetJson); + this.logger.debug({ msg: 'extracted tileset model polygon', logContext, modelPolygon }); + + return { modelPolygon, response: { isValid: true } }; } catch (err) { const message = err instanceof Error ? err.message : ERROR_TILESET_INVALID; - this.logger.error({ msg: 'tileset polygon extraction failed', logContext, modelPath: payload.modelPath, err }); - throw new AppError('badRequest', StatusCodes.BAD_REQUEST, message, true); + this.logger.error({ msg: 'tileset polygon extraction failed', logContext, err }); + return { response: { isValid: false, message } }; } - - this.validateFootprintCoverage(footprint, modelPolygon); } - private validateFootprintCoverage(footprint: Polygon | MultiPolygon, modelPolygon: Polygon): void { - const logContext = { ...this.logContext, function: this.validateFootprintCoverage.name }; - const footprintFeature = feature(footprint); - const modelFeature: Feature = feature(modelPolygon); - - const intersection = intersect(featureCollection([footprintFeature, modelFeature])); - if (intersection === null) { - throw new AppError('badRequest', StatusCodes.BAD_REQUEST, ERROR_FOOTPRINT_FAR_FROM_MODEL, true); + private isFootprintAndModelIntersects(footprint: Polygon | MultiPolygon, modelPolygon: Polygon): ValidationResponse { + const logContext = { ...this.logContext, function: this.isFootprintAndModelIntersects.name }; + try { + const footprintFeature = feature(footprint); + const modelFeature: Feature = feature(modelPolygon); + + const intersection = intersect(featureCollection([footprintFeature, modelFeature])); + if (intersection === null) { + return { isValid: false, message: ERROR_FOOTPRINT_FAR_FROM_MODEL }; + } + + const combined = union(featureCollection([footprintFeature, modelFeature])); + const combinedArea = combined === null ? 0 : area(combined); + const coverage = combinedArea === 0 ? 0 : (FULL_COVERAGE_PERCENT * area(footprintFeature)) / combinedArea; + this.logger.debug({ msg: 'calculated footprint coverage of the model', logContext, coverage, percentageLimit: this.percentageLimit }); + + if (coverage < this.percentageLimit) { + return { + isValid: false, + message: `The footprint intersectection with the model doesn't reach minimum required threshhold, the coverage is: ${coverage}% when the minimum coverage is ${this.percentageLimit}%`, + }; + } + return { isValid: true }; + } catch (err) { + this.logger.error({ msg: ERROR_INTERSECTION_FAILED, logContext, err }); + return { isValid: false, message: ERROR_INTERSECTION_FAILED }; } + } - const combined = union(featureCollection([footprintFeature, modelFeature])); - const combinedArea = combined === null ? 0 : area(combined); - const coverage = combinedArea === 0 ? 0 : (FULL_COVERAGE_PERCENT * area(footprintFeature)) / combinedArea; - this.logger.debug({ msg: 'footprint coverage validation', logContext, coverage, percentageLimit: this.percentageLimit }); + private rejectValidation(response: ValidationResponse): never { + const logContext = { ...this.logContext, function: this.rejectValidation.name }; + const message = response.message ?? ERROR_TILESET_INVALID; + this.logger.warn({ msg: 'ingestion validation failed', logContext, isValid: response.isValid, message }); - if (coverage < this.percentageLimit) { - throw new AppError( - 'badRequest', - StatusCodes.BAD_REQUEST, - `${ERROR_FOOTPRINT_COVERAGE}. coverage: ${coverage}%, minimum: ${this.percentageLimit}%`, - true - ); - } + throw new AppError('badRequest', StatusCodes.BAD_REQUEST, message, true); } private validateDates(start: unknown, end: unknown): void { diff --git a/tests/unit/validator/validationManager.spec.ts b/tests/unit/validator/validationManager.spec.ts index 01c965f..18ef9aa 100644 --- a/tests/unit/validator/validationManager.spec.ts +++ b/tests/unit/validator/validationManager.spec.ts @@ -162,6 +162,33 @@ describe('ValidationManager', function () { await expect(validator.validateIngestion(ingest(metadata))).rejects.toThrow(ERROR_FOOTPRINT_FAR_FROM_MODEL); }); + it('should throw 400 when the footprint coverage of the model is below the threshold', async function () { + const metadata = buildValidMetadata(); + metadata.footprint = { + type: 'Polygon', + coordinates: [ + [ + [35.17, 32.9], + [35.172, 32.9], + [35.172, 32.902], + [35.17, 32.902], + [35.17, 32.9], + ], + ], + }; + + let thrown: unknown; + try { + await validator.validateIngestion(ingest(metadata)); + } catch (err) { + thrown = err; + } + + expect(thrown).toBeInstanceOf(AppError); + expect((thrown as AppError).status).toBe(StatusCodes.BAD_REQUEST); + expect((thrown as AppError).message).toMatch(/minimum required threshhold/); + }); + it('should throw 400 when the tileset bounding volume is an unsupported box', async function () { (tilesetReaderStub.readTilesetJson as ReturnType).mockResolvedValueOnce(boxTilesetJson);