From 2bdbb583a8903e9f98a39376808590636e3d8933 Mon Sep 17 00:00:00 2001 From: Kilian Hofmann Date: Sun, 13 Jul 2025 22:53:20 +0200 Subject: [PATCH] Begin Refactor + Webapp --- .vscode/launch.json | 20 + browser/.gitignore | 24 + browser/README.md | 69 + browser/eslint.config.js | 23 + browser/index.html | 13 + browser/package.json | 38 + browser/pnpm-lock.yaml | 1931 +++++++++++++++++ browser/src/App.tsx | 45 + browser/src/index.css | 8 + browser/src/main.tsx | 12 + browser/src/parser/node.ts | 16 + browser/src/parser/parser.ts | 148 ++ browser/src/parser/terminators/AF.ts | 1 + .../src/parser/utils/computeIntersection.ts | 89 + browser/src/parser/utils/extensions.ts | 27 + .../parser/utils/generatePerformanceArc.ts | 54 + .../src/parser/utils/generateTangentArc.ts | 105 + browser/src/parser/utils/handleTurnAtFix.ts | 68 + browser/src/types/extensions.d.ts | 29 + browser/src/types/geojson.d.ts | 3 + browser/src/types/magvar.d.ts | 3 + browser/src/types/terminators/AF.d.ts | 18 + browser/src/types/terminators/CF.d.ts | 18 + browser/src/types/types.d.ts | 91 + browser/src/vite-env.d.ts | 1 + browser/tsconfig.app.json | 28 + browser/tsconfig.json | 7 + browser/tsconfig.node.json | 26 + browser/vite.config.ts | 7 + 29 files changed, 2922 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 browser/.gitignore create mode 100644 browser/README.md create mode 100644 browser/eslint.config.js create mode 100644 browser/index.html create mode 100644 browser/package.json create mode 100644 browser/pnpm-lock.yaml create mode 100644 browser/src/App.tsx create mode 100644 browser/src/index.css create mode 100644 browser/src/main.tsx create mode 100644 browser/src/parser/node.ts create mode 100644 browser/src/parser/parser.ts create mode 100644 browser/src/parser/terminators/AF.ts create mode 100644 browser/src/parser/utils/computeIntersection.ts create mode 100644 browser/src/parser/utils/extensions.ts create mode 100644 browser/src/parser/utils/generatePerformanceArc.ts create mode 100644 browser/src/parser/utils/generateTangentArc.ts create mode 100644 browser/src/parser/utils/handleTurnAtFix.ts create mode 100644 browser/src/types/extensions.d.ts create mode 100644 browser/src/types/geojson.d.ts create mode 100644 browser/src/types/magvar.d.ts create mode 100644 browser/src/types/terminators/AF.d.ts create mode 100644 browser/src/types/terminators/CF.d.ts create mode 100644 browser/src/types/types.d.ts create mode 100644 browser/src/vite-env.d.ts create mode 100644 browser/tsconfig.app.json create mode 100644 browser/tsconfig.json create mode 100644 browser/tsconfig.node.json create mode 100644 browser/vite.config.ts diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..795f5d7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Parser Node", + "skipFiles": ["/**"], + "args": [ + "--experimental-strip-types", + "${workspaceFolder}\\browser\\src\\parser\\node.ts" + ], + "cwd": "${workspaceFolder}\\browser\\", + "outFiles": ["${workspaceFolder}/**/*.js"] + } + ] +} diff --git a/browser/.gitignore b/browser/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/browser/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/browser/README.md b/browser/README.md new file mode 100644 index 0000000..7959ce4 --- /dev/null +++ b/browser/README.md @@ -0,0 +1,69 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/browser/eslint.config.js b/browser/eslint.config.js new file mode 100644 index 0000000..d94e7de --- /dev/null +++ b/browser/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { globalIgnores } from 'eslint/config' + +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/browser/index.html b/browser/index.html new file mode 100644 index 0000000..180865d --- /dev/null +++ b/browser/index.html @@ -0,0 +1,13 @@ + + + + + + + MD-11 NavData Browser + + +
+ + + diff --git a/browser/package.json b/browser/package.json new file mode 100644 index 0000000..d572eea --- /dev/null +++ b/browser/package.json @@ -0,0 +1,38 @@ +{ + "name": "browser", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "parser": "node --experimental-strip-types src/parser/node.ts" + }, + "dependencies": { + "geojson": "^0.5.0", + "geolib": "^3.3.4", + "leaflet": "^1.9.4", + "magvar": "^2.0.0", + "react": "19.0.0-rc.1", + "react-dom": "19.0.0-rc.1", + "react-leaflet": "5.0.0-rc.2" + }, + "devDependencies": { + "@eslint/js": "^9.30.1", + "@types/leaflet": "^1.9.20", + "@types/object-hash": "^3.0.6", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react-swc": "^3.10.2", + "eslint": "^9.30.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "object-hash": "^3.0.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.35.1", + "vite": "^7.0.4" + } +} diff --git a/browser/pnpm-lock.yaml b/browser/pnpm-lock.yaml new file mode 100644 index 0000000..85ae983 --- /dev/null +++ b/browser/pnpm-lock.yaml @@ -0,0 +1,1931 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + geojson: + specifier: ^0.5.0 + version: 0.5.0 + geolib: + specifier: ^3.3.4 + version: 3.3.4 + leaflet: + specifier: ^1.9.4 + version: 1.9.4 + magvar: + specifier: ^2.0.0 + version: 2.0.0 + object-hash: + specifier: ^3.0.0 + version: 3.0.0 + react: + specifier: 19.0.0-rc.1 + version: 19.0.0-rc.1 + react-dom: + specifier: 19.0.0-rc.1 + version: 19.0.0-rc.1(react@19.0.0-rc.1) + react-leaflet: + specifier: 5.0.0-rc.2 + version: 5.0.0-rc.2(leaflet@1.9.4)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1) + devDependencies: + '@eslint/js': + specifier: ^9.30.1 + version: 9.31.0 + '@types/leaflet': + specifier: ^1.9.20 + version: 1.9.20 + '@types/object-hash': + specifier: ^3.0.6 + version: 3.0.6 + '@types/react': + specifier: ^19.1.8 + version: 19.1.8 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.1.6(@types/react@19.1.8) + '@vitejs/plugin-react-swc': + specifier: ^3.10.2 + version: 3.10.2(vite@7.0.4) + eslint: + specifier: ^9.30.1 + version: 9.31.0 + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.31.0) + eslint-plugin-react-refresh: + specifier: ^0.4.20 + version: 0.4.20(eslint@9.31.0) + globals: + specifier: ^16.3.0 + version: 16.3.0 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: ^8.35.1 + version: 8.36.0(eslint@9.31.0)(typescript@5.8.3) + vite: + specifier: ^7.0.4 + version: 7.0.4 + +packages: + + '@esbuild/aix-ppc64@0.25.6': + resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.6': + resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.6': + resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.6': + resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.6': + resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.6': + resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.6': + resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.6': + resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.6': + resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.6': + resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.6': + resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.6': + resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.6': + resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.6': + resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.6': + resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.6': + resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.6': + resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.6': + resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.6': + resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.6': + resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.6': + resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.6': + resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.6': + resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.6': + resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.6': + resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.6': + resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.0': + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.31.0': + resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.3': + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@react-leaflet/core@3.0.0': + resolution: {integrity: sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==} + peerDependencies: + leaflet: ^1.9.0 + react: ^19.0.0 + react-dom: ^19.0.0 + + '@rolldown/pluginutils@1.0.0-beta.11': + resolution: {integrity: sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==} + + '@rollup/rollup-android-arm-eabi@4.45.0': + resolution: {integrity: sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.45.0': + resolution: {integrity: sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.45.0': + resolution: {integrity: sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.45.0': + resolution: {integrity: sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.45.0': + resolution: {integrity: sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.45.0': + resolution: {integrity: sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + resolution: {integrity: sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + resolution: {integrity: sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + resolution: {integrity: sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.45.0': + resolution: {integrity: sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + resolution: {integrity: sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + resolution: {integrity: sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + resolution: {integrity: sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + resolution: {integrity: sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + resolution: {integrity: sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.45.0': + resolution: {integrity: sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.45.0': + resolution: {integrity: sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + resolution: {integrity: sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + resolution: {integrity: sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.45.0': + resolution: {integrity: sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==} + cpu: [x64] + os: [win32] + + '@swc/core-darwin-arm64@1.12.11': + resolution: {integrity: sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.12.11': + resolution: {integrity: sha512-PTuUQrfStQ6cjW+uprGO2lpQHy84/l0v+GqRqq8s/jdK55rFRjMfCeyf6FAR0l6saO5oNOQl+zWR1aNpj8pMQw==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.12.11': + resolution: {integrity: sha512-poxBq152HsupOtnZilenvHmxZ9a8SRj4LtfxUnkMDNOGrZR9oxbQNwEzNKfi3RXEcXz+P8c0Rai1ubBazXv8oQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.12.11': + resolution: {integrity: sha512-y1HNamR/D0Hc8xIE910ysyLe269UYiGaQPoLjQS0phzWFfWdMj9bHM++oydVXZ4RSWycO7KyJ3uvw4NilvyMKQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.12.11': + resolution: {integrity: sha512-LlBxPh/32pyQsu2emMEOFRm7poEFLsw12Y1mPY7FWZiZeptomKSOSHRzKDz9EolMiV4qhK1caP1lvW4vminYgQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.12.11': + resolution: {integrity: sha512-bOjiZB8O/1AzHkzjge1jqX62HGRIpOHqFUrGPfAln/NC6NR+Z2A78u3ixV7k5KesWZFhCV0YVGJL+qToL27myA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.12.11': + resolution: {integrity: sha512-4dzAtbT/m3/UjF045+33gLiHd8aSXJDoqof7gTtu4q0ZyAf7XJ3HHspz+/AvOJLVo4FHHdFcdXhmo/zi1nFn8A==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.12.11': + resolution: {integrity: sha512-h8HiwBZErKvCAmjW92JvQp0iOqm6bncU4ac5jxBGkRApabpUenNJcj3h2g5O6GL5K6T9/WhnXE5gyq/s1fhPQg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.12.11': + resolution: {integrity: sha512-1pwr325mXRNUhxTtXmx1IokV5SiRL+6iDvnt3FRXj+X5UvXXKtg2zeyftk+03u8v8v8WUr5I32hIypVJPTNxNg==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.12.11': + resolution: {integrity: sha512-5gggWo690Gvs7XiPxAmb5tHwzB9RTVXUV7AWoGb6bmyUd1OXYaebQF0HAOtade5jIoNhfQMQJ7QReRgt/d2jAA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.12.11': + resolution: {integrity: sha512-P3GM+0lqjFctcp5HhR9mOcvLSX3SptI9L1aux0Fuvgt8oH4f92rCUrkodAa0U2ktmdjcyIiG37xg2mb/dSCYSA==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.23': + resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/leaflet@1.9.20': + resolution: {integrity: sha512-rooalPMlk61LCaLOvBF2VIf9M47HgMQqi5xQ9QRi7c8PkdIe0WrIi5IxXUXQjAdL0c+vcQ01mYWbthzmp9GHWw==} + + '@types/object-hash@3.0.6': + resolution: {integrity: sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w==} + + '@types/react-dom@19.1.6': + resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.1.8': + resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==} + + '@typescript-eslint/eslint-plugin@8.36.0': + resolution: {integrity: sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.36.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.36.0': + resolution: {integrity: sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.36.0': + resolution: {integrity: sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.36.0': + resolution: {integrity: sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.36.0': + resolution: {integrity: sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.36.0': + resolution: {integrity: sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.36.0': + resolution: {integrity: sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.36.0': + resolution: {integrity: sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.36.0': + resolution: {integrity: sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.36.0': + resolution: {integrity: sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react-swc@3.10.2': + resolution: {integrity: sha512-xD3Rdvrt5LgANug7WekBn1KhcvLn1H3jNBfJRL3reeOIua/WnZOEV5qi5qIBq5T8R0jUDmRtxuvk4bPhzGHDWw==} + peerDependencies: + vite: ^4 || ^5 || ^6 || ^7.0.0-beta.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + esbuild@0.25.6: + resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.4.20: + resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} + peerDependencies: + eslint: '>=8.40' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.31.0: + resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + geojson@0.5.0: + resolution: {integrity: sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==} + engines: {node: '>= 0.10'} + + geolib@3.3.4: + resolution: {integrity: sha512-EicrlLLL3S42gE9/wde+11uiaYAaeSVDwCUIv2uMIoRBfNJCn8EsSI+6nS3r4TCKDO6+RQNM9ayLq2at+oZQWQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.3.0: + resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + engines: {node: '>=18'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + leaflet@1.9.4: + resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + magvar@2.0.0: + resolution: {integrity: sha512-00LpSwEJZcnyX3VsdCM2CHSCvB+M6sVQTawLCB3J9oK7eEueNltNx9GFL4YR+/HIkLM+l8rFKiY77JTKnOf0jw==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.0.0-rc.1: + resolution: {integrity: sha512-k8MfDX+4G+eaa1cXXI9QF4d+pQtYol3nx8vauqRWUEOPqC7NQn2qmEqUsLoSd28rrZUL+R3T2VC+kZ2Hyx1geQ==} + peerDependencies: + react: 19.0.0-rc.1 + + react-leaflet@5.0.0-rc.2: + resolution: {integrity: sha512-1xQGYG9mEIW+nfkQhqgHImwUuB1UDlnzYFSzv6PrBFDBeYrFmv0BbpwpNAFdJg/UQ2yz5UZSL7ZwlUxjwb8MZw==} + peerDependencies: + leaflet: ^1.9.0 + react: ^19.0.0 + react-dom: ^19.0.0 + + react@19.0.0-rc.1: + resolution: {integrity: sha512-NZKln+uyPuyHchzP07I6GGYFxdAoaKhehgpCa3ltJGzwE31OYumLeshGaitA1R/fS5d9D2qpZVwTFAr6zCLM9w==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.45.0: + resolution: {integrity: sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + scheduler@0.25.0-rc.1: + resolution: {integrity: sha512-fVinv2lXqYpKConAMdergOl5owd0rY1O4P/QTe0aWKCqGtu7VsCt1iqQFxSJtqK4Lci/upVSBpGwVC7eWcuS9Q==} + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.36.0: + resolution: {integrity: sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite@7.0.4: + resolution: {integrity: sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@esbuild/aix-ppc64@0.25.6': + optional: true + + '@esbuild/android-arm64@0.25.6': + optional: true + + '@esbuild/android-arm@0.25.6': + optional: true + + '@esbuild/android-x64@0.25.6': + optional: true + + '@esbuild/darwin-arm64@0.25.6': + optional: true + + '@esbuild/darwin-x64@0.25.6': + optional: true + + '@esbuild/freebsd-arm64@0.25.6': + optional: true + + '@esbuild/freebsd-x64@0.25.6': + optional: true + + '@esbuild/linux-arm64@0.25.6': + optional: true + + '@esbuild/linux-arm@0.25.6': + optional: true + + '@esbuild/linux-ia32@0.25.6': + optional: true + + '@esbuild/linux-loong64@0.25.6': + optional: true + + '@esbuild/linux-mips64el@0.25.6': + optional: true + + '@esbuild/linux-ppc64@0.25.6': + optional: true + + '@esbuild/linux-riscv64@0.25.6': + optional: true + + '@esbuild/linux-s390x@0.25.6': + optional: true + + '@esbuild/linux-x64@0.25.6': + optional: true + + '@esbuild/netbsd-arm64@0.25.6': + optional: true + + '@esbuild/netbsd-x64@0.25.6': + optional: true + + '@esbuild/openbsd-arm64@0.25.6': + optional: true + + '@esbuild/openbsd-x64@0.25.6': + optional: true + + '@esbuild/openharmony-arm64@0.25.6': + optional: true + + '@esbuild/sunos-x64@0.25.6': + optional: true + + '@esbuild/win32-arm64@0.25.6': + optional: true + + '@esbuild/win32-ia32@0.25.6': + optional: true + + '@esbuild/win32-x64@0.25.6': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0)': + dependencies: + eslint: 9.31.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.0': {} + + '@eslint/core@0.15.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.31.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.3': + dependencies: + '@eslint/core': 0.15.1 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@react-leaflet/core@3.0.0(leaflet@1.9.4)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)': + dependencies: + leaflet: 1.9.4 + react: 19.0.0-rc.1 + react-dom: 19.0.0-rc.1(react@19.0.0-rc.1) + + '@rolldown/pluginutils@1.0.0-beta.11': {} + + '@rollup/rollup-android-arm-eabi@4.45.0': + optional: true + + '@rollup/rollup-android-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-x64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.45.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.45.0': + optional: true + + '@swc/core-darwin-arm64@1.12.11': + optional: true + + '@swc/core-darwin-x64@1.12.11': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.12.11': + optional: true + + '@swc/core-linux-arm64-gnu@1.12.11': + optional: true + + '@swc/core-linux-arm64-musl@1.12.11': + optional: true + + '@swc/core-linux-x64-gnu@1.12.11': + optional: true + + '@swc/core-linux-x64-musl@1.12.11': + optional: true + + '@swc/core-win32-arm64-msvc@1.12.11': + optional: true + + '@swc/core-win32-ia32-msvc@1.12.11': + optional: true + + '@swc/core-win32-x64-msvc@1.12.11': + optional: true + + '@swc/core@1.12.11': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.23 + optionalDependencies: + '@swc/core-darwin-arm64': 1.12.11 + '@swc/core-darwin-x64': 1.12.11 + '@swc/core-linux-arm-gnueabihf': 1.12.11 + '@swc/core-linux-arm64-gnu': 1.12.11 + '@swc/core-linux-arm64-musl': 1.12.11 + '@swc/core-linux-x64-gnu': 1.12.11 + '@swc/core-linux-x64-musl': 1.12.11 + '@swc/core-win32-arm64-msvc': 1.12.11 + '@swc/core-win32-ia32-msvc': 1.12.11 + '@swc/core-win32-x64-msvc': 1.12.11 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.23': + dependencies: + '@swc/counter': 0.1.3 + + '@types/estree@1.0.8': {} + + '@types/geojson@7946.0.16': {} + + '@types/json-schema@7.0.15': {} + + '@types/leaflet@1.9.20': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/object-hash@3.0.6': {} + + '@types/react-dom@19.1.6(@types/react@19.1.8)': + dependencies: + '@types/react': 19.1.8 + + '@types/react@19.1.8': + dependencies: + csstype: 3.1.3 + + '@typescript-eslint/eslint-plugin@8.36.0(@typescript-eslint/parser@8.36.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.36.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.36.0 + '@typescript-eslint/type-utils': 8.36.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.36.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.36.0 + eslint: 9.31.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.36.0(eslint@9.31.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.36.0 + '@typescript-eslint/types': 8.36.0 + '@typescript-eslint/typescript-estree': 8.36.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.36.0 + debug: 4.4.1 + eslint: 9.31.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.36.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.36.0(typescript@5.8.3) + '@typescript-eslint/types': 8.36.0 + debug: 4.4.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.36.0': + dependencies: + '@typescript-eslint/types': 8.36.0 + '@typescript-eslint/visitor-keys': 8.36.0 + + '@typescript-eslint/tsconfig-utils@8.36.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.36.0(eslint@9.31.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.36.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.36.0(eslint@9.31.0)(typescript@5.8.3) + debug: 4.4.1 + eslint: 9.31.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.36.0': {} + + '@typescript-eslint/typescript-estree@8.36.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.36.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.36.0(typescript@5.8.3) + '@typescript-eslint/types': 8.36.0 + '@typescript-eslint/visitor-keys': 8.36.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.36.0(eslint@9.31.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) + '@typescript-eslint/scope-manager': 8.36.0 + '@typescript-eslint/types': 8.36.0 + '@typescript-eslint/typescript-estree': 8.36.0(typescript@5.8.3) + eslint: 9.31.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.36.0': + dependencies: + '@typescript-eslint/types': 8.36.0 + eslint-visitor-keys: 4.2.1 + + '@vitejs/plugin-react-swc@3.10.2(vite@7.0.4)': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.11 + '@swc/core': 1.12.11 + vite: 7.0.4 + transitivePeerDependencies: + - '@swc/helpers' + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + esbuild@0.25.6: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.6 + '@esbuild/android-arm': 0.25.6 + '@esbuild/android-arm64': 0.25.6 + '@esbuild/android-x64': 0.25.6 + '@esbuild/darwin-arm64': 0.25.6 + '@esbuild/darwin-x64': 0.25.6 + '@esbuild/freebsd-arm64': 0.25.6 + '@esbuild/freebsd-x64': 0.25.6 + '@esbuild/linux-arm': 0.25.6 + '@esbuild/linux-arm64': 0.25.6 + '@esbuild/linux-ia32': 0.25.6 + '@esbuild/linux-loong64': 0.25.6 + '@esbuild/linux-mips64el': 0.25.6 + '@esbuild/linux-ppc64': 0.25.6 + '@esbuild/linux-riscv64': 0.25.6 + '@esbuild/linux-s390x': 0.25.6 + '@esbuild/linux-x64': 0.25.6 + '@esbuild/netbsd-arm64': 0.25.6 + '@esbuild/netbsd-x64': 0.25.6 + '@esbuild/openbsd-arm64': 0.25.6 + '@esbuild/openbsd-x64': 0.25.6 + '@esbuild/openharmony-arm64': 0.25.6 + '@esbuild/sunos-x64': 0.25.6 + '@esbuild/win32-arm64': 0.25.6 + '@esbuild/win32-ia32': 0.25.6 + '@esbuild/win32-x64': 0.25.6 + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@5.2.0(eslint@9.31.0): + dependencies: + eslint: 9.31.0 + + eslint-plugin-react-refresh@0.4.20(eslint@9.31.0): + dependencies: + eslint: 9.31.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.31.0: + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.0 + '@eslint/core': 0.15.1 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.31.0 + '@eslint/plugin-kit': 0.3.3 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + fsevents@2.3.3: + optional: true + + geojson@0.5.0: {} + + geolib@3.3.4: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.3.0: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + leaflet@1.9.4: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + magvar@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + object-hash@3.0.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.0.0-rc.1(react@19.0.0-rc.1): + dependencies: + react: 19.0.0-rc.1 + scheduler: 0.25.0-rc.1 + + react-leaflet@5.0.0-rc.2(leaflet@1.9.4)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1): + dependencies: + '@react-leaflet/core': 3.0.0(leaflet@1.9.4)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1) + leaflet: 1.9.4 + react: 19.0.0-rc.1 + react-dom: 19.0.0-rc.1(react@19.0.0-rc.1) + + react@19.0.0-rc.1: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + rollup@4.45.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.45.0 + '@rollup/rollup-android-arm64': 4.45.0 + '@rollup/rollup-darwin-arm64': 4.45.0 + '@rollup/rollup-darwin-x64': 4.45.0 + '@rollup/rollup-freebsd-arm64': 4.45.0 + '@rollup/rollup-freebsd-x64': 4.45.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.45.0 + '@rollup/rollup-linux-arm-musleabihf': 4.45.0 + '@rollup/rollup-linux-arm64-gnu': 4.45.0 + '@rollup/rollup-linux-arm64-musl': 4.45.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.45.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-musl': 4.45.0 + '@rollup/rollup-linux-s390x-gnu': 4.45.0 + '@rollup/rollup-linux-x64-gnu': 4.45.0 + '@rollup/rollup-linux-x64-musl': 4.45.0 + '@rollup/rollup-win32-arm64-msvc': 4.45.0 + '@rollup/rollup-win32-ia32-msvc': 4.45.0 + '@rollup/rollup-win32-x64-msvc': 4.45.0 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + scheduler@0.25.0-rc.1: {} + + semver@7.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + source-map-js@1.2.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.36.0(eslint@9.31.0)(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.36.0(@typescript-eslint/parser@8.36.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.36.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.36.0(eslint@9.31.0)(typescript@5.8.3) + eslint: 9.31.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + typescript@5.8.3: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite@7.0.4: + dependencies: + esbuild: 0.25.6 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.45.0 + tinyglobby: 0.2.14 + optionalDependencies: + fsevents: 2.3.3 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} diff --git a/browser/src/App.tsx b/browser/src/App.tsx new file mode 100644 index 0000000..da066e9 --- /dev/null +++ b/browser/src/App.tsx @@ -0,0 +1,45 @@ +import { MapContainer, GeoJSON, TileLayer } from "react-leaflet"; +import Parser from "./parser/parser"; +import { useEffect, useState } from "react"; +import hash from "object-hash"; + +const parser = await Parser.instance(); + +function App() { + const [procedure, setProcedure] = useState(); + console.log(procedure); + + useEffect(() => { + (async () => { + setProcedure(await parser.parse(10394)); + })(); + }, []); + + return ( +
+ + + + +
+
+ ); +} + +export default App; diff --git a/browser/src/index.css b/browser/src/index.css new file mode 100644 index 0000000..29806f8 --- /dev/null +++ b/browser/src/index.css @@ -0,0 +1,8 @@ +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; +} diff --git a/browser/src/main.tsx b/browser/src/main.tsx new file mode 100644 index 0000000..2af21e3 --- /dev/null +++ b/browser/src/main.tsx @@ -0,0 +1,12 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App.tsx"; + +import "./index.css"; +import "leaflet/dist/leaflet.css"; + +createRoot(document.getElementById("root")!).render( + + + +); diff --git a/browser/src/parser/node.ts b/browser/src/parser/node.ts new file mode 100644 index 0000000..84df73f --- /dev/null +++ b/browser/src/parser/node.ts @@ -0,0 +1,16 @@ +import Parser from "./parser.ts"; + +// mutate fetch to be local + +// @ts-ignore +fetch = async (path: string) => { + // @ts-ignore + const fs = await import("fs"); + return { + json: () => JSON.parse(fs.readFileSync(`public/${path}`)), + }; +}; + +const parser = await Parser.instance(); + +console.log(JSON.stringify(await parser.parse(10394))); diff --git a/browser/src/parser/parser.ts b/browser/src/parser/parser.ts new file mode 100644 index 0000000..a396bd7 --- /dev/null +++ b/browser/src/parser/parser.ts @@ -0,0 +1,148 @@ +import "./utils/extensions.ts"; +import { handleTurnAtFix } from "./utils/handleTurnAtFix.ts"; +import * as geolib from "geolib"; +import geojson from "geojson"; + +class Parser { + private static _instance: Parser; + + private waypoints: Waypoint[]; + private runways: Runway[]; + private terminals: Terminal[]; + + public static AC_SPEED = 250; + + private constructor( + waypoints: Waypoint[], + runways: Runway[], + terminals: Terminal[] + ) { + this.waypoints = waypoints; + this.runways = runways; + this.terminals = terminals; + } + + public static instance = async () => { + if (!Parser._instance) { + const waypoints = await (await fetch("navdata/Waypoints.json")).json(); + const runways = await (await fetch("navdata/Runways.json")).json(); + const terminals = await (await fetch("navdata/Terminals.json")).json(); + + Parser._instance = new Parser(waypoints, runways, terminals); + } + + return Parser._instance; + }; + + public parse = async (terminalID: number) => { + // Private functions + /** + * @param line Line segments + */ + const updateLastCourse = (line: LineSegment[]) => { + lastCourse = geolib.getGreatCircleBearing( + { + latitude: line.at(-2)![1], + longitude: line.at(-2)![0], + }, + { + latitude: line.at(-1)![1], + longitude: line.at(-1)![0], + } + ); + }; + + // Get Procedure main + const terminal = this.terminals.find(({ ID }) => ID === terminalID); + if (!terminal) throw new Error("Procedure does not exists"); + // Get runway this procedure is for + const runway = this.runways.find(({ ID }) => ID === terminal.RwyID); + if (!runway) throw new Error("Procedure links to non existent Runway"); + // Load procedure + const procedure = (await ( + await fetch(`navdata/TermID_${terminalID}.json`) + ).json()) as TerminalEntry[]; + + // Output variables + const navFixes: NavFix[] = []; + const lineSegments: { line: LineSegment[] }[] = []; + + // Initials + navFixes.push({ + latitude: runway.Latitude, + longitude: runway.Longitude, + altitude: runway.Elevation, + speed: 0, + name: runway.Ident, + }); + let lastCourse = runway.TrueHeading; + + // Main + for (let index = 0; index < procedure.length; index++) { + const leg = procedure[index]; + const previousFix = navFixes.at(-1)!; + const waypoint = this.waypoints.filter(({ ID }) => ID === leg.WptID)[0]; + + switch (leg.TrackCode) { + case "AF": + case "CA": + case "CD": + break; + case "CF": { + const _leg = leg as CFTerminalEntry; + const targetFix: NavFix = { + latitude: _leg.WptLat, + longitude: _leg.WptLon, + name: waypoint?.Ident ?? undefined, + "marker-color": _leg.IsFlyOver ? "#ff0000" : undefined, + isFlyOver: _leg.IsFlyOver, + altitude: previousFix.altitude, + }; + navFixes.push(targetFix); + + const line = handleTurnAtFix( + _leg.Course.toTrue(previousFix), + _leg.Course.toTrue(previousFix), + lastCourse, + previousFix, + targetFix, + _leg.TurnDir + ); + + lineSegments.push({ line }); + updateLastCourse(lineSegments.at(-1)!.line); + break; + } + case "CI": + case "CR": + case "DF": + case "FA": + case "FC": + case "FD": + case "FM": + case "HA": + case "HF": + case "HM": + case "IF": + case "PI": + case "RF": + case "TF": + case "VA": + case "VD": + case "VI": + case "VM": + case "VR": + default: + console.error("Unknown TrackCode", leg.TrackCode); + break; + } + } + + return geojson.parse([...navFixes, ...lineSegments], { + Point: ["latitude", "longitude"], + LineString: "line", + }); + }; +} + +export default Parser; diff --git a/browser/src/parser/terminators/AF.ts b/browser/src/parser/terminators/AF.ts new file mode 100644 index 0000000..94d72fb --- /dev/null +++ b/browser/src/parser/terminators/AF.ts @@ -0,0 +1 @@ +export const TerminatorsAF = () => {}; diff --git a/browser/src/parser/utils/computeIntersection.ts b/browser/src/parser/utils/computeIntersection.ts new file mode 100644 index 0000000..a665e78 --- /dev/null +++ b/browser/src/parser/utils/computeIntersection.ts @@ -0,0 +1,89 @@ +/** + * @param p1 Point 1 + * @param brng1 Bearing from Point 1 + * @param p2 Point 2 + * @param brng2 bearing from Point 2 + * @returns Intersection point + */ +export const computeIntersection = ( + p1: NavFix, + brng1: number, + p2: NavFix, + brng2: number +): NavFix | null => { + if (isNaN(brng1)) throw new TypeError(`invalid brng1 ${brng1}`); + if (isNaN(brng2)) throw new TypeError(`invalid brng2 ${brng2}`); + + const π = Math.PI; + + // see www.edwilliams.org/avform.htm#Intersection + + const φ1 = p1.latitude.toRadians(), + λ1 = p1.longitude.toRadians(); + const φ2 = p2.latitude.toRadians(), + λ2 = p2.longitude.toRadians(); + const θ13 = Number(brng1).toRadians(), + θ23 = Number(brng2).toRadians(); + const Δφ = φ2 - φ1, + Δλ = λ2 - λ1; + + // angular distance p1-p2 + const δ12 = + 2 * + Math.asin( + Math.sqrt( + Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2) + ) + ); + if (Math.abs(δ12) < Number.EPSILON) return p1; // coincident points + + // initial/final bearings between points + const cosθa = + (Math.sin(φ2) - Math.sin(φ1) * Math.cos(δ12)) / + (Math.sin(δ12) * Math.cos(φ1)); + const cosθb = + (Math.sin(φ1) - Math.sin(φ2) * Math.cos(δ12)) / + (Math.sin(δ12) * Math.cos(φ2)); + const θa = Math.acos(Math.min(Math.max(cosθa, -1), 1)); // protect against rounding errors + const θb = Math.acos(Math.min(Math.max(cosθb, -1), 1)); // protect against rounding errors + + const θ12 = Math.sin(λ2 - λ1) > 0 ? θa : 2 * π - θa; + const θ21 = Math.sin(λ2 - λ1) > 0 ? 2 * π - θb : θb; + + const α1 = θ13 - θ12; // angle 2-1-3 + const α2 = θ21 - θ23; // angle 1-2-3 + + if (Math.sin(α1) == 0 && Math.sin(α2) == 0) return null; // infinite intersections + if (Math.sin(α1) * Math.sin(α2) < 0) return null; // ambiguous intersection (antipodal/360°) + + const cosα3 = + -Math.cos(α1) * Math.cos(α2) + Math.sin(α1) * Math.sin(α2) * Math.cos(δ12); + + const δ13 = Math.atan2( + Math.sin(δ12) * Math.sin(α1) * Math.sin(α2), + Math.cos(α2) + Math.cos(α1) * cosα3 + ); + + const φ3 = Math.asin( + Math.min( + Math.max( + Math.sin(φ1) * Math.cos(δ13) + + Math.cos(φ1) * Math.sin(δ13) * Math.cos(θ13), + -1 + ), + 1 + ) + ); + + const Δλ13 = Math.atan2( + Math.sin(θ13) * Math.sin(δ13) * Math.cos(φ1), + Math.cos(δ13) - Math.sin(φ1) * Math.sin(φ3) + ); + const λ3 = λ1 + Δλ13; + + const lat = φ3.toDegrees(); + const lon = λ3.toDegrees(); + + return { ...p1, latitude: lat, longitude: lon, name: "INTC" }; +}; diff --git a/browser/src/parser/utils/extensions.ts b/browser/src/parser/utils/extensions.ts new file mode 100644 index 0000000..215ef69 --- /dev/null +++ b/browser/src/parser/utils/extensions.ts @@ -0,0 +1,27 @@ +import { magvar } from "magvar"; + +Number.prototype.toRadians = function () { + return ((this as number) * Math.PI) / 180; +}; +Number.prototype.toDegrees = function () { + return ((this as number) * 180) / Math.PI; +}; +Number.prototype.reciprocalCourse = function () { + let inv = (this as number) + 180; + inv = inv >= 360 ? inv - 360 : inv; + return inv; +}; +Number.prototype.normaliseDegrees = function () { + return (this as number) >= 360 + ? (this as number) - 360 + : (this as number) < 0 + ? (this as number) + 360 + : (this as number); +}; +Number.prototype.toTrue = function (fix) { + const _magvar = magvar(fix.latitude, fix.longitude); //Magvar is returned + for East + return ((this as number) + _magvar).normaliseDegrees(); +}; +Number.prototype.toMetre = function () { + return (this as number) * 1852.0; +}; diff --git a/browser/src/parser/utils/generatePerformanceArc.ts b/browser/src/parser/utils/generatePerformanceArc.ts new file mode 100644 index 0000000..a87dfb4 --- /dev/null +++ b/browser/src/parser/utils/generatePerformanceArc.ts @@ -0,0 +1,54 @@ +import * as geolib from "geolib"; +import Parser from "../parser.ts"; + +/** + * @param crsIntoEndpoint Course into arc endpoint + * @param crsFromOrigin Course from arc origin point + * @param start Arc origin point + * @param turnDir Turn direction + * @returns Line segments + */ +export const generatePerformanceArc = ( + crsIntoEndpoint: number, + crsFromOrigin: number, + start: NavFix, + turnDir?: TurnDirection +) => { + const line: LineSegment[] = [[start.longitude, start.latitude]]; + + // Check if there even is an arc + if (crsIntoEndpoint !== crsFromOrigin) { + // Turn Dir + if (!turnDir || turnDir === "E") { + let prov = crsFromOrigin - crsIntoEndpoint; + prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov; + turnDir = prov > 0 ? "L" : "R"; + } + + // Generate arc + while (crsFromOrigin !== crsIntoEndpoint) { + if (turnDir === "R") { + const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees(); + crsFromOrigin += delta < 1 ? delta : 1; + crsFromOrigin = crsFromOrigin.normaliseDegrees(); + } else { + const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees(); + crsFromOrigin -= delta < 1 ? delta : 1; + crsFromOrigin = crsFromOrigin.normaliseDegrees(); + } + + const arcFix = geolib.computeDestinationPoint( + { + latitude: line.at(-1)![1], + longitude: line.at(-1)![0], + }, + (Parser.AC_SPEED / 3600).toMetre(), + crsFromOrigin + ); + + line.push([arcFix.longitude, arcFix.latitude]); + } + } + + return line; +}; diff --git a/browser/src/parser/utils/generateTangentArc.ts b/browser/src/parser/utils/generateTangentArc.ts new file mode 100644 index 0000000..e8b2a2d --- /dev/null +++ b/browser/src/parser/utils/generateTangentArc.ts @@ -0,0 +1,105 @@ +import * as geolib from "geolib"; +import { computeIntersection } from "./computeIntersection.ts"; + +/** + * @param crsIntoEndpoint Course into arc endpoint + * @param crsFromOrigin Course from arc origin point + * @param start Arc origin point + * @param end Leg endpoint + * @param turnDir Turn direction + * @returns Line segments forming arc from `start`to `end` + */ +export const generateTangentArc = ( + crsIntoEndpoint: number, + crsFromOrigin: number, + start: NavFix, + end: NavFix, + turnDir?: TurnDirection +) => { + const line: LineSegment[] = [[start.longitude, start.latitude]]; + + // Check if there even is an arc + if (crsFromOrigin !== crsIntoEndpoint) { + // Course to the end of the arc + let crsFromStartToEnd; + if (!turnDir || turnDir === "E") { + let prov = crsFromOrigin - crsIntoEndpoint; + prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov; + turnDir = prov > 0 ? "L" : "R"; + } + if (turnDir === "R") { + const delta = (360 - crsFromOrigin + crsIntoEndpoint).normaliseDegrees(); + crsFromStartToEnd = (crsFromOrigin + delta / 2).normaliseDegrees(); + } else { + const delta = (crsFromOrigin + 360 - crsIntoEndpoint).normaliseDegrees(); + crsFromStartToEnd = (crsFromOrigin - delta / 2).normaliseDegrees(); + } + + // Arc end + const intcArcOnCrsIntoEndpoint = computeIntersection( + start, + crsFromStartToEnd, + end, + crsIntoEndpoint.reciprocalCourse() + ); + if (!intcArcOnCrsIntoEndpoint) return null; + + let crsOrthogonalOnOrigin; + let crsOrthogonalOnEndpoint; + if (turnDir === "R") { + crsOrthogonalOnOrigin = (crsFromOrigin + 90).normaliseDegrees(); + crsOrthogonalOnEndpoint = (crsIntoEndpoint + 90).normaliseDegrees(); + } else { + crsOrthogonalOnOrigin = (crsFromOrigin - 90).normaliseDegrees(); + crsOrthogonalOnEndpoint = (crsIntoEndpoint - 90).normaliseDegrees(); + } + + // Generate arc + const arcCenter = computeIntersection( + start, + crsOrthogonalOnOrigin, + intcArcOnCrsIntoEndpoint, + crsOrthogonalOnEndpoint + ); + if (!arcCenter) return null; + const arcRad = geolib.getDistance(arcCenter, start); + + crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse(); + crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse(); + // Start turn immediately + if (turnDir === "R") { + crsOrthogonalOnOrigin += + crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1; + } else { + crsOrthogonalOnOrigin -= + crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1; + } + + while (crsOrthogonalOnOrigin !== crsOrthogonalOnEndpoint) { + if (turnDir === "R") { + const delta = ( + crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin + ).normaliseDegrees(); + crsOrthogonalOnOrigin += delta < 1 ? delta : 1; + crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees(); + } else { + const delta = ( + crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint + ).normaliseDegrees(); + crsOrthogonalOnOrigin -= delta < 1 ? delta : 1; + crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees(); + } + if (crsOrthogonalOnOrigin === crsOrthogonalOnEndpoint) break; + + const arcFix = geolib.computeDestinationPoint( + arcCenter, + arcRad, + crsOrthogonalOnOrigin + ); + + line.push([arcFix.longitude, arcFix.latitude]); + } + } + + return line; +}; diff --git a/browser/src/parser/utils/handleTurnAtFix.ts b/browser/src/parser/utils/handleTurnAtFix.ts new file mode 100644 index 0000000..9f54d60 --- /dev/null +++ b/browser/src/parser/utils/handleTurnAtFix.ts @@ -0,0 +1,68 @@ +import * as geolib from "geolib"; +import { generateTangentArc } from "./generateTangentArc.ts"; +import { generatePerformanceArc } from "./generatePerformanceArc.ts"; + +/** + * @param crsIntoEndpoint Course into endpoint + * @param crsIntoIntercept Course into intercept point (only fo intercept type legs) + * @param crsFromOrigin Course from arc origin point + * @param start Waypoint where leg starts + * @param end Waypoint where leg ends + * @param turnDir Turn direction + * @returns Line segments from `start` to `end` + */ +export const handleTurnAtFix = ( + crsIntoEndpoint: number, + crsIntoIntercept: number, + crsFromOrigin: number, + start: NavFix, + end: NavFix, + turnDir?: TurnDirection +) => { + const line: LineSegment[] = []; + + // Overfly turn + if (start.isFlyOver) { + const arc1 = generateTangentArc( + crsIntoEndpoint, + crsFromOrigin, + start, + end, + turnDir + ); + + const arc2 = generatePerformanceArc( + crsIntoIntercept, + crsFromOrigin, + start, + turnDir + ); + + // Decide on arc + let arc; + if (arc1) { + const endCrs = geolib.getGreatCircleBearing( + { + latitude: arc1.at(-1)![1], + longitude: arc1.at(-1)![0], + }, + end + ); + + if (endCrs <= crsIntoEndpoint + 1 && endCrs >= crsIntoEndpoint - 1) + arc = arc1; + else arc = arc2; + } else { + arc = arc2; + } + line.push(...arc); + line.push([end.longitude, end.latitude]); + } + // FIXME: Procedural turn + else { + // Direct line for now + line.push([start.longitude, start.latitude], [end.longitude, end.latitude]); + } + + return line; +}; diff --git a/browser/src/types/extensions.d.ts b/browser/src/types/extensions.d.ts new file mode 100644 index 0000000..f5fe140 --- /dev/null +++ b/browser/src/types/extensions.d.ts @@ -0,0 +1,29 @@ +export declare global { + interface Number { + /** + * @returns Value converted from degrees to radians + */ + toRadians: () => number; + /** + * @returns Value converted from radians to degrees + */ + toDegrees: () => number; + /** + * @returns Reciprocal course of value + */ + reciprocalCourse: () => number; + /** + * @returns Normalises to [0,360) + */ + normaliseDegrees: () => number; + /** + * @param fix Fix for magnetic declination + * @returns Value referencing true north + */ + toTrue: (fix: { latitude: number; longitude: number }) => number; + /** + * @returns Value converted from nmi to metres + */ + toMetre: () => number; + } +} diff --git a/browser/src/types/geojson.d.ts b/browser/src/types/geojson.d.ts new file mode 100644 index 0000000..58b0663 --- /dev/null +++ b/browser/src/types/geojson.d.ts @@ -0,0 +1,3 @@ +declare module "geojson" { + export const parse: (data: any, format: any) => any; +} diff --git a/browser/src/types/magvar.d.ts b/browser/src/types/magvar.d.ts new file mode 100644 index 0000000..2b06a95 --- /dev/null +++ b/browser/src/types/magvar.d.ts @@ -0,0 +1,3 @@ +declare module "magvar" { + export const magvar: (latitude: number, longitude: number) => number; +} diff --git a/browser/src/types/terminators/AF.d.ts b/browser/src/types/terminators/AF.d.ts new file mode 100644 index 0000000..22c3f51 --- /dev/null +++ b/browser/src/types/terminators/AF.d.ts @@ -0,0 +1,18 @@ +export declare global { + type AFTerminalEntry = Required< + Pick< + TerminalEntry, + | "TurnDir" + | "WptID" + | "WptLat" + | "WptLon" + | "NavID" + | "NavLat" + | "NavLon" + | "NavBear" + | "NavDist" + | "Course" + > + > & + TerminalEntry; +} diff --git a/browser/src/types/terminators/CF.d.ts b/browser/src/types/terminators/CF.d.ts new file mode 100644 index 0000000..d6fefae --- /dev/null +++ b/browser/src/types/terminators/CF.d.ts @@ -0,0 +1,18 @@ +export declare global { + type CFTerminalEntry = Required< + Pick< + TerminalEntry, + | "WptID" + | "WptLat" + | "WptLon" + | "NavID" + | "NavLat" + | "NavLon" + | "NavBear" + | "NavDist" + | "Course" + | "Distance" + > + > & + TerminalEntry; +} diff --git a/browser/src/types/types.d.ts b/browser/src/types/types.d.ts new file mode 100644 index 0000000..18eb493 --- /dev/null +++ b/browser/src/types/types.d.ts @@ -0,0 +1,91 @@ +export declare global { + type Waypoint = { + ID: number; + Ident: string; + }; + + type Runway = { + ID: number; + Latitude: number; + Longitude: number; + Elevation: number; + Ident: string; + TrueHeading: number; + }; + + type TrackCode = + | "AF" + | "CA" + | "CD" + | "CF" + | "CI" + | "CR" + | "DF" + | "FA" + | "FC" + | "FD" + | "FM" + | "HA" + | "HF" + | "HM" + | "IF" + | "PI" + | "RF" + | "TF" + | "VA" + | "VD" + | "VI" + | "VM" + | "VR"; + + type TurnDirection = "E" | "L" | "R"; + + type TerminalEntry = { + ID: number; + TerminalID: number; + Type: string; + Transition: string; + TrackCode: TrackCode; + WptID?: number; + WptLat?: number; + WptLon?: number; + TurnDir?: TurnDirection; + NavID?: number; + NavLat?: number; + NavLon?: number; + NavBear?: number; + NavDist?: number; + Course?: number; + Distance?: number; + Alt?: string; + Vnav: unknown; + CenterID?: number; + CenterLat?: number; + CenterLon?: number; + IsFlyOver: boolean; + SpeedLimit?: string; + IsFAF: boolean; + IsMAP: boolean; + }; + + type Terminal = { + ID: number; + FullName: string; + ICAO: string; + RwyID: number; + }; + + type NavFix = { + latitude: number; + longitude: number; + altitude?: number; + speed?: number; + name?: string; + isFlyOver?: boolean; + "marker-color"?: string; + altitudeConstraint?: string; + speedConstraint?: string; + }; + + type LineSegment = [number, number]; +} diff --git a/browser/src/vite-env.d.ts b/browser/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/browser/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/browser/tsconfig.app.json b/browser/tsconfig.app.json new file mode 100644 index 0000000..9965de5 --- /dev/null +++ b/browser/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "typeRoots": ["node_modules/@types", "src/types/**"], + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/browser/tsconfig.json b/browser/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/browser/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/browser/tsconfig.node.json b/browser/tsconfig.node.json new file mode 100644 index 0000000..3d1e7ee --- /dev/null +++ b/browser/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + "typeRoots": ["node_modules/@types", "src/types/**"], + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/browser/vite.config.ts b/browser/vite.config.ts new file mode 100644 index 0000000..2328e17 --- /dev/null +++ b/browser/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react-swc' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +})