Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 80791a8f38 | |||
| 59ae422fe3 | |||
| 729cc22b37 | |||
| 678e52ddd5 | |||
| 1035befc6f | |||
| 1f99976f97 | |||
| 640a2e8ef6 | |||
| 787b3b108b | |||
| 001011cd23 | |||
| aa0bcdd091 | |||
| 5ec74129e2 | |||
| 372b31876d | |||
| 4036790a4b | |||
| 621691277e | |||
| 4ef2667c4e | |||
| b46646a462 | |||
| 171897033e | |||
| c3dbbf2637 | |||
| 60e074a70f | |||
| a0ea5a83a5 | |||
| 639b2c1aeb | |||
| de06d09d1c | |||
| d722364cf7 | |||
| fd7edc217d | |||
| 6e11b438a2 | |||
| 46bf44a324 | |||
| 3b28779b69 | |||
| 6f0fbac13e | |||
| 920681f68c | |||
| 26b1fbc60c | |||
| 3cef8e9e14 | |||
| d740c83b2e | |||
| 99fae9df17 | |||
| 03838bf714 | |||
| 50429fd4a6 | |||
| b8ddada94e | |||
| 59d9667109 | |||
| c2ab32c281 |
317
bun.lock
317
bun.lock
@ -6,8 +6,10 @@
|
||||
"dependencies": {
|
||||
"@diffusionstudio/vits-web": "^1.0.3",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@mapbox/vector-tile": "^2.0.4",
|
||||
"@tauri-apps/plugin-fs": "~2",
|
||||
"@tauri-apps/plugin-upload": "~2",
|
||||
"@turf/turf": "^7.2.0",
|
||||
"@types/sql.js": "^1.4.9",
|
||||
"buffer": "^6.0.3",
|
||||
"eslint": "^9.29.0",
|
||||
@ -18,6 +20,7 @@
|
||||
"libsodium-wrappers": "^0.7.15",
|
||||
"opening_hours": "^3.8.0",
|
||||
"pako": "^2.1.0",
|
||||
"pbf": "^4.0.1",
|
||||
"pmtiles": "^4.3.0",
|
||||
"sql.js": "^1.13.0",
|
||||
"svelte-maplibre-gl": "^0.1.8",
|
||||
@ -38,7 +41,7 @@
|
||||
"@types/libsodium-wrappers": "^0.7.14",
|
||||
"@types/node": "^22.15.24",
|
||||
"@types/pako": "^2.0.3",
|
||||
"bits-ui": "^2.8.6",
|
||||
"bits-ui": "^2.11.0",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"git-format-staged": "^3.1.1",
|
||||
@ -205,13 +208,13 @@
|
||||
|
||||
"@mapbox/jsonlint-lines-primitives": ["@mapbox/jsonlint-lines-primitives@2.0.2", "", {}, "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="],
|
||||
|
||||
"@mapbox/point-geometry": ["@mapbox/point-geometry@0.1.0", "", {}, "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="],
|
||||
"@mapbox/point-geometry": ["@mapbox/point-geometry@1.1.0", "", {}, "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ=="],
|
||||
|
||||
"@mapbox/tiny-sdf": ["@mapbox/tiny-sdf@2.0.6", "", {}, "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA=="],
|
||||
|
||||
"@mapbox/unitbezier": ["@mapbox/unitbezier@0.0.1", "", {}, "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="],
|
||||
|
||||
"@mapbox/vector-tile": ["@mapbox/vector-tile@1.3.1", "", { "dependencies": { "@mapbox/point-geometry": "~0.1.0" } }, "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw=="],
|
||||
"@mapbox/vector-tile": ["@mapbox/vector-tile@2.0.4", "", { "dependencies": { "@mapbox/point-geometry": "~1.1.0", "@types/geojson": "^7946.0.16", "pbf": "^4.0.1" } }, "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg=="],
|
||||
|
||||
"@mapbox/whoots-js": ["@mapbox/whoots-js@3.1.0", "", {}, "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="],
|
||||
|
||||
@ -373,6 +376,238 @@
|
||||
|
||||
"@tsconfig/svelte": ["@tsconfig/svelte@5.0.4", "", {}, "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q=="],
|
||||
|
||||
"@turf/along": ["@turf/along@7.2.0", "", { "dependencies": { "@turf/bearing": "^7.2.0", "@turf/destination": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-Cf+d2LozABdb0TJoIcJwFKB+qisJY4nMUW9z6PAuZ9UCH7AR//hy2Z06vwYCKFZKP4a7DRPkOMBadQABCyoYuw=="],
|
||||
|
||||
"@turf/angle": ["@turf/angle@7.2.0", "", { "dependencies": { "@turf/bearing": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/rhumb-bearing": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-b28rs1NO8Dt/MXadFhnpqH7GnEWRsl+xF5JeFtg9+eM/+l/zGrdliPYMZtAj12xn33w22J1X4TRprAI0rruvVQ=="],
|
||||
|
||||
"@turf/area": ["@turf/area@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-zuTTdQ4eoTI9nSSjerIy4QwgvxqwJVciQJ8tOPuMHbXJ9N/dNjI7bU8tasjhxas/Cx3NE9NxVHtNpYHL0FSzoA=="],
|
||||
|
||||
"@turf/bbox": ["@turf/bbox@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-wzHEjCXlYZiDludDbXkpBSmv8Zu6tPGLmJ1sXQ6qDwpLE1Ew3mcWqt8AaxfTP5QwDNQa3sf2vvgTEzNbPQkCiA=="],
|
||||
|
||||
"@turf/bbox-clip": ["@turf/bbox-clip@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-q6RXTpqeUQAYLAieUL1n3J6ukRGsNVDOqcYtfzaJbPW+0VsAf+1cI16sN700t0sekbeU1DH/RRVAHhpf8+36wA=="],
|
||||
|
||||
"@turf/bbox-polygon": ["@turf/bbox-polygon@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-Aj4G1GAAy26fmOqMjUk0Z+Lcax5VQ9g1xYDbHLQWXvfTsaueBT+RzdH6XPnZ/seEEnZkio2IxE8V5af/osupgA=="],
|
||||
|
||||
"@turf/bearing": ["@turf/bearing@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-Jm0Xt3GgHjRrWvBtAGvgfnADLm+4exud2pRlmCYx8zfiKuNXQFkrcTZcOiJOgTfG20Agq28iSh15uta47jSIbg=="],
|
||||
|
||||
"@turf/bezier-spline": ["@turf/bezier-spline@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-7BPkc3ufYB9KLvcaTpTsnpXzh9DZoENxCS0Ms9XUwuRXw45TpevwUpOsa3atO76iKQ5puHntqFO4zs8IUxBaaA=="],
|
||||
|
||||
"@turf/boolean-clockwise": ["@turf/boolean-clockwise@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-0fJeFSARxy6ealGBM4Gmgpa1o8msQF87p2Dx5V6uSqzT8VPDegX1NSWl4b7QgXczYa9qv7IAABttdWP0K7Q7eQ=="],
|
||||
|
||||
"@turf/boolean-concave": ["@turf/boolean-concave@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-v3dTN04dfO6VqctQj1a+pjDHb6+/Ev90oAR2QjJuAntY4ubhhr7vKeJdk/w+tWNSMKULnYwfe65Du3EOu3/TeA=="],
|
||||
|
||||
"@turf/boolean-contains": ["@turf/boolean-contains@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/boolean-point-on-line": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-dgRQm4uVO5XuLee4PLVH7CFQZKdefUBMIXTPITm2oRIDmPLJKHDOFKQTNkGJ73mDKKBR2lmt6eVH3br6OYrEYg=="],
|
||||
|
||||
"@turf/boolean-crosses": ["@turf/boolean-crosses@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/line-intersect": "^7.2.0", "@turf/polygon-to-line": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-9GyM4UUWFKQOoNhHVSfJBf5XbPy8Fxfz9djjJNAnm/IOl8NmFUSwFPAjKlpiMcr6yuaAoc9R/1KokS9/eLqPvA=="],
|
||||
|
||||
"@turf/boolean-disjoint": ["@turf/boolean-disjoint@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/line-intersect": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/polygon-to-line": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-xdz+pYKkLMuqkNeJ6EF/3OdAiJdiHhcHCV0ykX33NIuALKIEpKik0+NdxxNsZsivOW6keKwr61SI+gcVtHYcnQ=="],
|
||||
|
||||
"@turf/boolean-equal": ["@turf/boolean-equal@7.2.0", "", { "dependencies": { "@turf/clean-coords": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "geojson-equality-ts": "^1.0.2", "tslib": "^2.8.1" } }, "sha512-TmjKYLsxXqEmdDtFq3QgX4aSogiISp3/doeEtDOs3NNSR8susOtBEZkmvwO6DLW+g/rgoQJIBR6iVoWiRqkBxw=="],
|
||||
|
||||
"@turf/boolean-intersects": ["@turf/boolean-intersects@7.2.0", "", { "dependencies": { "@turf/boolean-disjoint": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-GLRyLQgK3F14drkK5Qi9Mv7Z9VT1bgQUd9a3DB3DACTZWDSwfh8YZUFn/HBwRkK8dDdgNEXaavggQHcPi1k9ow=="],
|
||||
|
||||
"@turf/boolean-overlap": ["@turf/boolean-overlap@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/line-intersect": "^7.2.0", "@turf/line-overlap": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "geojson-equality-ts": "^1.0.2", "tslib": "^2.8.1" } }, "sha512-ieM5qIE4anO+gUHIOvEN7CjyowF+kQ6v20/oNYJCp63TVS6eGMkwgd+I4uMzBXfVW66nVHIXjODdUelU+Xyctw=="],
|
||||
|
||||
"@turf/boolean-parallel": ["@turf/boolean-parallel@7.2.0", "", { "dependencies": { "@turf/clean-coords": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/line-segment": "^7.2.0", "@turf/rhumb-bearing": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-iOtuzzff8nmwv05ROkSvyeGLMrfdGkIi+3hyQ+DH4IVyV37vQbqR5oOJ0Nt3Qq1Tjrq9fvF8G3OMdAv3W2kY9w=="],
|
||||
|
||||
"@turf/boolean-point-in-polygon": ["@turf/boolean-point-in-polygon@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "point-in-polygon-hao": "^1.1.0", "tslib": "^2.8.1" } }, "sha512-lvEOjxeXIp+wPXgl9kJA97dqzMfNexjqHou+XHVcfxQgolctoJiRYmcVCWGpiZ9CBf/CJha1KmD1qQoRIsjLaA=="],
|
||||
|
||||
"@turf/boolean-point-on-line": ["@turf/boolean-point-on-line@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-H/bXX8+2VYeSyH8JWrOsu8OGmeA9KVZfM7M6U5/fSqGsRHXo9MyYJ94k39A9kcKSwI0aWiMXVD2UFmiWy8423Q=="],
|
||||
|
||||
"@turf/boolean-touches": ["@turf/boolean-touches@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/boolean-point-on-line": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-8qb1CO+cwFATGRGFgTRjzL9aibfsbI91pdiRl7KIEkVdeN/H9k8FDrUA1neY7Yq48IaciuwqjbbojQ16FD9b0w=="],
|
||||
|
||||
"@turf/boolean-valid": ["@turf/boolean-valid@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/boolean-crosses": "^7.2.0", "@turf/boolean-disjoint": "^7.2.0", "@turf/boolean-overlap": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/boolean-point-on-line": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/line-intersect": "^7.2.0", "@types/geojson": "^7946.0.10", "geojson-polygon-self-intersections": "^1.2.1", "tslib": "^2.8.1" } }, "sha512-xb7gdHN8VV6ivPJh6rPpgxmAEGReiRxqY+QZoEZVGpW2dXcmU1BdY6FA6G/cwvggXAXxJBREoANtEDgp/0ySbA=="],
|
||||
|
||||
"@turf/boolean-within": ["@turf/boolean-within@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/boolean-point-on-line": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-zB3AiF59zQZ27Dp1iyhp9mVAKOFHat8RDH45TZhLY8EaqdEPdmLGvwMFCKfLryQcUDQvmzP8xWbtUR82QM5C4g=="],
|
||||
|
||||
"@turf/buffer": ["@turf/buffer@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/center": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/jsts": "^2.7.1", "@turf/meta": "^7.2.0", "@turf/projection": "^7.2.0", "@types/geojson": "^7946.0.10", "d3-geo": "1.7.1" } }, "sha512-QH1FTr5Mk4z1kpQNztMD8XBOZfpOXPOtlsxaSAj2kDIf5+LquA6HtJjZrjUngnGtzG5+XwcfyRL4ImvLnFjm5Q=="],
|
||||
|
||||
"@turf/center": ["@turf/center@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-UTNp9abQ2kuyRg5gCIGDNwwEQeF3NbpYsd1Q0KW9lwWuzbLVNn0sOwbxjpNF4J2HtMOs5YVOcqNvYyuoa2XrXw=="],
|
||||
|
||||
"@turf/center-mean": ["@turf/center-mean@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-NaW6IowAooTJ35O198Jw3U4diZ6UZCCeJY+4E+WMLpks3FCxMDSHEfO2QjyOXQMGWZnVxVelqI5x9DdniDbQ+A=="],
|
||||
|
||||
"@turf/center-median": ["@turf/center-median@7.2.0", "", { "dependencies": { "@turf/center-mean": "^7.2.0", "@turf/centroid": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-/CgVyHNG4zAoZpvkl7qBCe4w7giWNVtLyTU5PoIfg1vWM4VpYw+N7kcBBH46bbzvVBn0vhmZr586r543EwdC/A=="],
|
||||
|
||||
"@turf/center-of-mass": ["@turf/center-of-mass@7.2.0", "", { "dependencies": { "@turf/centroid": "^7.2.0", "@turf/convex": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-ij3pmG61WQPHGTQvOziPOdIgwTMegkYTwIc71Gl7xn4C0vWH6KLDSshCphds9xdWSXt2GbHpUs3tr4XGntHkEQ=="],
|
||||
|
||||
"@turf/centroid": ["@turf/centroid@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-yJqDSw25T7P48au5KjvYqbDVZ7qVnipziVfZ9aSo7P2/jTE7d4BP21w0/XLi3T/9bry/t9PR1GDDDQljN4KfDw=="],
|
||||
|
||||
"@turf/circle": ["@turf/circle@7.2.0", "", { "dependencies": { "@turf/destination": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-1AbqBYtXhstrHmnW6jhLwsv7TtmT0mW58Hvl1uZXEDM1NCVXIR50yDipIeQPjrCuJ/Zdg/91gU8+4GuDCAxBGA=="],
|
||||
|
||||
"@turf/clean-coords": ["@turf/clean-coords@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-+5+J1+D7wW7O/RDXn46IfCHuX1gIV1pIAQNSA7lcDbr3HQITZj334C4mOGZLEcGbsiXtlHWZiBtm785Vg8i+QQ=="],
|
||||
|
||||
"@turf/clone": ["@turf/clone@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-JlGUT+/5qoU5jqZmf6NMFIoLDY3O7jKd53Up+zbpJ2vzUp6QdwdNzwrsCeONhynWM13F0MVtPXH4AtdkrgFk4g=="],
|
||||
|
||||
"@turf/clusters": ["@turf/clusters@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-sKOrIKHHtXAuTKNm2USnEct+6/MrgyzMW42deZ2YG2RRKWGaaxHMFU2Yw71Yk4DqStOqTIBQpIOdrRuSOwbuQw=="],
|
||||
|
||||
"@turf/clusters-dbscan": ["@turf/clusters-dbscan@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "rbush": "^3.0.1", "tslib": "^2.8.1" } }, "sha512-VWVUuDreev56g3/BMlnq/81yzczqaz+NVTypN5CigGgP67e+u/CnijphiuhKjtjDd/MzGjXgEWBJc26Y6LYKAw=="],
|
||||
|
||||
"@turf/clusters-kmeans": ["@turf/clusters-kmeans@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "skmeans": "0.9.7", "tslib": "^2.8.1" } }, "sha512-BxQdK8jc8Mwm9yoClCYkktm4W004uiQGqb/i/6Y7a8xqgJITWDgTu/cy//wOxAWPk4xfe6MThjnqkszWW8JdyQ=="],
|
||||
|
||||
"@turf/collect": ["@turf/collect@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "rbush": "^3.0.1", "tslib": "^2.8.1" } }, "sha512-zRVGDlYS8Bx/Zz4vnEUyRg4dmqHhkDbW/nIUIJh657YqaMj1SFi4Iv2i9NbcurlUBDJFkpuOhCvvEvAdskJ8UA=="],
|
||||
|
||||
"@turf/combine": ["@turf/combine@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-VEjm3IvnbMt3IgeRIhCDhhQDbLqCU1/5uN1+j1u6fyA095pCizPThGp4f/COSzC3t1s/iiV+fHuDsB6DihHffQ=="],
|
||||
|
||||
"@turf/concave": ["@turf/concave@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/tin": "^7.2.0", "@types/geojson": "^7946.0.10", "topojson-client": "3.x", "topojson-server": "3.x", "tslib": "^2.8.1" } }, "sha512-cpaDDlumK762kdadexw5ZAB6g/h2pJdihZ+e65lbQVe3WukJHAANnIEeKsdFCuIyNKrwTz2gWu5ws+OpjP48Yw=="],
|
||||
|
||||
"@turf/convex": ["@turf/convex@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "concaveman": "^1.2.1", "tslib": "^2.8.1" } }, "sha512-HsgHm+zHRE8yPCE/jBUtWFyaaBmpXcSlyHd5/xsMhSZRImFzRzBibaONWQo7xbKZMISC3Nc6BtUjDi/jEVbqyA=="],
|
||||
|
||||
"@turf/destination": ["@turf/destination@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-8DUxtOO0Fvrh1xclIUj3d9C5WS20D21F5E+j+X9Q+ju6fcM4huOqTg5ckV1DN2Pg8caABEc5HEZJnGch/5YnYQ=="],
|
||||
|
||||
"@turf/difference": ["@turf/difference@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "polyclip-ts": "^0.16.8", "tslib": "^2.8.1" } }, "sha512-NHKD1v3s8RX+9lOpvHJg6xRuJOKiY3qxHhz5/FmE0VgGqnCkE7OObqWZ5SsXG+Ckh0aafs5qKhmDdDV/gGi6JA=="],
|
||||
|
||||
"@turf/dissolve": ["@turf/dissolve@7.2.0", "", { "dependencies": { "@turf/flatten": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "polyclip-ts": "^0.16.8", "tslib": "^2.8.1" } }, "sha512-gPG5TE3mAYuZqBut8tPYCKwi4hhx5Cq0ALoQMB9X0hrVtFIKrihrsj98XQM/5pL/UIpAxQfwisQvy6XaOFaoPA=="],
|
||||
|
||||
"@turf/distance": ["@turf/distance@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-HBjjXIgEcD/wJYjv7/6OZj5yoky2oUvTtVeIAqO3lL80XRvoYmVg6vkOIu6NswkerwLDDNT9kl7+BFLJoHbh6Q=="],
|
||||
|
||||
"@turf/distance-weight": ["@turf/distance-weight@7.2.0", "", { "dependencies": { "@turf/centroid": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-NeoyV0fXDH+7nIoNtLjAoH9XL0AS1pmTIyDxEE6LryoDTsqjnuR0YQxIkLCCWDqECoqaOmmBqpeWONjX5BwWCg=="],
|
||||
|
||||
"@turf/ellipse": ["@turf/ellipse@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/rhumb-destination": "^7.2.0", "@turf/transform-rotate": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-/Y75S5hE2+xjnTw4dXpQ5r/Y2HPM4xrwkPRCCQRpuuboKdEvm42azYmh7isPnMnBTVcmGb9UmGKj0HHAbiwt1g=="],
|
||||
|
||||
"@turf/envelope": ["@turf/envelope@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/bbox-polygon": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-xOMtDeNKHwUuDfzQeoSNmdabsP0/IgVDeyzitDe/8j9wTeW+MrKzVbGz7627PT3h6gsO+2nUv5asfKtUbmTyHA=="],
|
||||
|
||||
"@turf/explode": ["@turf/explode@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-jyMXg93J1OI7/65SsLE1k9dfQD3JbcPNMi4/O3QR2Qb3BAs2039oFaSjtW+YqhMqVC4V3ZeKebMcJ8h9sK1n+A=="],
|
||||
|
||||
"@turf/flatten": ["@turf/flatten@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-q38Qsqr4l7mxp780zSdn0gp/WLBX+sa+gV6qIbDQ1HKCrrPK8QQJmNx7gk1xxEXVot6tq/WyAPysCQdX+kLmMA=="],
|
||||
|
||||
"@turf/flip": ["@turf/flip@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-X0TQ0U/UYh4tyXdLO5itP1sO2HOvfrZC0fYSWmTfLDM14jEPkEK8PblofznfBygL+pIFtOS2is8FuVcp5XxYpQ=="],
|
||||
|
||||
"@turf/geojson-rbush": ["@turf/geojson-rbush@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "rbush": "^3.0.1" } }, "sha512-ST8fLv+EwxVkDgsmhHggM0sPk2SfOHTZJkdgMXVFT7gB9o4lF8qk4y4lwvCCGIfFQAp2yv/PN5EaGMEKutk6xw=="],
|
||||
|
||||
"@turf/great-circle": ["@turf/great-circle@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-n30OiADyOKHhor0aXNgYfXQYXO3UtsOKmhQsY1D89/Oh1nCIXG/1ZPlLL9ZoaRXXBTUBjh99a+K8029NQbGDhw=="],
|
||||
|
||||
"@turf/helpers": ["@turf/helpers@7.2.0", "", { "dependencies": { "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw=="],
|
||||
|
||||
"@turf/hex-grid": ["@turf/hex-grid@7.2.0", "", { "dependencies": { "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/intersect": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-Yo2yUGxrTCQfmcVsSjDt0G3Veg8YD26WRd7etVPD9eirNNgXrIyZkbYA7zVV/qLeRWVmYIKRXg1USWl7ORQOGA=="],
|
||||
|
||||
"@turf/interpolate": ["@turf/interpolate@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/centroid": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/hex-grid": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/point-grid": "^7.2.0", "@turf/square-grid": "^7.2.0", "@turf/triangle-grid": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-Ifgjm1SEo6XujuSAU6lpRMvoJ1SYTreil1Rf5WsaXj16BQJCedht/4FtWCTNhSWTwEz2motQ1WNrjTCuPG94xA=="],
|
||||
|
||||
"@turf/intersect": ["@turf/intersect@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "polyclip-ts": "^0.16.8", "tslib": "^2.8.1" } }, "sha512-81GMzKS9pKqLPa61qSlFxLFeAC8XbwyCQ9Qv4z6o5skWk1qmMUbEHeMqaGUTEzk+q2XyhZ0sju1FV4iLevQ/aw=="],
|
||||
|
||||
"@turf/invariant": ["@turf/invariant@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q=="],
|
||||
|
||||
"@turf/isobands": ["@turf/isobands@7.2.0", "", { "dependencies": { "@turf/area": "^7.2.0", "@turf/bbox": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/explode": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "marchingsquares": "^1.3.3", "tslib": "^2.8.1" } }, "sha512-lYoHeRieFzpBp29Jh19QcDIb0E+dzo/K5uwZuNga4wxr6heNU0AfkD4ByAHYIXHtvmp4m/JpSKq/2N6h/zvBkg=="],
|
||||
|
||||
"@turf/isolines": ["@turf/isolines@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "marchingsquares": "^1.3.3", "tslib": "^2.8.1" } }, "sha512-4ZXKxvA/JKkxAXixXhN3UVza5FABsdYgOWXyYm3L5ryTPJVOYTVSSd9A+CAVlv9dZc3YdlsqMqLTXNOOre/kwg=="],
|
||||
|
||||
"@turf/jsts": ["@turf/jsts@2.7.2", "", { "dependencies": { "jsts": "2.7.1" } }, "sha512-zAezGlwWHPyU0zxwcX2wQY3RkRpwuoBmhhNE9HY9kWhFDkCxZ3aWK5URKwa/SWKJbj9aztO+8vtdiBA28KVJFg=="],
|
||||
|
||||
"@turf/kinks": ["@turf/kinks@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-BtxDxGewJR0Q5WR9HKBSxZhirFX+GEH1rD7/EvgDsHS8e1Y5/vNQQUmXdURjdPa4StzaUBsWRU5T3A356gLbPA=="],
|
||||
|
||||
"@turf/length": ["@turf/length@7.2.0", "", { "dependencies": { "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-LBmYN+iCgVtWNLsckVnpQIJENqIIPO63mogazMp23lrDGfWXu07zZQ9ZinJVO5xYurXNhc/QI2xxoqt2Xw90Ig=="],
|
||||
|
||||
"@turf/line-arc": ["@turf/line-arc@7.2.0", "", { "dependencies": { "@turf/circle": "^7.2.0", "@turf/destination": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-kfWzA5oYrTpslTg5fN50G04zSypiYQzjZv3FLjbZkk6kta5fo4JkERKjTeA8x4XNojb+pfmjMBB0yIh2w2dDRw=="],
|
||||
|
||||
"@turf/line-chunk": ["@turf/line-chunk@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/length": "^7.2.0", "@turf/line-slice-along": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-1ODyL5gETtWSL85MPI0lgp/78vl95M39gpeBxePXyDIqx8geDP9kXfAzctuKdxBoR4JmOVM3NT7Fz7h+IEkC+g=="],
|
||||
|
||||
"@turf/line-intersect": ["@turf/line-intersect@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "sweepline-intersections": "^1.5.0", "tslib": "^2.8.1" } }, "sha512-GhCJVEkc8EmggNi85EuVLoXF5T5jNVxmhIetwppiVyJzMrwkYAkZSYB3IBFYGUUB9qiNFnTwungVSsBV/S8ZiA=="],
|
||||
|
||||
"@turf/line-offset": ["@turf/line-offset@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-1+OkYueDCbnEWzbfBh3taVr+3SyM2bal5jfnSEuDiLA6jnlScgr8tn3INo+zwrUkPFZPPAejL1swVyO5TjUahw=="],
|
||||
|
||||
"@turf/line-overlap": ["@turf/line-overlap@7.2.0", "", { "dependencies": { "@turf/boolean-point-on-line": "^7.2.0", "@turf/geojson-rbush": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/line-segment": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/nearest-point-on-line": "^7.2.0", "@types/geojson": "^7946.0.10", "fast-deep-equal": "^3.1.3", "tslib": "^2.8.1" } }, "sha512-NNn7/jg53+N10q2Kyt66bEDqN3101iW/1zA5FW7J6UbKApDFkByh+18YZq1of71kS6oUYplP86WkDp16LFpqqw=="],
|
||||
|
||||
"@turf/line-segment": ["@turf/line-segment@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-E162rmTF9XjVN4rINJCd15AdQGCBlNqeWN3V0YI1vOUpZFNT2ii4SqEMCcH2d+5EheHLL8BWVwZoOsvHZbvaWA=="],
|
||||
|
||||
"@turf/line-slice": ["@turf/line-slice@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/nearest-point-on-line": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-bHotzZIaU1GPV3RMwttYpDrmcvb3X2i1g/WUttPZWtKrEo2VVAkoYdeZ2aFwtogERYS4quFdJ/TDzAtquBC8WQ=="],
|
||||
|
||||
"@turf/line-slice-along": ["@turf/line-slice-along@7.2.0", "", { "dependencies": { "@turf/bearing": "^7.2.0", "@turf/destination": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-4/gPgP0j5Rp+1prbhXqn7kIH/uZTmSgiubUnn67F8nb9zE+MhbRglhSlRYEZxAVkB7VrGwjyolCwvrROhjHp2A=="],
|
||||
|
||||
"@turf/line-split": ["@turf/line-split@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/geojson-rbush": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/line-intersect": "^7.2.0", "@turf/line-segment": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/nearest-point-on-line": "^7.2.0", "@turf/square": "^7.2.0", "@turf/truncate": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-yJTZR+c8CwoKqdW/aIs+iLbuFwAa3Yan+EOADFQuXXIUGps3bJUXx/38rmowNoZbHyP1np1+OtrotyHu5uBsfQ=="],
|
||||
|
||||
"@turf/line-to-polygon": ["@turf/line-to-polygon@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-iKpJqc7EYc5NvlD4KaqrKKO6mXR7YWO/YwtW60E2FnsF/blnsy9OfAOcilYHgH3S/V/TT0VedC7DW7Kgjy2EIA=="],
|
||||
|
||||
"@turf/mask": ["@turf/mask@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "polyclip-ts": "^0.16.8", "tslib": "^2.8.1" } }, "sha512-ulJ6dQqXC0wrjIoqFViXuMUdIPX5Q6GPViZ3kGfeVijvlLM7kTFBsZiPQwALSr5nTQg4Ppf3FD0Jmg8IErPrgA=="],
|
||||
|
||||
"@turf/meta": ["@turf/meta@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10" } }, "sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw=="],
|
||||
|
||||
"@turf/midpoint": ["@turf/midpoint@7.2.0", "", { "dependencies": { "@turf/bearing": "^7.2.0", "@turf/destination": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-AMn5S9aSrbXdE+Q4Rj+T5nLdpfpn+mfzqIaEKkYI021HC0vb22HyhQHsQbSeX+AWcS4CjD1hFsYVcgKI+5qCfw=="],
|
||||
|
||||
"@turf/moran-index": ["@turf/moran-index@7.2.0", "", { "dependencies": { "@turf/distance-weight": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-Aexh1EmXVPJhApr9grrd120vbalIthcIsQ3OAN2Tqwf+eExHXArJEJqGBo9IZiQbIpFJeftt/OvUvlI8BeO1bA=="],
|
||||
|
||||
"@turf/nearest-neighbor-analysis": ["@turf/nearest-neighbor-analysis@7.2.0", "", { "dependencies": { "@turf/area": "^7.2.0", "@turf/bbox": "^7.2.0", "@turf/bbox-polygon": "^7.2.0", "@turf/centroid": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/nearest-point": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-LmP/crXb7gilgsL0wL9hsygqc537W/a1W5r9XBKJT4SKdqjoXX5APJatJfd3nwXbRIqwDH0cDA9/YyFjBPlKnA=="],
|
||||
|
||||
"@turf/nearest-point": ["@turf/nearest-point@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-0wmsqXZ8CGw4QKeZmS+NdjYTqCMC+HXZvM3XAQIU6k6laNLqjad2oS4nDrtcRs/nWDvcj1CR+Io7OiQ6sbpn5Q=="],
|
||||
|
||||
"@turf/nearest-point-on-line": ["@turf/nearest-point-on-line@7.2.0", "", { "dependencies": { "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-UOhAeoDPVewBQV+PWg1YTMQcYpJsIqfW5+EuZ5vJl60XwUa0+kqB/eVfSLNXmHENjKKIlEt9Oy9HIDF4VeWmXA=="],
|
||||
|
||||
"@turf/nearest-point-to-line": ["@turf/nearest-point-to-line@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/point-to-line-distance": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-EorU7Qj30A7nAjh++KF/eTPDlzwuuV4neBz7tmSTB21HKuXZAR0upJsx6M2X1CSyGEgNsbFB0ivNKIvymRTKBw=="],
|
||||
|
||||
"@turf/planepoint": ["@turf/planepoint@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-8Vno01tvi5gThUEKBQ46CmlEKDAwVpkl7stOPFvJYlA1oywjAL4PsmgwjXgleZuFtXQUPBNgv5a42Pf438XP4g=="],
|
||||
|
||||
"@turf/point-grid": ["@turf/point-grid@7.2.0", "", { "dependencies": { "@turf/boolean-within": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-ai7lwBV2FREPW3XiUNohT4opC1hd6+F56qZe20xYhCTkTD9diWjXHiNudQPSmVAUjgMzQGasblQQqvOdL+bJ3Q=="],
|
||||
|
||||
"@turf/point-on-feature": ["@turf/point-on-feature@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/center": "^7.2.0", "@turf/explode": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/nearest-point": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-ksoYoLO9WtJ/qI8VI9ltF+2ZjLWrAjZNsCsu8F7nyGeCh4I8opjf4qVLytFG44XA2qI5yc6iXDpyv0sshvP82Q=="],
|
||||
|
||||
"@turf/point-to-line-distance": ["@turf/point-to-line-distance@7.2.0", "", { "dependencies": { "@turf/bearing": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/nearest-point-on-line": "^7.2.0", "@turf/projection": "^7.2.0", "@turf/rhumb-bearing": "^7.2.0", "@turf/rhumb-distance": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-fB9Rdnb5w5+t76Gho2dYDkGe20eRrFk8CXi4v1+l1PC8YyLXO+x+l3TrtT8HzL/dVaZeepO6WUIsIw3ditTOPg=="],
|
||||
|
||||
"@turf/point-to-polygon-distance": ["@turf/point-to-polygon-distance@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/point-to-line-distance": "^7.2.0", "@turf/polygon-to-line": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-w+WYuINgTiFjoZemQwOaQSje/8Kq+uqJOynvx7+gleQPHyWQ3VtTodtV4LwzVzXz8Sf7Mngx1Jcp2SNai5CJYA=="],
|
||||
|
||||
"@turf/points-within-polygon": ["@turf/points-within-polygon@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-jRKp8/mWNMzA+hKlQhxci97H5nOio9tp14R2SzpvkOt+cswxl+NqTEi1hDd2XetA7tjU0TSoNjEgVY8FfA0S6w=="],
|
||||
|
||||
"@turf/polygon-smooth": ["@turf/polygon-smooth@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-KCp9wF2IEynvGXVhySR8oQ2razKP0zwg99K+fuClP21pSKCFjAPaihPEYq6e8uI/1J7ibjL5++6EMl+LrUTrLg=="],
|
||||
|
||||
"@turf/polygon-tangents": ["@turf/polygon-tangents@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/boolean-within": "^7.2.0", "@turf/explode": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/nearest-point": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-AHUUPmOjiQDrtP/ODXukHBlUG0C/9I1je7zz50OTfl2ZDOdEqFJQC3RyNELwq07grTXZvg5TS5wYx/Y7nsm47g=="],
|
||||
|
||||
"@turf/polygon-to-line": ["@turf/polygon-to-line@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-9jeTN3LiJ933I5sd4K0kwkcivOYXXm1emk0dHorwXeSFSHF+nlYesEW3Hd889wb9lZd7/SVLMUeX/h39mX+vCA=="],
|
||||
|
||||
"@turf/polygonize": ["@turf/polygonize@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/envelope": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-U9v+lBhUPDv+nsg/VcScdiqCB59afO6CHDGrwIl2+5i6Ve+/KQKjpTV/R+NqoC1iMXAEq3brY6HY8Ukp/pUWng=="],
|
||||
|
||||
"@turf/projection": ["@turf/projection@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-/qke5vJScv8Mu7a+fU3RSChBRijE6EVuFHU3RYihMuYm04Vw8dBMIs0enEpoq0ke/IjSbleIrGQNZIMRX9EwZQ=="],
|
||||
|
||||
"@turf/quadrat-analysis": ["@turf/quadrat-analysis@7.2.0", "", { "dependencies": { "@turf/area": "^7.2.0", "@turf/bbox": "^7.2.0", "@turf/bbox-polygon": "^7.2.0", "@turf/centroid": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/point-grid": "^7.2.0", "@turf/random": "^7.2.0", "@turf/square-grid": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-fDQh3+ldYNxUqS6QYlvJ7GZLlCeDZR6tD3ikdYtOsSemwW1n/4gm2xcgWJqy3Y0uszBwxc13IGGY7NGEjHA+0w=="],
|
||||
|
||||
"@turf/random": ["@turf/random@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-fNXs5mOeXsrirliw84S8UCNkpm4RMNbefPNsuCTfZEXhcr1MuHMzq4JWKb4FweMdN1Yx2l/xcytkO0s71cJ50w=="],
|
||||
|
||||
"@turf/rectangle-grid": ["@turf/rectangle-grid@7.2.0", "", { "dependencies": { "@turf/boolean-intersects": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-f0o5ifvy0Ml/nHDJzMNcuSk4h11aa3BfvQNnYQhLpuTQu03j/ICZNlzKTLxwjcUqvxADUifty7Z9CX5W6zky4A=="],
|
||||
|
||||
"@turf/rewind": ["@turf/rewind@7.2.0", "", { "dependencies": { "@turf/boolean-clockwise": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-SZpRAZiZsE22+HVz6pEID+ST25vOdpAMGk5NO1JeqzhpMALIkIGnkG+xnun2CfYHz7wv8/Z0ADiAvei9rkcQYA=="],
|
||||
|
||||
"@turf/rhumb-bearing": ["@turf/rhumb-bearing@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-jbdexlrR8X2ZauUciHx3tRwG+BXoMXke4B8p8/IgDlAfIrVdzAxSQN89FMzIKnjJ/kdLjo9bFGvb92bu31Etug=="],
|
||||
|
||||
"@turf/rhumb-destination": ["@turf/rhumb-destination@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-U9OLgLAHlH4Wfx3fBZf3jvnkDjdTcfRan5eI7VPV1+fQWkOteATpzkiRjCvSYK575GljVwWBjkKca8LziGWitQ=="],
|
||||
|
||||
"@turf/rhumb-distance": ["@turf/rhumb-distance@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-NsijTPON1yOc9tirRPEQQuJ5aQi7pREsqchQquaYKbHNWsexZjcDi4wnw2kM3Si4XjmgynT+2f7aXH7FHarHzw=="],
|
||||
|
||||
"@turf/sample": ["@turf/sample@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-f+ZbcbQJ9glQ/F26re8LadxO0ORafy298EJZe6XtbctRTJrNus6UNAsl8+GYXFqMnXM22tbTAznnJX3ZiWNorA=="],
|
||||
|
||||
"@turf/sector": ["@turf/sector@7.2.0", "", { "dependencies": { "@turf/circle": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/line-arc": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-zL06MjbbMG4DdpiNz+Q9Ax8jsCekt3R76uxeWShulAGkyDB5smdBOUDoRwxn05UX7l4kKv4Ucq2imQXhxKFd1w=="],
|
||||
|
||||
"@turf/shortest-path": ["@turf/shortest-path@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/bbox-polygon": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/clean-coords": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/transform-scale": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-6fpx8feZ2jMSaeRaFdqFShGWkNb+veUOeyLFSHA/aRD9n/e9F2pWZoRbQWKbKTpcKFJ2FnDEqCZnh/GrcAsqWA=="],
|
||||
|
||||
"@turf/simplify": ["@turf/simplify@7.2.0", "", { "dependencies": { "@turf/clean-coords": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-9YHIfSc8BXQfi5IvEMbCeQYqNch0UawIGwbboJaoV8rodhtk6kKV2wrpXdGqk/6Thg6/RWvChJFKVVTjVrULyQ=="],
|
||||
|
||||
"@turf/square": ["@turf/square@7.2.0", "", { "dependencies": { "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-9pMoAGFvqzCDOlO9IRSSBCGXKbl8EwMx6xRRBMKdZgpS0mZgfm9xiptMmx/t1m4qqHIlb/N+3MUF7iMBx6upcA=="],
|
||||
|
||||
"@turf/square-grid": ["@turf/square-grid@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/rectangle-grid": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-EmzGXa90hz+tiCOs9wX+Lak6pH0Vghb7QuX6KZej+pmWi3Yz7vdvQLmy/wuN048+wSkD5c8WUo/kTeNDe7GnmA=="],
|
||||
|
||||
"@turf/standard-deviational-ellipse": ["@turf/standard-deviational-ellipse@7.2.0", "", { "dependencies": { "@turf/center-mean": "^7.2.0", "@turf/ellipse": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/points-within-polygon": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-+uC0pR2nRjm90JvMXe/2xOCZsYV2II1ZZ2zmWcBWv6bcFXBspcxk2QfCC3k0bj6jDapELzoQgnn3cG5lbdQV2w=="],
|
||||
|
||||
"@turf/tag": ["@turf/tag@7.2.0", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-TAFvsbp5TCBqXue8ui+CtcLsPZ6NPC88L8Ad6Hb/R6VAi21qe0U42WJHQYXzWmtThoTNwxi+oKSeFbRDsr0FIA=="],
|
||||
|
||||
"@turf/tesselate": ["@turf/tesselate@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "earcut": "^2.2.4", "tslib": "^2.8.1" } }, "sha512-zHGcG85aOJJu1seCm+CYTJ3UempX4Xtyt669vFG6Hbr/Hc7ii6STQ2ysFr7lJwFtU9uyYhphVrrgwIqwglvI/Q=="],
|
||||
|
||||
"@turf/tin": ["@turf/tin@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-y24Vt3oeE6ZXvyLJamP0Ke02rPlDGE9gF7OFADnR0mT+2uectb0UTIBC3kKzON80TEAlA3GXpKFkCW5Fo/O/Kg=="],
|
||||
|
||||
"@turf/transform-rotate": ["@turf/transform-rotate@7.2.0", "", { "dependencies": { "@turf/centroid": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/rhumb-bearing": "^7.2.0", "@turf/rhumb-destination": "^7.2.0", "@turf/rhumb-distance": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-EMCj0Zqy3cF9d3mGRqDlYnX2ZBXe3LgT+piDR0EuF5c5sjuKErcFcaBIsn/lg1gp4xCNZFinkZ3dsFfgGHf6fw=="],
|
||||
|
||||
"@turf/transform-scale": ["@turf/transform-scale@7.2.0", "", { "dependencies": { "@turf/bbox": "^7.2.0", "@turf/center": "^7.2.0", "@turf/centroid": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/rhumb-bearing": "^7.2.0", "@turf/rhumb-destination": "^7.2.0", "@turf/rhumb-distance": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-HYB+pw938eeI8s1/zSWFy6hq+t38fuUaBb0jJsZB1K9zQ1WjEYpPvKF/0//80zNPlyxLv3cOkeBucso3hzI07A=="],
|
||||
|
||||
"@turf/transform-translate": ["@turf/transform-translate@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/rhumb-destination": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-zAglR8MKCqkzDTjGMIQgbg/f+Q3XcKVzr9cELw5l9CrS1a0VTSDtBZLDm0kWx0ankwtam7ZmI2jXyuQWT8Gbug=="],
|
||||
|
||||
"@turf/triangle-grid": ["@turf/triangle-grid@7.2.0", "", { "dependencies": { "@turf/distance": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/intersect": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-4gcAqWKh9hg6PC5nNSb9VWyLgl821cwf9yR9yEzQhEFfwYL/pZONBWCO1cwVF23vSYMSMm+/TwqxH4emxaArfw=="],
|
||||
|
||||
"@turf/truncate": ["@turf/truncate@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-jyFzxYbPugK4XjV5V/k6Xr3taBjjvo210IbPHJXw0Zh7Y6sF+hGxeRVtSuZ9VP/6oRyqAOHKUrze+OOkPqBgUg=="],
|
||||
|
||||
"@turf/turf": ["@turf/turf@7.2.0", "", { "dependencies": { "@turf/along": "^7.2.0", "@turf/angle": "^7.2.0", "@turf/area": "^7.2.0", "@turf/bbox": "^7.2.0", "@turf/bbox-clip": "^7.2.0", "@turf/bbox-polygon": "^7.2.0", "@turf/bearing": "^7.2.0", "@turf/bezier-spline": "^7.2.0", "@turf/boolean-clockwise": "^7.2.0", "@turf/boolean-concave": "^7.2.0", "@turf/boolean-contains": "^7.2.0", "@turf/boolean-crosses": "^7.2.0", "@turf/boolean-disjoint": "^7.2.0", "@turf/boolean-equal": "^7.2.0", "@turf/boolean-intersects": "^7.2.0", "@turf/boolean-overlap": "^7.2.0", "@turf/boolean-parallel": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/boolean-point-on-line": "^7.2.0", "@turf/boolean-touches": "^7.2.0", "@turf/boolean-valid": "^7.2.0", "@turf/boolean-within": "^7.2.0", "@turf/buffer": "^7.2.0", "@turf/center": "^7.2.0", "@turf/center-mean": "^7.2.0", "@turf/center-median": "^7.2.0", "@turf/center-of-mass": "^7.2.0", "@turf/centroid": "^7.2.0", "@turf/circle": "^7.2.0", "@turf/clean-coords": "^7.2.0", "@turf/clone": "^7.2.0", "@turf/clusters": "^7.2.0", "@turf/clusters-dbscan": "^7.2.0", "@turf/clusters-kmeans": "^7.2.0", "@turf/collect": "^7.2.0", "@turf/combine": "^7.2.0", "@turf/concave": "^7.2.0", "@turf/convex": "^7.2.0", "@turf/destination": "^7.2.0", "@turf/difference": "^7.2.0", "@turf/dissolve": "^7.2.0", "@turf/distance": "^7.2.0", "@turf/distance-weight": "^7.2.0", "@turf/ellipse": "^7.2.0", "@turf/envelope": "^7.2.0", "@turf/explode": "^7.2.0", "@turf/flatten": "^7.2.0", "@turf/flip": "^7.2.0", "@turf/geojson-rbush": "^7.2.0", "@turf/great-circle": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/hex-grid": "^7.2.0", "@turf/interpolate": "^7.2.0", "@turf/intersect": "^7.2.0", "@turf/invariant": "^7.2.0", "@turf/isobands": "^7.2.0", "@turf/isolines": "^7.2.0", "@turf/kinks": "^7.2.0", "@turf/length": "^7.2.0", "@turf/line-arc": "^7.2.0", "@turf/line-chunk": "^7.2.0", "@turf/line-intersect": "^7.2.0", "@turf/line-offset": "^7.2.0", "@turf/line-overlap": "^7.2.0", "@turf/line-segment": "^7.2.0", "@turf/line-slice": "^7.2.0", "@turf/line-slice-along": "^7.2.0", "@turf/line-split": "^7.2.0", "@turf/line-to-polygon": "^7.2.0", "@turf/mask": "^7.2.0", "@turf/meta": "^7.2.0", "@turf/midpoint": "^7.2.0", "@turf/moran-index": "^7.2.0", "@turf/nearest-neighbor-analysis": "^7.2.0", "@turf/nearest-point": "^7.2.0", "@turf/nearest-point-on-line": "^7.2.0", "@turf/nearest-point-to-line": "^7.2.0", "@turf/planepoint": "^7.2.0", "@turf/point-grid": "^7.2.0", "@turf/point-on-feature": "^7.2.0", "@turf/point-to-line-distance": "^7.2.0", "@turf/point-to-polygon-distance": "^7.2.0", "@turf/points-within-polygon": "^7.2.0", "@turf/polygon-smooth": "^7.2.0", "@turf/polygon-tangents": "^7.2.0", "@turf/polygon-to-line": "^7.2.0", "@turf/polygonize": "^7.2.0", "@turf/projection": "^7.2.0", "@turf/quadrat-analysis": "^7.2.0", "@turf/random": "^7.2.0", "@turf/rectangle-grid": "^7.2.0", "@turf/rewind": "^7.2.0", "@turf/rhumb-bearing": "^7.2.0", "@turf/rhumb-destination": "^7.2.0", "@turf/rhumb-distance": "^7.2.0", "@turf/sample": "^7.2.0", "@turf/sector": "^7.2.0", "@turf/shortest-path": "^7.2.0", "@turf/simplify": "^7.2.0", "@turf/square": "^7.2.0", "@turf/square-grid": "^7.2.0", "@turf/standard-deviational-ellipse": "^7.2.0", "@turf/tag": "^7.2.0", "@turf/tesselate": "^7.2.0", "@turf/tin": "^7.2.0", "@turf/transform-rotate": "^7.2.0", "@turf/transform-scale": "^7.2.0", "@turf/transform-translate": "^7.2.0", "@turf/triangle-grid": "^7.2.0", "@turf/truncate": "^7.2.0", "@turf/union": "^7.2.0", "@turf/unkink-polygon": "^7.2.0", "@turf/voronoi": "^7.2.0", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-G1kKBu4hYgoNoRJgnpJohNuS7bLnoWHZ2G/4wUMym5xOSiYah6carzdTEsMoTsauyi7ilByWHx5UHwbjjCVcBw=="],
|
||||
|
||||
"@turf/union": ["@turf/union@7.2.0", "", { "dependencies": { "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "polyclip-ts": "^0.16.8", "tslib": "^2.8.1" } }, "sha512-Xex/cfKSmH0RZRWSJl4RLlhSmEALVewywiEXcu0aIxNbuZGTcpNoI0h4oLFrE/fUd0iBGFg/EGLXRL3zTfpg6g=="],
|
||||
|
||||
"@turf/unkink-polygon": ["@turf/unkink-polygon@7.2.0", "", { "dependencies": { "@turf/area": "^7.2.0", "@turf/boolean-point-in-polygon": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/meta": "^7.2.0", "@types/geojson": "^7946.0.10", "rbush": "^3.0.1", "tslib": "^2.8.1" } }, "sha512-dFPfzlIgkEr15z6oXVxTSWshWi51HeITGVFtl1GAKGMtiXJx1uMqnfRsvljqEjaQu/4AzG1QAp3b+EkSklQSiQ=="],
|
||||
|
||||
"@turf/voronoi": ["@turf/voronoi@7.2.0", "", { "dependencies": { "@turf/clone": "^7.2.0", "@turf/helpers": "^7.2.0", "@turf/invariant": "^7.2.0", "@types/d3-voronoi": "^1.1.12", "@types/geojson": "^7946.0.10", "d3-voronoi": "1.1.2", "tslib": "^2.8.1" } }, "sha512-3K6N0LtJsWTXxPb/5N2qD9e8f4q8+tjTbGV3lE3v8x06iCnNlnuJnqM5NZNPpvgvCatecBkhClO3/3RndE61Fw=="],
|
||||
|
||||
"@types/d3-voronoi": ["@types/d3-voronoi@1.1.12", "", {}, "sha512-DauBl25PKZZ0WVJr42a6CNvI6efsdzofl9sajqZr2Gf5Gu733WkDdUGiPkUHXiUvYGzNNlFQde2wdZdfQPG+yw=="],
|
||||
|
||||
"@types/emscripten": ["@types/emscripten@1.40.1", "", {}, "sha512-sr53lnYkQNhjHNN0oJDdUm5564biioI5DuOpycufDVK7D3y+GR3oUswe2rlwY1nPNyusHbrJ9WoTyIHl4/Bpwg=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||
@ -441,7 +676,9 @@
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"bits-ui": ["bits-ui@2.9.2", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.29.1", "svelte-toolbelt": "^0.9.3", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-GGbyr4oVKtHin//Q0AhlygkasmfWt328VjsnmB3sP+h8Sh+Eyghm+1AQ8o+xQMDCYbdL35JZ9UZGTZYTMar4Uw=="],
|
||||
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||
|
||||
"bits-ui": ["bits-ui@2.11.5", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.31.1", "svelte-toolbelt": "^0.10.4", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-d7b6HrrCUeK261c777agFz0G5lx13RMA0DT022e4SRuIjI3bZ8ci53YxIZ2/jpXTmeAeqeShyC+Mgibh9OeW9A=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||
|
||||
@ -471,6 +708,8 @@
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"concaveman": ["concaveman@1.2.1", "", { "dependencies": { "point-in-polygon": "^1.1.0", "rbush": "^3.0.1", "robust-predicates": "^2.0.4", "tinyqueue": "^2.0.3" } }, "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw=="],
|
||||
|
||||
"consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="],
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||
@ -479,6 +718,12 @@
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"d3-array": ["d3-array@1.2.4", "", {}, "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="],
|
||||
|
||||
"d3-geo": ["d3-geo@1.7.1", "", { "dependencies": { "d3-array": "1" } }, "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw=="],
|
||||
|
||||
"d3-voronoi": ["d3-voronoi@1.1.2", "", {}, "sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"dedent": ["dedent@1.5.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="],
|
||||
@ -555,6 +800,10 @@
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"geojson-equality-ts": ["geojson-equality-ts@1.0.2", "", { "dependencies": { "@types/geojson": "^7946.0.14" } }, "sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ=="],
|
||||
|
||||
"geojson-polygon-self-intersections": ["geojson-polygon-self-intersections@1.2.1", "", { "dependencies": { "rbush": "^2.0.1" } }, "sha512-/QM1b5u2d172qQVO//9CGRa49jEmclKEsYOQmWP9ooEjj63tBM51m2805xsbxkzlEELQ2REgTf700gUhhlegxA=="],
|
||||
|
||||
"geojson-vt": ["geojson-vt@4.0.2", "", {}, "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A=="],
|
||||
|
||||
"get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
|
||||
@ -629,6 +878,8 @@
|
||||
|
||||
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
||||
|
||||
"jsts": ["jsts@2.7.1", "", {}, "sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg=="],
|
||||
|
||||
"jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="],
|
||||
|
||||
"jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
|
||||
@ -703,6 +954,8 @@
|
||||
|
||||
"maplibre-gl": ["maplibre-gl@5.5.0", "", { "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^2.0.6", "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "@maplibre/maplibre-gl-style-spec": "^23.2.2", "@types/geojson": "^7946.0.16", "@types/geojson-vt": "3.2.5", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", "@types/supercluster": "^7.1.3", "earcut": "^3.0.1", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "global-prefix": "^4.0.0", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", "pbf": "^3.3.0", "potpack": "^2.0.0", "quickselect": "^3.0.0", "supercluster": "^8.0.1", "tinyqueue": "^3.0.0", "vt-pbf": "^3.1.3" } }, "sha512-p8AOPuzzqn1ZA9gcXxKw0IED715we/2Owa/YUr6PANmgMvNMe/JG+V/C1hRra43Wm62Biz+Aa8AgbOLJimA8tA=="],
|
||||
|
||||
"marchingsquares": ["marchingsquares@1.3.3", "", {}, "sha512-gz6nNQoVK7Lkh2pZulrT4qd4347S/toG9RXH2pyzhLgkL5mLkBoqgv4EvAGXcV0ikDW72n/OQb3Xe8bGagQZCg=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
@ -749,7 +1002,7 @@
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"pbf": ["pbf@3.3.0", "", { "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q=="],
|
||||
"pbf": ["pbf@4.0.1", "", { "dependencies": { "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
@ -759,6 +1012,12 @@
|
||||
|
||||
"pmtiles": ["pmtiles@4.3.0", "", { "dependencies": { "fflate": "^0.8.2" } }, "sha512-wnzQeSiYT/MyO63o7AVxwt7+uKqU0QUy2lHrivM7GvecNy0m1A4voVyGey7bujnEW5Hn+ZzLdvHPoFaqrOzbPA=="],
|
||||
|
||||
"point-in-polygon": ["point-in-polygon@1.1.0", "", {}, "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw=="],
|
||||
|
||||
"point-in-polygon-hao": ["point-in-polygon-hao@1.2.4", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ=="],
|
||||
|
||||
"polyclip-ts": ["polyclip-ts@0.16.8", "", { "dependencies": { "bignumber.js": "^9.1.0", "splaytree-ts": "^1.0.2" } }, "sha512-JPtKbDRuPEuAjuTdhR62Gph7Is2BS1Szx69CFOO3g71lpJDFo78k4tFyi+qFOMVPePEzdSKkpGU3NBXPHHjvKQ=="],
|
||||
|
||||
"postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="],
|
||||
|
||||
"postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="],
|
||||
@ -787,6 +1046,8 @@
|
||||
|
||||
"quickselect": ["quickselect@3.0.0", "", {}, "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g=="],
|
||||
|
||||
"rbush": ["rbush@3.0.1", "", { "dependencies": { "quickselect": "^2.0.0" } }, "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="],
|
||||
@ -797,11 +1058,13 @@
|
||||
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
|
||||
|
||||
"rollup": ["rollup@4.41.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.41.1", "@rollup/rollup-android-arm64": "4.41.1", "@rollup/rollup-darwin-arm64": "4.41.1", "@rollup/rollup-darwin-x64": "4.41.1", "@rollup/rollup-freebsd-arm64": "4.41.1", "@rollup/rollup-freebsd-x64": "4.41.1", "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", "@rollup/rollup-linux-arm-musleabihf": "4.41.1", "@rollup/rollup-linux-arm64-gnu": "4.41.1", "@rollup/rollup-linux-arm64-musl": "4.41.1", "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-musl": "4.41.1", "@rollup/rollup-linux-s390x-gnu": "4.41.1", "@rollup/rollup-linux-x64-gnu": "4.41.1", "@rollup/rollup-linux-x64-musl": "4.41.1", "@rollup/rollup-win32-arm64-msvc": "4.41.1", "@rollup/rollup-win32-ia32-msvc": "4.41.1", "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"runed": ["runed@0.29.2", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA=="],
|
||||
"runed": ["runed@0.31.1", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-v3czcTnO+EJjiPvD4dwIqfTdHLZ8oH0zJheKqAHh9QMViY7Qb29UlAMRpX7ZtHh7AFqV60KmfxaJ9QMy+L1igQ=="],
|
||||
|
||||
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
|
||||
|
||||
@ -815,8 +1078,12 @@
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"skmeans": ["skmeans@0.9.7", "", {}, "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"splaytree-ts": ["splaytree-ts@1.0.2", "", {}, "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA=="],
|
||||
|
||||
"sql.js": ["sql.js@1.13.0", "", {}, "sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA=="],
|
||||
|
||||
"sqlite-wasm-kysely": ["sqlite-wasm-kysely@0.3.0", "", { "dependencies": { "@sqlite.org/sqlite-wasm": "^3.48.0-build2" }, "peerDependencies": { "kysely": "*" } }, "sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg=="],
|
||||
@ -839,7 +1106,9 @@
|
||||
|
||||
"svelte-maplibre-gl": ["svelte-maplibre-gl@0.1.8", "", { "peerDependencies": { "@deck.gl/core": "^9.1.0", "@deck.gl/layers": "^9.1.0", "@deck.gl/mapbox": "^9.1.0", "maplibre-contour": ">=0.1.0", "maplibre-gl": "^5.0.0 || ^4.0.0", "pmtiles": "^4.0.0", "svelte": ">=5.0.0", "terra-draw": "^1.0.0", "terra-draw-maplibre-gl-adapter": "^1.0.3" } }, "sha512-YvMo25q/rpNDNE4iBvOuYYt+E+6jT+PBLxX7vR20LE5ZD2K3cLV9cR34S4SX7w81E00lP7InD2+CvFr7T0vBxg=="],
|
||||
|
||||
"svelte-toolbelt": ["svelte-toolbelt@0.9.3", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.29.0", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw=="],
|
||||
"svelte-toolbelt": ["svelte-toolbelt@0.10.5", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.29.0", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-8e+eWTgxw1aiLxhDE8Rb1X6AoLitqpJz+WhAul2W7W58C8KoLoJQf1TgQdFPBiCPJ0Jg5y0Zi1uyua9em4VS0w=="],
|
||||
|
||||
"sweepline-intersections": ["sweepline-intersections@1.5.0", "", { "dependencies": { "tinyqueue": "^2.0.0" } }, "sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ=="],
|
||||
|
||||
"tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
|
||||
|
||||
@ -867,6 +1136,10 @@
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"topojson-client": ["topojson-client@3.1.0", "", { "dependencies": { "commander": "2" }, "bin": { "topo2geo": "bin/topo2geo", "topomerge": "bin/topomerge", "topoquantize": "bin/topoquantize" } }, "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw=="],
|
||||
|
||||
"topojson-server": ["topojson-server@3.0.1", "", { "dependencies": { "commander": "2" }, "bin": { "geo2topo": "bin/geo2topo" } }, "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw=="],
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
@ -941,22 +1214,52 @@
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"concaveman/robust-predicates": ["robust-predicates@2.0.4", "", {}, "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg=="],
|
||||
|
||||
"concaveman/tinyqueue": ["tinyqueue@2.0.3", "", {}, "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"geojson-polygon-self-intersections/rbush": ["rbush@2.0.2", "", { "dependencies": { "quickselect": "^1.0.1" } }, "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA=="],
|
||||
|
||||
"global-prefix/which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
||||
|
||||
"maplibre-gl/@mapbox/point-geometry": ["@mapbox/point-geometry@0.1.0", "", {}, "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="],
|
||||
|
||||
"maplibre-gl/@mapbox/vector-tile": ["@mapbox/vector-tile@1.3.1", "", { "dependencies": { "@mapbox/point-geometry": "~0.1.0" } }, "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw=="],
|
||||
|
||||
"maplibre-gl/earcut": ["earcut@3.0.1", "", {}, "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw=="],
|
||||
|
||||
"maplibre-gl/pbf": ["pbf@3.3.0", "", { "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"rbush/quickselect": ["quickselect@2.0.0", "", {}, "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="],
|
||||
|
||||
"svelte-toolbelt/runed": ["runed@0.29.2", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA=="],
|
||||
|
||||
"sweepline-intersections/tinyqueue": ["tinyqueue@2.0.3", "", {}, "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="],
|
||||
|
||||
"tailwind-variants/tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="],
|
||||
|
||||
"topojson-client/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||
|
||||
"topojson-server/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||
|
||||
"vaul-svelte/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="],
|
||||
|
||||
"vaul-svelte/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="],
|
||||
|
||||
"vt-pbf/@mapbox/point-geometry": ["@mapbox/point-geometry@0.1.0", "", {}, "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="],
|
||||
|
||||
"vt-pbf/@mapbox/vector-tile": ["@mapbox/vector-tile@1.3.1", "", { "dependencies": { "@mapbox/point-geometry": "~0.1.0" } }, "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw=="],
|
||||
|
||||
"vt-pbf/pbf": ["pbf@3.3.0", "", { "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"geojson-polygon-self-intersections/rbush/quickselect": ["quickselect@1.1.1", "", {}, "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ=="],
|
||||
|
||||
"global-prefix/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"language": "Deutsch - German",
|
||||
"language": {
|
||||
"name": "Deutsch - German",
|
||||
"tts": "de",
|
||||
"speechSynthesis": "de-DE",
|
||||
"valhalla": "de"
|
||||
},
|
||||
"saved": {
|
||||
"home": "Heim",
|
||||
"school": "Schule",
|
||||
@ -101,7 +106,7 @@
|
||||
"ask-question": "Stellen Sie eine Frage zu diesem Ort ..."
|
||||
},
|
||||
"in-route": {
|
||||
"left": "links",
|
||||
"left": "übrig",
|
||||
"end-trip": "Route beenden",
|
||||
"share-code": "Code teilen",
|
||||
"stop-sharing": "Standortfreigabe beenden",
|
||||
@ -155,5 +160,9 @@
|
||||
"poi": {
|
||||
"fuel": "Tankstelle",
|
||||
"parking": "Parken"
|
||||
},
|
||||
"routing": {
|
||||
"off-route": "Sie sind von der Route abgekommen",
|
||||
"back-on-route": "Sie sind wieder auf der Route"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"language": "English",
|
||||
"language": {
|
||||
"name": "English",
|
||||
"tts": "en",
|
||||
"speechSynthesis": "en-US",
|
||||
"valhalla": "en"
|
||||
},
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"loading": "Loading...",
|
||||
@ -73,7 +78,8 @@
|
||||
"settings": {
|
||||
"header": "Settings",
|
||||
"general": "General",
|
||||
"map": "Map"
|
||||
"map": "Map",
|
||||
"connections": "Connections"
|
||||
},
|
||||
"info": {
|
||||
"dropped": "Dropped Pin",
|
||||
@ -143,6 +149,18 @@
|
||||
"header": "Nearby Points of Interest",
|
||||
"no-poi": "No points of interest found nearby.",
|
||||
"loading": "Loading nearby points of interest..."
|
||||
},
|
||||
"calendar": {
|
||||
"header": "Calendar",
|
||||
"add": "Add Calendar",
|
||||
"connect": "Connect Calendar",
|
||||
"probing-server": "Probing server...",
|
||||
"discovering-calendars": "Discovering calendars...",
|
||||
"choose": "Choose calendars to add:"
|
||||
},
|
||||
"appearance": {
|
||||
"header": "Appearance",
|
||||
"bigger-buttons-in-drive": "Bigger Buttons while Driving"
|
||||
}
|
||||
},
|
||||
"unsave": "Unsave",
|
||||
@ -155,5 +173,21 @@
|
||||
"poi": {
|
||||
"fuel": "Fuel Station",
|
||||
"parking": "Parking"
|
||||
},
|
||||
"routing": {
|
||||
"off-route": "You went off route",
|
||||
"back-on-route": "You are back on route"
|
||||
},
|
||||
"open": "Open",
|
||||
"submit": "Submit",
|
||||
"done": "Done",
|
||||
"delete": "Delete",
|
||||
"calendar": {
|
||||
"location": "Location",
|
||||
"no-location": "No location",
|
||||
"start": "Start",
|
||||
"end": "End",
|
||||
"no-start": "No start",
|
||||
"no-end": "No end"
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"@types/libsodium-wrappers": "^0.7.14",
|
||||
"@types/node": "^22.15.24",
|
||||
"@types/pako": "^2.0.3",
|
||||
"bits-ui": "^2.8.6",
|
||||
"bits-ui": "^2.11.0",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"git-format-staged": "^3.1.1",
|
||||
@ -42,8 +42,10 @@
|
||||
"dependencies": {
|
||||
"@diffusionstudio/vits-web": "^1.0.3",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@mapbox/vector-tile": "^2.0.4",
|
||||
"@tauri-apps/plugin-fs": "~2",
|
||||
"@tauri-apps/plugin-upload": "~2",
|
||||
"@turf/turf": "^7.2.0",
|
||||
"@types/sql.js": "^1.4.9",
|
||||
"buffer": "^6.0.3",
|
||||
"eslint": "^9.29.0",
|
||||
@ -54,6 +56,7 @@
|
||||
"libsodium-wrappers": "^0.7.15",
|
||||
"opening_hours": "^3.8.0",
|
||||
"pako": "^2.1.0",
|
||||
"pbf": "^4.0.1",
|
||||
"pmtiles": "^4.3.0",
|
||||
"sql.js": "^1.13.0",
|
||||
"svelte-maplibre-gl": "^0.1.8",
|
||||
|
||||
BIN
public/img/no-overtaking.png
Normal file
BIN
public/img/no-overtaking.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
1421
src-tauri/Cargo.lock
generated
1421
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -27,3 +27,10 @@ tauri-plugin-keep-screen-on = "0.1.2"
|
||||
tauri-plugin-upload = "2"
|
||||
tauri-plugin-fs = "2"
|
||||
tauri-plugin-tts = { git = "https://github.com/cfpwastaken/tauri-plugin-tts.git" }
|
||||
tauri-plugin-duck = { git = "https://git.picoscratch.de/trafficcue/tauri-plugin-duck.git" }
|
||||
reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls"] }
|
||||
diqwest = "3.1.0"
|
||||
minidom = "0.17.0"
|
||||
icalendar = { version = "0.17.5", features = ["chrono-tz"] }
|
||||
anyhow = "1.0.100"
|
||||
chrono = "0.4.42"
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
]
|
||||
},
|
||||
"upload:allow-download",
|
||||
"tts:allow-speak"
|
||||
"tts:allow-speak",
|
||||
"duck:default"
|
||||
]
|
||||
}
|
||||
389
src-tauri/src/dav.rs
Normal file
389
src-tauri/src/dav.rs
Normal file
@ -0,0 +1,389 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use diqwest::WithDigestAuth;
|
||||
use icalendar::{Calendar};
|
||||
use minidom::Element;
|
||||
use reqwest::{
|
||||
header::{CONTENT_TYPE, USER_AGENT}, Client, Method, RequestBuilder, Url
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
static PRINCIPAL_BODY: &str = r#"
|
||||
<d:propfind xmlns:d="DAV:">
|
||||
<d:prop>
|
||||
<d:current-user-principal />
|
||||
</d:prop>
|
||||
</d:propfind>
|
||||
"#;
|
||||
|
||||
static HOMESET_BODY: &str = r#"
|
||||
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
||||
<d:prop>
|
||||
<c:calendar-home-set />
|
||||
</d:prop>
|
||||
</d:propfind>
|
||||
"#;
|
||||
|
||||
static CAL_BODY: &str = r#"
|
||||
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" >
|
||||
<d:prop>
|
||||
<d:displayname />
|
||||
<d:resourcetype />
|
||||
<c:supported-calendar-component-set />
|
||||
</d:prop>
|
||||
</d:propfind>
|
||||
"#;
|
||||
|
||||
static EVENT_BODY: &str = r#"
|
||||
<c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
||||
<d:prop>
|
||||
<d:getetag />
|
||||
<c:calendar-data />
|
||||
</d:prop>
|
||||
<c:filter>
|
||||
<c:comp-filter name="VCALENDAR">
|
||||
<c:comp-filter name="VEVENT" >
|
||||
<c:time-range start="{start}" end="{end}" />
|
||||
</c:comp-filter>
|
||||
</c:comp-filter>
|
||||
</c:filter>
|
||||
</c:calendar-query>
|
||||
"#;
|
||||
|
||||
pub fn find_elems(root: &Element, tag: String) -> Vec<&Element> {
|
||||
let mut elems: Vec<&Element> = Vec::new();
|
||||
|
||||
for el in root.children() {
|
||||
if el.name() == tag {
|
||||
elems.push(el);
|
||||
} else {
|
||||
let ret = find_elems(el, tag.clone());
|
||||
elems.extend(ret);
|
||||
}
|
||||
}
|
||||
elems
|
||||
}
|
||||
|
||||
pub fn find_elem(root: &Element, tag: String) -> Option<&Element> {
|
||||
if root.name() == tag {
|
||||
return Some(root);
|
||||
}
|
||||
|
||||
for el in root.children() {
|
||||
if el.name() == tag {
|
||||
return Some(el);
|
||||
} else {
|
||||
let ret = find_elem(el, tag.clone());
|
||||
if ret.is_some() {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum AuthScheme {
|
||||
Basic,
|
||||
Digest,
|
||||
}
|
||||
|
||||
impl std::str::FromStr for AuthScheme {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"basic" => Ok(AuthScheme::Basic),
|
||||
"digest" => Ok(AuthScheme::Digest),
|
||||
_ => Err(format!("Invalid auth scheme: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AuthScheme {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AuthScheme::Basic => write!(f, "Basic"),
|
||||
AuthScheme::Digest => write!(f, "Digest"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReqwestAuth {
|
||||
fn send_authed(self, scheme: &AuthScheme, username: &str, password: &str) -> impl std::future::Future<Output = anyhow::Result<reqwest::Response>> + Send;
|
||||
}
|
||||
|
||||
impl ReqwestAuth for RequestBuilder {
|
||||
fn send_authed(self, scheme: &AuthScheme, username: &str, password: &str) -> impl std::future::Future<Output = anyhow::Result<reqwest::Response>> + Send {
|
||||
async move {
|
||||
let res = match scheme {
|
||||
AuthScheme::Basic => self.basic_auth(username, Some(password)).send().await?,
|
||||
AuthScheme::Digest => self.send_with_digest_auth(username, password).await?,
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn request(
|
||||
client: &Client,
|
||||
method: Method,
|
||||
url: Url,
|
||||
body: String,
|
||||
depth: u8,
|
||||
credentials: &DAVCredentials,
|
||||
) -> anyhow::Result<Element> {
|
||||
let res = client
|
||||
.request(method, url)
|
||||
.header("Depth", depth.to_string())
|
||||
.header(USER_AGENT, "TrafficCue")
|
||||
.header(CONTENT_TYPE, "application/xml")
|
||||
.body(body)
|
||||
.send_authed(&credentials.scheme, &credentials.username, &credentials.password)
|
||||
.await?;
|
||||
let status = res.status();
|
||||
if !status.is_success() {
|
||||
Err(anyhow::anyhow!("Request failed with status: {}", status))?;
|
||||
}
|
||||
|
||||
let text = res.text().await?;
|
||||
let root: Element = text.parse()?;
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct DAVCredentials {
|
||||
pub scheme: AuthScheme,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
impl DAVCredentials {
|
||||
pub async fn find_scheme(url: &Url) -> anyhow::Result<AuthScheme> {
|
||||
let client = Client::new();
|
||||
let res = client
|
||||
.request(Method::from_bytes(b"PROPFIND").expect("Invalid method"), url.clone())
|
||||
.header(USER_AGENT, "TrafficCue")
|
||||
.send()
|
||||
.await;
|
||||
if res.is_err() {
|
||||
return Err(anyhow::anyhow!("Failed to connect to server"));
|
||||
}
|
||||
let res = res.unwrap();
|
||||
if res.status().is_success() {
|
||||
return Err(anyhow::anyhow!("Server did not require authentication"));
|
||||
}
|
||||
let headers = res.headers();
|
||||
if let Some(www_auth) = headers.get("www-authenticate") {
|
||||
let www_auth = www_auth.to_str().unwrap_or("");
|
||||
if www_auth.to_lowercase().contains("digest") {
|
||||
return Ok(AuthScheme::Digest);
|
||||
} else if www_auth.to_lowercase().contains("basic") {
|
||||
return Ok(AuthScheme::Basic);
|
||||
}
|
||||
}
|
||||
Err(anyhow::anyhow!("Could not determine authentication scheme"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Client for interacting with a CalDAV server.
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// let client = DAVClient::new("https://cal.example.com/.well-known/caldav", DAVCredentials {
|
||||
/// username: "user".into(),
|
||||
/// password: "pass".into(),
|
||||
/// });
|
||||
/// client.init().await?;
|
||||
/// let calendars = client.get_calendars().await?;
|
||||
/// for cal in calendars {
|
||||
/// println!("{}", cal);
|
||||
/// let events = cal.get_events(&client.credentials).await?;
|
||||
/// for event in events {
|
||||
/// println!("Event:\n{}", event);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct DAVClient {
|
||||
pub url: Url,
|
||||
pub credentials: DAVCredentials,
|
||||
client: Client,
|
||||
|
||||
principal_url: Option<Url>,
|
||||
cal_url: Option<Url>,
|
||||
}
|
||||
|
||||
impl DAVClient {
|
||||
/// **Ensure the URL is actually the CalDAV endpoint, and not just the base URL.**
|
||||
pub fn new(string_url: &str, credentials: DAVCredentials) -> Self {
|
||||
let url = Url::parse(string_url).expect("Invalid URL");
|
||||
Self {
|
||||
url,
|
||||
credentials,
|
||||
client: Client::new(),
|
||||
principal_url: None,
|
||||
cal_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn init(&mut self) -> anyhow::Result<()> {
|
||||
self.get_principal().await?;
|
||||
self.get_cal_homeset().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn _propfind(
|
||||
&mut self,
|
||||
url: Url,
|
||||
body: String,
|
||||
depth: u8,
|
||||
) -> anyhow::Result<Element> {
|
||||
let method = Method::from_bytes(b"PROPFIND").expect("Invalid method");
|
||||
let root = request(
|
||||
&self.client,
|
||||
method,
|
||||
url,
|
||||
body,
|
||||
depth,
|
||||
&self.credentials,
|
||||
).await?;
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
pub async fn get_principal(&mut self) -> anyhow::Result<()> {
|
||||
let root: Element = self
|
||||
._propfind(self.url.clone(), PRINCIPAL_BODY.to_string(), 0)
|
||||
.await?;
|
||||
let principal = find_elem(&root, "current-user-principal".to_string())
|
||||
.ok_or(anyhow::anyhow!("No principal found"))?;
|
||||
let principal_href =
|
||||
find_elem(principal, "href".to_string()).ok_or(anyhow::anyhow!("No href found"))?;
|
||||
let h_str = principal_href.text();
|
||||
let mut p_url = self.url.clone();
|
||||
p_url.set_path(&h_str);
|
||||
self.principal_url = Some(p_url);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_cal_homeset(&mut self) -> anyhow::Result<()> {
|
||||
if self.principal_url.is_none() {
|
||||
return Err(anyhow::anyhow!("Principal URL not set"));
|
||||
}
|
||||
let root: Element = self
|
||||
._propfind(
|
||||
self.principal_url.clone().unwrap(),
|
||||
HOMESET_BODY.to_string(),
|
||||
0,
|
||||
)
|
||||
.await?;
|
||||
let homeset = find_elem(&root, "calendar-home-set".to_string())
|
||||
.ok_or(anyhow::anyhow!("No calendar-home-set found"))?;
|
||||
let href =
|
||||
find_elem(homeset, "href".to_string()).ok_or(anyhow::anyhow!("No href found"))?;
|
||||
let h_str = href.text();
|
||||
let mut cal_url = self.url.clone();
|
||||
cal_url.set_path(&h_str);
|
||||
self.cal_url = Some(cal_url.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_calendars(&mut self) -> anyhow::Result<Vec<DAVCalendar>> {
|
||||
if self.cal_url.is_none() {
|
||||
return Err(anyhow::anyhow!("Calendar URL not set"));
|
||||
}
|
||||
let root: Element = self
|
||||
._propfind(self.cal_url.clone().unwrap(), CAL_BODY.to_string(), 1)
|
||||
.await?;
|
||||
let reps = find_elems(&root, "response".to_string());
|
||||
let mut calendars = Vec::new();
|
||||
for rep in reps {
|
||||
let displayname = find_elem(rep, "displayname".to_string())
|
||||
.ok_or(anyhow::anyhow!("No displayname found"))?
|
||||
.text();
|
||||
if displayname == "" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let resourcetype = find_elem(rep, "resourcetype".to_string())
|
||||
.ok_or(anyhow::anyhow!("No resourcetype found"))?;
|
||||
let is_calendar = find_elem(resourcetype, "calendar".to_string()).is_some();
|
||||
if !is_calendar {
|
||||
continue;
|
||||
}
|
||||
|
||||
let url_base = self.cal_url.as_ref().unwrap();
|
||||
let mut href = url_base.clone();
|
||||
href.set_path(
|
||||
&find_elem(rep, "href".to_string())
|
||||
.ok_or(anyhow::anyhow!("No href found"))?
|
||||
.text(),
|
||||
);
|
||||
calendars.push(DAVCalendar {
|
||||
name: displayname,
|
||||
url: href,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(calendars)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a CalDAV calendar.
|
||||
/// Can be used to fetch events.
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct DAVCalendar {
|
||||
pub name: String,
|
||||
pub url: Url,
|
||||
}
|
||||
|
||||
impl Display for DAVCalendar {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} ({})", self.name, self.url)
|
||||
}
|
||||
}
|
||||
|
||||
impl DAVCalendar {
|
||||
pub fn new(name: &str, url: Url) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
url,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn _report(
|
||||
&mut self,
|
||||
credentials: &DAVCredentials,
|
||||
body: String,
|
||||
) -> anyhow::Result<Element> {
|
||||
let client = Client::new();
|
||||
let method = Method::from_bytes(b"REPORT").expect("Invalid method");
|
||||
let root = request(
|
||||
&client,
|
||||
method,
|
||||
self.url.clone(),
|
||||
body,
|
||||
1,
|
||||
credentials,
|
||||
).await?;
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
pub async fn get_events(
|
||||
&mut self,
|
||||
credentials: &DAVCredentials,
|
||||
) -> anyhow::Result<Vec<Calendar>> {
|
||||
let mut events = Vec::new();
|
||||
let time = chrono::Utc::now();
|
||||
let start = time.format("%Y%m%dT000000Z").to_string();
|
||||
let end = time.format("%Y%m%dT235959Z").to_string();
|
||||
let body = EVENT_BODY.replace("{start}", &start).replace("{end}", &end);
|
||||
let root = self._report(credentials, body).await?;
|
||||
let datas = find_elems(&root, "calendar-data".to_string());
|
||||
for data in datas {
|
||||
let etext = data.text();
|
||||
let cal: Calendar = etext.parse().unwrap();
|
||||
events.push(cal);
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
}
|
||||
64
src-tauri/src/dav_commands.rs
Normal file
64
src-tauri/src/dav_commands.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use chrono::Local;
|
||||
use icalendar::{CalendarComponent, Component, DatePerhapsTime, EventLike};
|
||||
|
||||
use crate::dav::{DAVClient, DAVCalendar, DAVCredentials};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dav_find_scheme(url: &str) -> Result<String, String> {
|
||||
let url = reqwest::Url::parse(url).map_err(|e| format!("Invalid URL: {}", e))?;
|
||||
let scheme = DAVCredentials::find_scheme(&url).await.map_err(|e| format!("Failed to determine authentication scheme: {}", e))?;
|
||||
Ok(scheme.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dav_fetch_calendars(url: &str, credentials: DAVCredentials) -> Result<Vec<DAVCalendar>, String> {
|
||||
let mut client = DAVClient::new(url, credentials);
|
||||
client.init().await.map_err(|e| format!("Failed to initialize DAV client: {}", e))?;
|
||||
let calendars = client.get_calendars().await.map_err(|e| format!("Failed to fetch calendars: {}", e))?;
|
||||
Ok(calendars)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DAVEvent {
|
||||
summary: String,
|
||||
start: Option<String>,
|
||||
end: Option<String>,
|
||||
description: Option<String>,
|
||||
location: Option<String>,
|
||||
}
|
||||
|
||||
pub fn dateperhapstime_to_string(d: DatePerhapsTime) -> Option<String> {
|
||||
match d {
|
||||
DatePerhapsTime::DateTime(dt) => dt.try_into_utc(),
|
||||
DatePerhapsTime::Date(d) => d
|
||||
.and_hms_opt(0, 0, 0)
|
||||
.and_then(|dt| dt.and_local_timezone(Local).earliest())
|
||||
.map(|dt| dt.to_utc()),
|
||||
}.map(|dt| dt.to_rfc3339())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn dav_fetch_events(calendar: DAVCalendar, credentials: DAVCredentials) -> Result<Vec<DAVEvent>, String> {
|
||||
let events = calendar.clone().get_events(&credentials).await.map_err(|e| format!("Failed to fetch events: {}", e))?;
|
||||
let mut res: Vec<DAVEvent> = Vec::new();
|
||||
for event in events {
|
||||
for component in &event.components {
|
||||
if let CalendarComponent::Event(event) = component {
|
||||
let location = event.get_location();
|
||||
if location.is_none() {
|
||||
continue;
|
||||
}
|
||||
let start_at = event.get_start().and_then(dateperhapstime_to_string);
|
||||
let end_at = event.get_end().and_then(dateperhapstime_to_string);
|
||||
res.push(DAVEvent {
|
||||
summary: event.get_summary().unwrap_or("No summary").to_string(),
|
||||
start: start_at,
|
||||
end: end_at,
|
||||
description: event.get_description().map(|s| s.to_string()),
|
||||
location: location.map(|s| s.to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
mod dav;
|
||||
mod dav_commands;
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
@ -5,6 +8,7 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_upload::init())
|
||||
.plugin(tauri_plugin_keep_screen_on::init())
|
||||
.plugin(tauri_plugin_tts::init())
|
||||
.plugin(tauri_plugin_duck::init())
|
||||
.setup(|app| {
|
||||
if cfg!(debug_assertions) {
|
||||
app.handle().plugin(
|
||||
@ -15,6 +19,7 @@ pub fn run() {
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![dav_commands::dav_find_scheme, dav_commands::dav_fetch_calendars, dav_commands::dav_fetch_events])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
@ -21,6 +21,8 @@
|
||||
import Input from "../ui/input/input.svelte";
|
||||
import EvConnectorSelect from "./EVConnectorSelect.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
import { localStore } from "$lib/services/localStore.svelte";
|
||||
import { isDriving } from "./location.svelte";
|
||||
|
||||
let open = $state(false);
|
||||
|
||||
@ -52,11 +54,23 @@
|
||||
fuelType: "diesel",
|
||||
preferredFuel: "Diesel",
|
||||
});
|
||||
|
||||
const biggerButtonsInDrive = localStore<boolean>(
|
||||
"bigger-buttons-in-drive",
|
||||
false,
|
||||
);
|
||||
const shouldUseLargeSize = $derived(
|
||||
biggerButtonsInDrive.current ? isDriving() : false,
|
||||
);
|
||||
</script>
|
||||
|
||||
<Drawer.Root bind:open>
|
||||
<Drawer.Trigger
|
||||
class={buttonVariants({ variant: "secondary", class: "w-full p-5" })}
|
||||
class={buttonVariants({
|
||||
variant: "secondary",
|
||||
class: "w-full",
|
||||
size: shouldUseLargeSize ? "drive" : "default",
|
||||
})}
|
||||
>
|
||||
{@render children()}
|
||||
</Drawer.Trigger>
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
routing,
|
||||
} from "$lib/services/navigation/routing.svelte";
|
||||
import { location } from "./location.svelte";
|
||||
import { saved } from "$lib/saved.svelte";
|
||||
import RoutingLayers from "$lib/services/navigation/RoutingLayers.svelte";
|
||||
import {
|
||||
getPMTilesURL,
|
||||
@ -20,6 +19,7 @@
|
||||
import HazardMarker from "./HazardMarker.svelte";
|
||||
import { hazards } from "./hazards.svelte";
|
||||
import RequiresCapability from "./RequiresCapability.svelte";
|
||||
import MapLocationMarkers from "./MapLocationMarkers.svelte";
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("resize", map.updateMapPadding);
|
||||
@ -28,9 +28,6 @@
|
||||
|
||||
let locationDot: HTMLDivElement | undefined = $state();
|
||||
let locationAccuracyCircle: HTMLDivElement | undefined = $state();
|
||||
let homeMarker: HTMLImageElement | undefined = $state();
|
||||
let workMarker: HTMLImageElement | undefined = $state();
|
||||
let schoolMarker: HTMLImageElement | undefined = $state();
|
||||
|
||||
const DEBUG_POINTS = false; // Set to true to show debug points on the map
|
||||
</script>
|
||||
@ -45,6 +42,7 @@
|
||||
bind:map={map.value}
|
||||
bind:zoom={map.zoom}
|
||||
padding={map.padding}
|
||||
attributionControl={false}
|
||||
onload={async () => {
|
||||
map.updateMapPadding();
|
||||
location.locked = true;
|
||||
@ -129,58 +127,18 @@
|
||||
<Marker
|
||||
lnglat={{ lat: location.lat, lng: location.lng }}
|
||||
element={locationDot}
|
||||
rotationAlignment="map"
|
||||
pitchAlignment="map"
|
||||
/>
|
||||
<Marker
|
||||
lnglat={{ lat: location.lat, lng: location.lng }}
|
||||
element={locationAccuracyCircle}
|
||||
rotationAlignment="map"
|
||||
pitchAlignment="map"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if saved.home}
|
||||
<img
|
||||
src={map.zoom > 9 ? "/img/saved/home.png" : "/img/saved/small.png"}
|
||||
alt="Home Marker"
|
||||
bind:this={homeMarker}
|
||||
style="width: 32px;"
|
||||
/>
|
||||
<Marker
|
||||
lnglat={{
|
||||
lat: saved.home.lat,
|
||||
lng: saved.home.lon,
|
||||
}}
|
||||
element={homeMarker}
|
||||
/>
|
||||
{/if}
|
||||
{#if saved.school}
|
||||
<img
|
||||
src={map.zoom > 9 ? "/img/saved/school.png" : "/img/saved/small.png"}
|
||||
alt="School Marker"
|
||||
bind:this={schoolMarker}
|
||||
style="width: 32px;"
|
||||
/>
|
||||
<Marker
|
||||
lnglat={{
|
||||
lat: saved.school.lat,
|
||||
lng: saved.school.lon,
|
||||
}}
|
||||
element={schoolMarker}
|
||||
/>
|
||||
{/if}
|
||||
{#if saved.work}
|
||||
<img
|
||||
src={map.zoom > 9 ? "/img/saved/work.png" : "/img/saved/small.png"}
|
||||
alt="Work Marker"
|
||||
bind:this={workMarker}
|
||||
style="width: 32px;"
|
||||
/>
|
||||
<Marker
|
||||
lnglat={{
|
||||
lat: saved.work.lat,
|
||||
lng: saved.work.lon,
|
||||
}}
|
||||
element={workMarker}
|
||||
/>
|
||||
{/if}
|
||||
<MapLocationMarkers />
|
||||
|
||||
<RequiresCapability capability="hazards">
|
||||
{#each hazards as hazard (hazard.latitude + "-" + hazard.longitude)}
|
||||
|
||||
25
src/lib/components/lnv/MapLocationMarker.svelte
Normal file
25
src/lib/components/lnv/MapLocationMarker.svelte
Normal file
@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { MAP_ICONS, type Location } from "$lib/saved.svelte";
|
||||
import { Marker } from "svelte-maplibre-gl";
|
||||
import { map } from "./map.svelte";
|
||||
|
||||
let { store }: { store: Location } = $props();
|
||||
|
||||
let marker: HTMLImageElement | undefined = $state();
|
||||
</script>
|
||||
|
||||
<img
|
||||
src={map.zoom > 9
|
||||
? `/img/saved/${MAP_ICONS[store.icon ?? "small.png"]}`
|
||||
: "/img/saved/small.png"}
|
||||
alt="Work Marker"
|
||||
bind:this={marker}
|
||||
style="width: 32px;"
|
||||
/>
|
||||
<Marker
|
||||
lnglat={{
|
||||
lat: store.lat,
|
||||
lng: store.lng,
|
||||
}}
|
||||
element={marker}
|
||||
/>
|
||||
11
src/lib/components/lnv/MapLocationMarkers.svelte
Normal file
11
src/lib/components/lnv/MapLocationMarkers.svelte
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { Location } from "$lib/saved.svelte";
|
||||
import { stores } from "$lib/services/stores.svelte";
|
||||
import MapLocationMarker from "./MapLocationMarker.svelte";
|
||||
|
||||
const locations = stores<Location>("location");
|
||||
</script>
|
||||
|
||||
{#each locations.current as location (location.data.lat + "-" + location.data.lng)}
|
||||
<MapLocationMarker store={location.data} />
|
||||
{/each}
|
||||
@ -14,6 +14,7 @@
|
||||
import RequiresCapability from "./RequiresCapability.svelte";
|
||||
import {
|
||||
advertiseRemoteLocation,
|
||||
getRoadMetadata,
|
||||
location,
|
||||
remoteLocation,
|
||||
} from "./location.svelte";
|
||||
@ -27,6 +28,7 @@
|
||||
import Progressbar from "../Progressbar.svelte";
|
||||
import { postHazard } from "$lib/services/lnv";
|
||||
import { fetchHazards } from "./hazards.svelte";
|
||||
import { getSpeed } from "$lib/services/TileMeta";
|
||||
|
||||
const views: Record<string, string> = {
|
||||
main: "MainSidebar",
|
||||
@ -44,6 +46,8 @@
|
||||
"onboarding-vehicles": "onboarding/OnboardingVehiclesSidebar",
|
||||
"nearby-poi": "NearbyPOISidebar",
|
||||
licenses: "settings/LicensesSidebar",
|
||||
calendar: "settings/CalendarSidebar",
|
||||
appearance: "settings/AppearanceSidebar",
|
||||
};
|
||||
|
||||
const fullscreen: Record<string, boolean> = {
|
||||
@ -62,6 +66,8 @@
|
||||
"onboarding-vehicles": true,
|
||||
"nearby-poi": false,
|
||||
licenses: true,
|
||||
calendar: true,
|
||||
appearance: true,
|
||||
};
|
||||
|
||||
let isDragging = false;
|
||||
@ -170,6 +176,47 @@
|
||||
<Input class="h-10" placeholder="Search..." bind:value={searchbar.text} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !hideSearch && !!location.speed != false}
|
||||
<div
|
||||
id="speedometer"
|
||||
style="position: fixed; {mobileView
|
||||
? `bottom: calc(50px + ${sidebarHeight.current}px + 10px); right: 10px;`
|
||||
: 'bottom: 10px; right: 10px;'}"
|
||||
>
|
||||
{((location.speed * 3.6) | 0).toFixed(0)}
|
||||
</div>
|
||||
{/if}
|
||||
{#if getRoadMetadata().current}
|
||||
{@const meta = getRoadMetadata().current!}
|
||||
{#if meta.maxspeed}
|
||||
{@const maxspeed = getSpeed(meta.maxspeed)}
|
||||
{#if maxspeed && maxspeed < 100}
|
||||
<div
|
||||
id="max-speed"
|
||||
style="position: fixed; {mobileView
|
||||
? `bottom: calc(50px + ${sidebarHeight.current}px + 10px + 2ch + 20px + 10px); right: 10px;`
|
||||
: 'bottom: calc(10px + 2ch + 20px + 10px); right: 10px;'}"
|
||||
>
|
||||
{meta.maxspeed}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if meta.overtaking}
|
||||
{@const overtaking = meta.overtaking}
|
||||
{#if overtaking && overtaking == "no"}
|
||||
<img
|
||||
alt="No Overtaking"
|
||||
src="/img/no-overtaking.png"
|
||||
id="overtaking"
|
||||
style="position: fixed; {mobileView
|
||||
? `bottom: calc(50px + ${sidebarHeight.current}px + 10px + (2ch + 20px + 10px) * 2); right: 10px;`
|
||||
: 'bottom: calc(10px + (2ch + 20px + 10px) * 2); right: 10px;'}"
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<div
|
||||
id="sidebar"
|
||||
class={mobileView ? "mobileView" : ""}
|
||||
@ -394,4 +441,32 @@
|
||||
border-bottom-right-radius: 0;
|
||||
padding-bottom: calc(40px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
#speedometer,
|
||||
#max-speed,
|
||||
#overtaking {
|
||||
z-index: 30;
|
||||
background-color: hsla(0, 0%, 5%, 0.6);
|
||||
backdrop-filter: blur(5px);
|
||||
aspect-ratio: 1 / 1;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
width: calc(2ch + 20px);
|
||||
height: calc(2ch + 20px);
|
||||
}
|
||||
|
||||
#max-speed {
|
||||
border-radius: 50%;
|
||||
border: 5px solid #FA5959;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#overtaking {
|
||||
padding: 0;
|
||||
background: none;
|
||||
backdrop-filter: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
PlusCircleIcon,
|
||||
TractorIcon,
|
||||
TruckIcon,
|
||||
XIcon,
|
||||
} from "@lucide/svelte";
|
||||
import Button, { buttonVariants } from "../ui/button/button.svelte";
|
||||
import {
|
||||
@ -17,6 +18,9 @@
|
||||
} from "$lib/vehicles/vehicles.svelte";
|
||||
import AddVehicleDrawer from "./AddVehicleDrawer.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
import { updateStore } from "$lib/services/stores.svelte";
|
||||
import { localStore } from "$lib/services/localStore.svelte";
|
||||
import { isDriving } from "./location.svelte";
|
||||
|
||||
let open = $state(false);
|
||||
|
||||
@ -36,13 +40,25 @@
|
||||
return TractorIcon; // Default icon if no match
|
||||
}
|
||||
}
|
||||
|
||||
const biggerButtonsInDrive = localStore<boolean>(
|
||||
"bigger-buttons-in-drive",
|
||||
false,
|
||||
);
|
||||
const shouldUseLargeSize = $derived(
|
||||
biggerButtonsInDrive.current ? isDriving() : false,
|
||||
);
|
||||
</script>
|
||||
|
||||
<Drawer.Root bind:open>
|
||||
<Drawer.Trigger
|
||||
class={buttonVariants({ variant: "secondary", class: "w-full p-5" })}
|
||||
class={buttonVariants({
|
||||
variant: "secondary",
|
||||
class: "w-full",
|
||||
size: shouldUseLargeSize ? "drive" : "default",
|
||||
})}
|
||||
>
|
||||
{@const vehicle = selectedVehicle() ?? DefaultVehicle}
|
||||
{@const vehicle = selectedVehicle()?.data ?? DefaultVehicle}
|
||||
{@const Icon = getVehicleIcon(vehicle.type)}
|
||||
<Icon />
|
||||
{vehicle.name}
|
||||
@ -56,25 +72,42 @@
|
||||
</Drawer.Header>
|
||||
<div class="p-4 pt-0 flex flex-col gap-2">
|
||||
{#each vehicles.current as vehicle (vehicle.name)}
|
||||
<Button
|
||||
variant={selectedVehicle() === vehicle.data ? "default" : "secondary"}
|
||||
class="w-full p-5"
|
||||
onclick={() => {
|
||||
selectVehicle(vehicle.data);
|
||||
open = false;
|
||||
}}
|
||||
>
|
||||
{@const Icon = getVehicleIcon(vehicle.data.type)}
|
||||
<Icon />
|
||||
{vehicle.data.name}
|
||||
</Button>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
variant={selectedVehicle()?.data === vehicle.data
|
||||
? "default"
|
||||
: "secondary"}
|
||||
class="w-[calc(100%-48px-8px)]"
|
||||
onclick={() => {
|
||||
selectVehicle(vehicle.data);
|
||||
open = false;
|
||||
}}
|
||||
>
|
||||
{@const Icon = getVehicleIcon(vehicle.data.type)}
|
||||
<Icon />
|
||||
{vehicle.data.name}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
class="w-[48px]"
|
||||
onclick={() => {
|
||||
if (
|
||||
!confirm(
|
||||
"Are you sure you want to delete this vehicle? This action cannot be undone.",
|
||||
)
|
||||
)
|
||||
return;
|
||||
updateStore({ type: "vehicle", name: vehicle.name }, null);
|
||||
}}
|
||||
>
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<AddVehicleDrawer>
|
||||
<Button variant="secondary" class="w-full p-5">
|
||||
<PlusCircleIcon />
|
||||
{m["vehicles.selector.add"]()}
|
||||
</Button>
|
||||
<PlusCircleIcon />
|
||||
{m["vehicles.selector.add"]()}
|
||||
</AddVehicleDrawer>
|
||||
</div>
|
||||
</Drawer.Content>
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { LNV_SERVER } from "$lib/services/hosts";
|
||||
import { routing } from "$lib/services/navigation/routing.svelte";
|
||||
import type { WrappedValue } from "$lib/services/stores.svelte";
|
||||
import { getFeature } from "$lib/services/TileMeta";
|
||||
import { lineString, nearestPointOnLine, point } from "@turf/turf";
|
||||
import { map } from "./map.svelte";
|
||||
|
||||
export const location = $state({
|
||||
@ -20,7 +23,7 @@ export const location = $state({
|
||||
center: [location.lng, location.lat],
|
||||
zoom: 16,
|
||||
duration: 1000,
|
||||
// bearing: location.heading !== null ? location.heading : undefined
|
||||
bearing: location.heading != null ? location.heading : 0,
|
||||
},
|
||||
{
|
||||
reason: "location",
|
||||
@ -31,29 +34,139 @@ export const location = $state({
|
||||
advertiser: null as WebSocket | null,
|
||||
code: null as string | null,
|
||||
lastUpdate: null as Date | null,
|
||||
useSnapped: true,
|
||||
});
|
||||
|
||||
const _isDriving = $derived(location.speed > 7 / 3.6);
|
||||
|
||||
export function isDriving() {
|
||||
return _isDriving;
|
||||
}
|
||||
|
||||
const roadMetadata: WrappedValue<GeoJSON.GeoJsonProperties> = $state({
|
||||
current: null,
|
||||
});
|
||||
const roadFeature: WrappedValue<GeoJSON.Feature | null> = $state({
|
||||
current: null,
|
||||
});
|
||||
const rawLocation: WrappedValue<WorldLocation | null> = $state({
|
||||
current: null,
|
||||
});
|
||||
const snappedLocation: WrappedValue<WorldLocation | null> = $state({
|
||||
current: null,
|
||||
});
|
||||
let lastFeatureId: string | null = null;
|
||||
|
||||
export function getRoadMetadata() {
|
||||
return roadMetadata;
|
||||
}
|
||||
|
||||
export function getRoadFeature() {
|
||||
return roadFeature;
|
||||
}
|
||||
|
||||
export function getSnappedLocation() {
|
||||
return snappedLocation;
|
||||
}
|
||||
|
||||
function snapLocation() {
|
||||
const feature = roadFeature.current;
|
||||
if (!feature) return;
|
||||
if (!rawLocation.current) return;
|
||||
if (feature.geometry.type === "LineString") {
|
||||
const loc = nearestPointOnLine(
|
||||
lineString(feature.geometry.coordinates),
|
||||
point([rawLocation.current.lon, rawLocation.current.lat]),
|
||||
);
|
||||
snappedLocation.current = {
|
||||
lat: loc.geometry.coordinates[1],
|
||||
lon: loc.geometry.coordinates[0],
|
||||
};
|
||||
} else if (feature.geometry.type === "MultiLineString") {
|
||||
// Find nearest point across all parts
|
||||
let nearestLoc: GeoJSON.Feature<GeoJSON.Point> | null = null;
|
||||
let minDist = Infinity;
|
||||
for (const coords of feature.geometry.coordinates) {
|
||||
const loc = nearestPointOnLine(
|
||||
lineString(coords),
|
||||
point([rawLocation.current.lon, rawLocation.current.lat]),
|
||||
);
|
||||
const dist = Math.hypot(
|
||||
loc.geometry.coordinates[0] - rawLocation.current.lon,
|
||||
loc.geometry.coordinates[1] - rawLocation.current.lat,
|
||||
);
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
nearestLoc = loc;
|
||||
}
|
||||
}
|
||||
if (nearestLoc) {
|
||||
snappedLocation.current = {
|
||||
lat: nearestLoc.geometry.coordinates[1],
|
||||
lon: nearestLoc.geometry.coordinates[0],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function watchLocation() {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.watchPosition(
|
||||
(pos) => {
|
||||
if (location.provider !== "gps") return;
|
||||
// console.log("Geolocation update:", pos)
|
||||
location.lat = pos.coords.latitude;
|
||||
location.lng = pos.coords.longitude;
|
||||
rawLocation.current = {
|
||||
lat: pos.coords.latitude,
|
||||
lon: pos.coords.longitude,
|
||||
};
|
||||
if (!location.useSnapped) {
|
||||
location.lat = pos.coords.latitude;
|
||||
location.lng = pos.coords.longitude;
|
||||
}
|
||||
location.accuracy = pos.coords.accuracy;
|
||||
location.speed = pos.coords.speed || 0;
|
||||
location.available = true;
|
||||
location.heading = pos.coords.heading;
|
||||
location.lastUpdate = new Date();
|
||||
|
||||
const blacklist = [
|
||||
"path",
|
||||
"track",
|
||||
"raceway",
|
||||
"busway",
|
||||
"bus_guideway",
|
||||
"ferry",
|
||||
];
|
||||
getFeature(
|
||||
{ lat: rawLocation.current.lat, lon: rawLocation.current.lon },
|
||||
"transportation",
|
||||
{
|
||||
filter: (f) => {
|
||||
if (f.properties) {
|
||||
return !blacklist.includes(f.properties["class"]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
lastId: lastFeatureId || undefined,
|
||||
},
|
||||
).then((feature) => {
|
||||
roadFeature.current = feature;
|
||||
roadMetadata.current = feature ? feature.properties : null;
|
||||
lastFeatureId = feature ? String(feature.id) : null;
|
||||
snapLocation();
|
||||
if (location.useSnapped && snappedLocation.current) {
|
||||
location.lat = snappedLocation.current.lat;
|
||||
location.lng = snappedLocation.current.lon;
|
||||
}
|
||||
});
|
||||
|
||||
if (location.locked) {
|
||||
map.value?.flyTo(
|
||||
{
|
||||
center: [location.lng, location.lat],
|
||||
zoom: 16,
|
||||
duration: 1000,
|
||||
// bearing: location.heading !== null ? location.heading : undefined
|
||||
bearing: location.heading != null ? location.heading : 0,
|
||||
},
|
||||
{
|
||||
reason: "location",
|
||||
|
||||
85
src/lib/components/lnv/main/Calendar.svelte
Normal file
85
src/lib/components/lnv/main/Calendar.svelte
Normal file
@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
import Button from "$lib/components/ui/button/button.svelte";
|
||||
import * as Card from "$lib/components/ui/card";
|
||||
import {
|
||||
fetchEvents,
|
||||
type DAVCalendar,
|
||||
type DAVCredentials,
|
||||
type DAVEvent,
|
||||
} from "$lib/services/CalDAV";
|
||||
import { search } from "$lib/services/Search";
|
||||
import { onMount } from "svelte";
|
||||
import { map, pin } from "../map.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
|
||||
let events: DAVEvent[] = $state([]);
|
||||
|
||||
onMount(async () => {
|
||||
const calendars = localStorage.getItem("calendars");
|
||||
if (!calendars) return;
|
||||
const parsedCalendars: (DAVCalendar & { credentials: DAVCredentials })[] =
|
||||
JSON.parse(calendars);
|
||||
for (const calendar of parsedCalendars) {
|
||||
const calendarEvents = await fetchEvents(calendar, calendar.credentials);
|
||||
events.push(...calendarEvents);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="events">
|
||||
{#each events as event ((event.start + "" || "") + (event.end + "" || ""))}
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>{event.summary}</Card.Title>
|
||||
{#if event.description}
|
||||
<Card.Description>{event.description}</Card.Description>
|
||||
{/if}
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<p>
|
||||
<strong>{m["calendar.start"]()}:</strong>
|
||||
{event.start?.toLocaleString("de-DE") || m["calendar.no-start"]()}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{m["calendar.end"]()}:</strong>
|
||||
{event.end?.toLocaleString("de-DE") || m["calendar.no-end"]()}
|
||||
</p>
|
||||
<p>
|
||||
{#if event.location}
|
||||
<strong>{m["calendar.location"]()}:</strong> {event.location}
|
||||
{:else}
|
||||
<strong>{m["calendar.location"]()}:</strong>
|
||||
{m["calendar.no-location"]()}
|
||||
{/if}
|
||||
</p>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<Button
|
||||
onclick={async () => {
|
||||
if (!event.location) return;
|
||||
const features = await search(event.location, 0, 0);
|
||||
if (features.length == 0) {
|
||||
return void alert("Can't find location");
|
||||
}
|
||||
const lat = features[0].geometry.coordinates[1];
|
||||
const lng = features[0].geometry.coordinates[0];
|
||||
pin.dropPin(lat, lng);
|
||||
pin.showInfo();
|
||||
map.value?.flyTo({
|
||||
center: [lng, lat],
|
||||
zoom: 19,
|
||||
});
|
||||
}}>{m.open()}</Button
|
||||
>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#events {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
59
src/lib/components/lnv/main/SavedLocations.svelte
Normal file
59
src/lib/components/lnv/main/SavedLocations.svelte
Normal file
@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$lang/messages";
|
||||
import Button from "$lib/components/ui/button/button.svelte";
|
||||
import { circInOut } from "svelte/easing";
|
||||
import { fly } from "svelte/transition";
|
||||
import { map, pin } from "../map.svelte";
|
||||
import { MapPinIcon } from "@lucide/svelte";
|
||||
import { stores } from "$lib/services/stores.svelte";
|
||||
import { ICONS, type Location } from "$lib/saved.svelte";
|
||||
|
||||
const locations = stores<Location>("location");
|
||||
|
||||
function getName(name: string) {
|
||||
if (name == "home") {
|
||||
return m["saved.home"]();
|
||||
} else if (name == "work") {
|
||||
return m["saved.work"]();
|
||||
} else if (name == "school") {
|
||||
return m["saved.school"]();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="saved"
|
||||
class="mt-2 mb-2"
|
||||
in:fly={{ y: 20, duration: 200, easing: circInOut }}
|
||||
>
|
||||
{#each locations.current as location (location.data.lat + "-" + location.data.lng)}
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="flex-1"
|
||||
onclick={() => {
|
||||
const { lat, lng } = location.data;
|
||||
pin.dropPin(lat, lng);
|
||||
pin.showInfo();
|
||||
map.value?.flyTo({
|
||||
center: [lng, lat],
|
||||
zoom: 19,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{@const Icon = ICONS[location.data.icon ?? "pin"] ?? MapPinIcon}
|
||||
<Icon />
|
||||
{getName(location.data.name)}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#saved {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
/* justify-content: space-evenly; */
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
@ -22,12 +22,12 @@
|
||||
import Reviews from "../info/Reviews.svelte";
|
||||
import MapAi from "../info/MapAI.svelte";
|
||||
import RequiresCapability from "../RequiresCapability.svelte";
|
||||
import { saved, saveLocations } from "$lib/saved.svelte";
|
||||
import { getDeveloperToggle } from "./settings/developer.svelte";
|
||||
import InternetAccess from "../info/InternetAccess.svelte";
|
||||
import RestaurantInfo from "../info/RestaurantInfo.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
import { onMount } from "svelte";
|
||||
import { updateStore } from "$lib/services/stores.svelte";
|
||||
|
||||
// let { feature }: { feature: Feature } = $props();
|
||||
|
||||
@ -99,6 +99,18 @@
|
||||
>
|
||||
{m["sidebar.info.dropped"]()}
|
||||
</SidebarHeader>
|
||||
<div id="actions">
|
||||
<Button
|
||||
onclick={() => {
|
||||
view.switch("route", {
|
||||
to: lat + "," + lng,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<RouteIcon />
|
||||
{m["sidebar.info.route"]()}
|
||||
</Button>
|
||||
</div>
|
||||
<p>{m.loading()}</p>
|
||||
{:then res}
|
||||
{#if res.elements.length === 0}
|
||||
@ -185,12 +197,15 @@
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
// localStorage.setItem(
|
||||
// "saved.home",
|
||||
// JSON.stringify({ lat, lon: lng }),
|
||||
// );
|
||||
saved.home = { lat, lon: lng };
|
||||
saveLocations();
|
||||
updateStore(
|
||||
{ name: "home", type: "location" },
|
||||
{
|
||||
lat,
|
||||
lng,
|
||||
name: "home",
|
||||
icon: "home",
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
<HomeIcon />
|
||||
@ -199,8 +214,15 @@
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
saved.school = { lat, lon: lng };
|
||||
saveLocations();
|
||||
updateStore(
|
||||
{ name: "school", type: "location" },
|
||||
{
|
||||
lat,
|
||||
lng,
|
||||
name: "school",
|
||||
icon: "school",
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
<SchoolIcon />
|
||||
@ -209,12 +231,15 @@
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
// localStorage.setItem(
|
||||
// "saved.work",
|
||||
// JSON.stringify({ lat, lon: lng }),
|
||||
// );
|
||||
saved.work = { lat, lon: lng };
|
||||
saveLocations();
|
||||
updateStore(
|
||||
{ name: "work", type: "location" },
|
||||
{
|
||||
lat,
|
||||
lng,
|
||||
name: "work",
|
||||
icon: "work",
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
<BriefcaseIcon />
|
||||
@ -292,6 +317,18 @@
|
||||
>
|
||||
Dropped Pin
|
||||
</SidebarHeader>
|
||||
<div id="actions">
|
||||
<Button
|
||||
onclick={() => {
|
||||
view.switch("route", {
|
||||
to: lat + "," + lng,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<RouteIcon />
|
||||
{m["sidebar.info.route"]()}
|
||||
</Button>
|
||||
</div>
|
||||
<p>Error: {err.message}</p>
|
||||
{/await}
|
||||
|
||||
|
||||
@ -1,108 +1,18 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
BriefcaseIcon,
|
||||
DownloadIcon,
|
||||
FuelIcon,
|
||||
HomeIcon,
|
||||
ParkingSquareIcon,
|
||||
SchoolIcon,
|
||||
} from "@lucide/svelte";
|
||||
import { DownloadIcon, FuelIcon, ParkingSquareIcon } from "@lucide/svelte";
|
||||
import { Button } from "../../ui/button";
|
||||
import { fly } from "svelte/transition";
|
||||
import { circInOut } from "svelte/easing";
|
||||
import { map, pin } from "../map.svelte";
|
||||
import VehicleSelector from "../VehicleSelector.svelte";
|
||||
import Post from "../Post.svelte";
|
||||
import RequiresCapability from "../RequiresCapability.svelte";
|
||||
import { saved } from "$lib/saved.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
import { view } from "../view.svelte";
|
||||
import * as Card from "$lib/components/ui/card";
|
||||
import SavedRoutes from "../main/SavedRoutes.svelte";
|
||||
import SavedLocations from "../main/SavedLocations.svelte";
|
||||
import Calendar from "../main/Calendar.svelte";
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="saved"
|
||||
class="mt-2 mb-2"
|
||||
in:fly={{ y: 20, duration: 200, easing: circInOut }}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="flex-1"
|
||||
onclick={() => {
|
||||
const loc = saved.home;
|
||||
if (!loc) {
|
||||
alert(m["saved.no-location"]({ name: m["saved.home"]() }));
|
||||
return;
|
||||
}
|
||||
const { lat, lon } = loc;
|
||||
if (!lat || !lon) {
|
||||
alert(m["saved.no-location"]({ name: m["saved.home"]() }));
|
||||
return;
|
||||
}
|
||||
pin.dropPin(lat, lon);
|
||||
pin.showInfo();
|
||||
map.value?.flyTo({
|
||||
center: [lon, lat],
|
||||
zoom: 19,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<HomeIcon />
|
||||
{m["saved.home"]()}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="flex-1"
|
||||
onclick={() => {
|
||||
console.log(saved);
|
||||
const loc = saved.school;
|
||||
if (!loc) {
|
||||
alert(m["saved.no-location"]({ name: m["saved.school"]() }));
|
||||
return;
|
||||
}
|
||||
const { lat, lon } = loc;
|
||||
if (!lat || !lon) {
|
||||
alert(m["saved.no-location"]({ name: m["saved.school"]() }));
|
||||
return;
|
||||
}
|
||||
pin.dropPin(lat, lon);
|
||||
pin.showInfo();
|
||||
map.value?.flyTo({
|
||||
center: [lon, lat],
|
||||
zoom: 19,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SchoolIcon />
|
||||
{m["saved.school"]()}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="flex-1"
|
||||
onclick={() => {
|
||||
const loc = saved.work;
|
||||
if (!loc) {
|
||||
alert(m["saved.no-location"]({ name: m["saved.work"]() }));
|
||||
return;
|
||||
}
|
||||
const { lat, lon } = loc;
|
||||
if (!lat || !lon) {
|
||||
alert(m["saved.no-location"]({ name: m["saved.work"]() }));
|
||||
return;
|
||||
}
|
||||
pin.dropPin(lat, lon);
|
||||
pin.showInfo();
|
||||
map.value?.flyTo({
|
||||
center: [lon, lat],
|
||||
zoom: 19,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<BriefcaseIcon />
|
||||
{m["saved.work"]()}
|
||||
</Button>
|
||||
</div>
|
||||
<SavedLocations />
|
||||
|
||||
<VehicleSelector />
|
||||
|
||||
@ -112,7 +22,6 @@
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
style="flex: 1;"
|
||||
onclick={() => {
|
||||
view.switch("nearby-poi", {
|
||||
@ -126,11 +35,10 @@
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
style="flex: 1;"
|
||||
onclick={() => {
|
||||
view.switch("nearby-poi", {
|
||||
tags: "amenity=parking",
|
||||
tags: "amenity=parking,parking!=street_side",
|
||||
});
|
||||
}}
|
||||
>
|
||||
@ -140,6 +48,8 @@
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Calendar />
|
||||
|
||||
{#if !window.__TAURI__}
|
||||
<Card.Root style="margin-top: 1rem;">
|
||||
<Card.Header>
|
||||
@ -170,13 +80,3 @@
|
||||
<Post />
|
||||
</div>
|
||||
</RequiresCapability>
|
||||
|
||||
<style>
|
||||
#saved {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
/* justify-content: space-evenly; */
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
return;
|
||||
}
|
||||
view.loading = true;
|
||||
fetchNearbyPOI(location.lat, location.lng, tags.split(","), 500).then(
|
||||
fetchNearbyPOI(location.lat, location.lng, tags.split(","), 5000).then(
|
||||
(results) => {
|
||||
pois = results.elements.sort((a, b) => {
|
||||
const distA = distanceTo(
|
||||
@ -89,7 +89,7 @@
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div class="font-bold">
|
||||
<div class="font-bold text-wrap">
|
||||
{poi.tags.name ?? poi.tags.brand ?? m.unnamed()}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
selectedVehicle,
|
||||
} from "$lib/vehicles/vehicles.svelte";
|
||||
import { location } from "../location.svelte";
|
||||
import { saved } from "$lib/saved.svelte";
|
||||
import { savedLocations } from "$lib/saved.svelte";
|
||||
import { m } from "$lang/messages";
|
||||
import LocationSelect from "../LocationSelect.svelte";
|
||||
|
||||
@ -74,8 +74,13 @@
|
||||
const FROM: WorldLocation =
|
||||
fromLocation == "current"
|
||||
? { lat: location.lat, lon: location.lng }
|
||||
: saved[fromLocation]
|
||||
? saved[fromLocation]
|
||||
: savedLocations.current.find((s) => s.name == fromLocation)
|
||||
? {
|
||||
lat: savedLocations.current.find((s) => s.name == fromLocation)!
|
||||
.data.lat,
|
||||
lon: savedLocations.current.find((s) => s.name == fromLocation)!
|
||||
.data.lng,
|
||||
}
|
||||
: {
|
||||
lat: parseFloat(fromLocation.split(",")[0]),
|
||||
lon: parseFloat(fromLocation.split(",")[1]),
|
||||
@ -83,16 +88,21 @@
|
||||
const TO: WorldLocation =
|
||||
toLocation == "current"
|
||||
? { lat: location.lat, lon: location.lng }
|
||||
: saved[toLocation]
|
||||
? saved[toLocation]
|
||||
: savedLocations.current.find((s) => s.name == toLocation)
|
||||
? {
|
||||
lat: savedLocations.current.find((s) => s.name == fromLocation)!
|
||||
.data.lat,
|
||||
lon: savedLocations.current.find((s) => s.name == fromLocation)!
|
||||
.data.lng,
|
||||
}
|
||||
: {
|
||||
lat: parseFloat(toLocation.split(",")[0]),
|
||||
lon: parseFloat(toLocation.split(",")[1]),
|
||||
};
|
||||
const req = createValhallaRequest(selectedVehicle() ?? DefaultVehicle, [
|
||||
FROM,
|
||||
TO,
|
||||
]);
|
||||
const req = createValhallaRequest(
|
||||
selectedVehicle()?.data ?? DefaultVehicle,
|
||||
[FROM, TO],
|
||||
);
|
||||
const res = await fetchRoute(ROUTING_SERVER, req);
|
||||
routes = [res.trip];
|
||||
if (res.alternates) {
|
||||
|
||||
@ -5,7 +5,13 @@
|
||||
import { getAuthURL, getOIDCUser } from "$lib/services/oidc";
|
||||
import * as Avatar from "$lib/components/ui/avatar";
|
||||
import { m } from "$lang/messages";
|
||||
import { refreshToken, uploadID } from "$lib/services/lnv";
|
||||
import {
|
||||
fetchMyUser,
|
||||
followUser,
|
||||
refreshToken,
|
||||
unfollowUser,
|
||||
uploadID,
|
||||
} from "$lib/services/lnv";
|
||||
|
||||
interface OIDCUser {
|
||||
sub: string;
|
||||
@ -25,6 +31,8 @@
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let testInput = "";
|
||||
</script>
|
||||
|
||||
{#if !user}
|
||||
@ -74,6 +82,26 @@
|
||||
refreshToken();
|
||||
}}>refresh</button
|
||||
>
|
||||
|
||||
{#await fetchMyUser() then u}
|
||||
<span><b>Followers:</b> {u.followers}</span>
|
||||
<span><b>Following:</b> {u.following}</span>
|
||||
<span><b>Reviews:</b> {u.reviewsCount}</span>
|
||||
<span><b>Hazards:</b> {u.hazardsCount}</span>
|
||||
{/await}
|
||||
|
||||
<input type="text" bind:value={testInput} />
|
||||
<button
|
||||
onclick={async () => {
|
||||
alert(await followUser(testInput).catch(alert));
|
||||
}}>Follow</button
|
||||
>
|
||||
<button
|
||||
onclick={async () => {
|
||||
alert(await unfollowUser(testInput).catch(alert));
|
||||
}}>Unfollow</button
|
||||
>
|
||||
|
||||
<pre>{user.sub}</pre>
|
||||
{JSON.stringify(user, null, 2)}
|
||||
{/if}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
<div id="languages">
|
||||
{#each locales as locale, _index (locale)}
|
||||
<SettingsButton
|
||||
text={m.language({}, { locale })}
|
||||
text={m["language.name"]({}, { locale })}
|
||||
icon={LanguagesIcon}
|
||||
onclick={() => {
|
||||
setOnboardingState("vehicles");
|
||||
|
||||
@ -12,10 +12,8 @@
|
||||
</h1>
|
||||
|
||||
<AddVehicleDrawer>
|
||||
<Button variant="secondary" class="w-full p-5">
|
||||
<PlusCircleIcon />
|
||||
{m["vehicles.selector.add"]()}
|
||||
</Button>
|
||||
<PlusCircleIcon />
|
||||
{m["vehicles.selector.add"]()}
|
||||
</AddVehicleDrawer>
|
||||
|
||||
<Button
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
<script>
|
||||
import { m } from "$lang/messages";
|
||||
import { BeakerIcon } from "@lucide/svelte";
|
||||
import { location } from "../../location.svelte";
|
||||
import SidebarHeader from "../SidebarHeader.svelte";
|
||||
import SettingsToggle from "./SettingsToggle.svelte";
|
||||
</script>
|
||||
|
||||
<SidebarHeader>
|
||||
{m["sidebar.appearance.header"]()}
|
||||
</SidebarHeader>
|
||||
|
||||
<SettingsToggle
|
||||
text={m["sidebar.appearance.bigger-buttons-in-drive"]()}
|
||||
localStorageKey="bigger-buttons-in-drive"
|
||||
/>
|
||||
<SettingsToggle
|
||||
text="Use snapped location (experimental)"
|
||||
bind:value={location.useSnapped}
|
||||
icon={BeakerIcon}
|
||||
/>
|
||||
179
src/lib/components/lnv/sidebar/settings/CalendarSidebar.svelte
Normal file
179
src/lib/components/lnv/sidebar/settings/CalendarSidebar.svelte
Normal file
@ -0,0 +1,179 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$lang/messages";
|
||||
import { CalendarPlusIcon } from "@lucide/svelte";
|
||||
import SidebarHeader from "../SidebarHeader.svelte";
|
||||
import SettingsButton from "./SettingsButton.svelte";
|
||||
import * as Drawer from "$lib/components/ui/drawer";
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import Input from "$lib/components/ui/input/input.svelte";
|
||||
import {
|
||||
fetchCalendars,
|
||||
findScheme,
|
||||
type AuthScheme,
|
||||
type DAVCalendar,
|
||||
type DAVCredentials,
|
||||
} from "$lib/services/CalDAV";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let calendars: (DAVCalendar & { credentials: DAVCredentials })[] = $state([]);
|
||||
|
||||
let calDavDrawerOpen = $state(false);
|
||||
let calDavLoading = $state(false);
|
||||
let calDavUrl = $state("");
|
||||
let calDavUsername = $state("");
|
||||
let calDavPassword = $state("");
|
||||
let calDavCalendars: DAVCalendar[] = $state([]);
|
||||
let calDavScheme: AuthScheme;
|
||||
|
||||
let calDavState = $state("");
|
||||
|
||||
async function fetchCalDav() {
|
||||
calDavState = m["sidebar.calendar.probing-server"]();
|
||||
calDavScheme = await findScheme(calDavUrl);
|
||||
calDavState = m["sidebar.calendar.discovering-calendars"]();
|
||||
calDavCalendars = await fetchCalendars(calDavUrl, {
|
||||
scheme: calDavScheme,
|
||||
username: calDavUsername,
|
||||
password: calDavPassword,
|
||||
});
|
||||
calDavState = "";
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (localStorage.getItem("calendars")) {
|
||||
calendars = JSON.parse(localStorage.getItem("calendars")!);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<SidebarHeader>
|
||||
{m["sidebar.calendar.header"]()}
|
||||
</SidebarHeader>
|
||||
|
||||
<Drawer.Root bind:open={calDavDrawerOpen}>
|
||||
<Drawer.Content>
|
||||
<Drawer.Header>
|
||||
<Drawer.Title>{m["sidebar.calendar.connect"]()}</Drawer.Title>
|
||||
</Drawer.Header>
|
||||
<div class="p-4 pt-0 flex flex-col gap-2">
|
||||
{#if calDavCalendars}
|
||||
<Input
|
||||
type="url"
|
||||
placeholder="https://my-caldav-server.com/..."
|
||||
disabled={calDavLoading}
|
||||
bind:value={calDavUrl}
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
disabled={calDavLoading}
|
||||
bind:value={calDavUsername}
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
disabled={calDavLoading}
|
||||
bind:value={calDavPassword}
|
||||
/>
|
||||
{#if calDavState}
|
||||
<span>{calDavState}</span>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if calDavCalendars.length > 0}
|
||||
<h3 class="font-medium pt-4">{m["sidebar.calendar.choose"]()}</h3>
|
||||
<ul class="max-h-48 overflow-y-auto">
|
||||
{#each calDavCalendars as calendar (calendar.url)}
|
||||
<li>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="secondary"
|
||||
onclick={() => {
|
||||
if (localStorage.getItem("calendars")) {
|
||||
const existing = JSON.parse(
|
||||
localStorage.getItem("calendars")!,
|
||||
);
|
||||
existing.push({
|
||||
name: calendar.name,
|
||||
url: calendar.url,
|
||||
credentials: {
|
||||
username: calDavUsername,
|
||||
password: calDavPassword,
|
||||
scheme: calDavScheme,
|
||||
},
|
||||
});
|
||||
localStorage.setItem("calendars", JSON.stringify(existing));
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"calendars",
|
||||
JSON.stringify([
|
||||
{
|
||||
name: calendar.name,
|
||||
url: calendar.url,
|
||||
credentials: {
|
||||
username: calDavUsername,
|
||||
password: calDavPassword,
|
||||
scheme: calDavScheme,
|
||||
},
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
calendars.push({
|
||||
name: calendar.name,
|
||||
url: calendar.url,
|
||||
credentials: {
|
||||
username: calDavUsername,
|
||||
password: calDavPassword,
|
||||
scheme: calDavScheme,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{calendar.name}
|
||||
</Button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
<Drawer.Footer>
|
||||
{#if calDavCalendars.length === 0}
|
||||
<Button
|
||||
onclick={async () => {
|
||||
calDavLoading = true;
|
||||
await fetchCalDav().catch((e) => {
|
||||
calDavState = e;
|
||||
calDavLoading = false;
|
||||
});
|
||||
}}>{m.submit()}</Button
|
||||
>
|
||||
{/if}
|
||||
<Drawer.Close>
|
||||
{calDavCalendars.length === 0 ? m.done() : m.cancel()}
|
||||
</Drawer.Close>
|
||||
</Drawer.Footer>
|
||||
</Drawer.Content>
|
||||
</Drawer.Root>
|
||||
|
||||
{#each calendars as calendar (calendar.url)}
|
||||
<div class="p-2 border rounded mb-2">
|
||||
<h3 class="font-medium">{calendar.name}</h3>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
class="mt-2"
|
||||
onclick={() => {
|
||||
calendars = calendars.filter((c) => c.url !== calendar.url);
|
||||
localStorage.setItem("calendars", JSON.stringify(calendars));
|
||||
}}>{m.delete()}</Button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<SettingsButton
|
||||
text={m["sidebar.calendar.connect"]()}
|
||||
icon={CalendarPlusIcon}
|
||||
onclick={() => {
|
||||
calDavDrawerOpen = true;
|
||||
}}
|
||||
/>
|
||||
@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import {
|
||||
CalendarSearchIcon,
|
||||
CloudUploadIcon,
|
||||
HandIcon,
|
||||
MapIcon,
|
||||
@ -24,6 +25,7 @@
|
||||
syncStores,
|
||||
updateStore,
|
||||
} from "$lib/services/stores.svelte";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
const dev = getDeveloperToggle();
|
||||
|
||||
@ -54,6 +56,51 @@
|
||||
await downloadPMTiles(url, name);
|
||||
}}
|
||||
/>
|
||||
<SettingsButton
|
||||
icon={CalendarSearchIcon}
|
||||
text="Fetch calendars"
|
||||
onclick={async () => {
|
||||
const url = prompt("URL?");
|
||||
if (!url) return;
|
||||
const scheme = await invoke("dav_find_scheme", { url }).catch((err) => {
|
||||
alert("Error fetching scheme: " + err);
|
||||
});
|
||||
alert("Found scheme: " + scheme);
|
||||
const username = prompt("Username?");
|
||||
const password = prompt("Password?");
|
||||
if (!username || !password) return;
|
||||
const credentials = { scheme, username, password };
|
||||
invoke("dav_fetch_calendars", { url, credentials })
|
||||
.then((calendars) => {
|
||||
alert("Fetched calendars: " + JSON.stringify(calendars));
|
||||
})
|
||||
.catch((err) => {
|
||||
alert("Error fetching calendars: " + err);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SettingsButton
|
||||
icon={CalendarSearchIcon}
|
||||
text="Fetch CalDAV events"
|
||||
onclick={async () => {
|
||||
const url = prompt("URL?");
|
||||
const scheme = prompt("Scheme? (basic, digest)");
|
||||
const username = prompt("Username?");
|
||||
const password = prompt("Password?");
|
||||
if (!url || !username || !password) return;
|
||||
const credentials = { scheme, username, password };
|
||||
invoke("dav_fetch_events", {
|
||||
calendar: { name: "Calendar", url },
|
||||
credentials,
|
||||
})
|
||||
.then((events) => {
|
||||
alert("Fetched events: " + JSON.stringify(events));
|
||||
})
|
||||
.catch((err) => {
|
||||
alert("Error fetching events: " + err);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
@ -13,10 +13,10 @@
|
||||
<div id="languages">
|
||||
{#each locales as locale, _index (locale)}
|
||||
{#if locale == getLocale()}
|
||||
<SettingsButton text={m.language()} icon={CheckIcon} disabled />
|
||||
<SettingsButton text={m["language.name"]()} icon={CheckIcon} disabled />
|
||||
{:else}
|
||||
<SettingsButton
|
||||
text={m.language({}, { locale })}
|
||||
text={m["language.name"]({}, { locale })}
|
||||
icon={LanguagesIcon}
|
||||
onclick={() => {
|
||||
setLocale(locale);
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
style="width: 100%; height: 40px;"
|
||||
style="width: 100%;"
|
||||
{disabled}
|
||||
onclick={() => {
|
||||
if (viewName) view.switch(viewName);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import {
|
||||
CalendarIcon,
|
||||
CodeIcon,
|
||||
InfoIcon,
|
||||
LanguagesIcon,
|
||||
@ -24,6 +25,20 @@
|
||||
text={m["sidebar.language.header"]()}
|
||||
view="language"
|
||||
/>
|
||||
<SettingsButton
|
||||
icon={PaintbrushIcon}
|
||||
text={m["sidebar.appearance.header"]()}
|
||||
view="appearance"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>{m["sidebar.settings.connections"]()}</h2>
|
||||
<SettingsButton
|
||||
icon={CalendarIcon}
|
||||
text={m["sidebar.calendar.header"]()}
|
||||
view="calendar"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
import type { IconProps } from "@lucide/svelte";
|
||||
import type { Component } from "svelte";
|
||||
import Switch from "$lib/components/ui/switch/switch.svelte";
|
||||
import { Label } from "$lib/components/ui/label";
|
||||
import { eventTarget } from "$lib/services/localStore.svelte";
|
||||
|
||||
let {
|
||||
icon: Icon,
|
||||
text,
|
||||
onchange,
|
||||
disabled,
|
||||
localStorageKey,
|
||||
value = $bindable(
|
||||
localStorageKey
|
||||
? localStorage.getItem(localStorageKey) === "true"
|
||||
: false,
|
||||
),
|
||||
}: {
|
||||
icon?: Component<IconProps>;
|
||||
text: string;
|
||||
onchange?: () => void;
|
||||
disabled?: boolean;
|
||||
value?: boolean;
|
||||
localStorageKey?: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex">
|
||||
<Label style="width: 100%;" for="settings-toggle">
|
||||
{#if Icon}
|
||||
<Icon />
|
||||
{/if}
|
||||
{text}
|
||||
</Label>
|
||||
<Switch
|
||||
{disabled}
|
||||
bind:checked={value}
|
||||
onCheckedChange={() => {
|
||||
if (onchange) onchange();
|
||||
if (localStorageKey) {
|
||||
localStorage.setItem(localStorageKey, value ? "true" : "false");
|
||||
eventTarget.dispatchEvent(
|
||||
new CustomEvent("localStorageChanged", {
|
||||
detail: { key: localStorageKey, value },
|
||||
}),
|
||||
);
|
||||
}
|
||||
}}
|
||||
id="settings-toggle"
|
||||
/>
|
||||
</div>
|
||||
@ -1,4 +1,6 @@
|
||||
<script lang="ts" module>
|
||||
import { isDriving } from "$lib/components/lnv/location.svelte";
|
||||
import { localStore } from "$lib/services/localStore.svelte";
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type {
|
||||
HTMLAnchorAttributes,
|
||||
@ -6,6 +8,14 @@
|
||||
} from "svelte/elements";
|
||||
import { type VariantProps, tv } from "tailwind-variants";
|
||||
|
||||
const biggerButtonsInDrive = localStore<boolean>(
|
||||
"bigger-buttons-in-drive",
|
||||
false,
|
||||
);
|
||||
const shouldUseLargeSize = $derived(
|
||||
biggerButtonsInDrive.current ? isDriving() : false,
|
||||
);
|
||||
|
||||
export const buttonVariants = tv({
|
||||
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||
variants: {
|
||||
@ -27,6 +37,7 @@
|
||||
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
drive: "h-12 rounded-md px-8 has-[>svg]:px-6",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@ -63,7 +74,10 @@
|
||||
<a
|
||||
bind:this={ref}
|
||||
data-slot="button"
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
class={cn(
|
||||
buttonVariants({ variant, size: shouldUseLargeSize ? "drive" : size }),
|
||||
className,
|
||||
)}
|
||||
href={disabled ? undefined : href}
|
||||
aria-disabled={disabled}
|
||||
role={disabled ? "link" : undefined}
|
||||
@ -76,7 +90,10 @@
|
||||
<button
|
||||
bind:this={ref}
|
||||
data-slot="button"
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
class={cn(
|
||||
buttonVariants({ variant, size: shouldUseLargeSize ? "drive" : size }),
|
||||
className,
|
||||
)}
|
||||
{type}
|
||||
{disabled}
|
||||
{...restProps}
|
||||
|
||||
7
src/lib/components/ui/label/index.ts
Normal file
7
src/lib/components/ui/label/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import Root from "./label.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Label,
|
||||
};
|
||||
20
src/lib/components/ui/label/label.svelte
Normal file
20
src/lib/components/ui/label/label.svelte
Normal file
@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Label as LabelPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: LabelPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<LabelPrimitive.Root
|
||||
bind:ref
|
||||
data-slot="label"
|
||||
class={cn(
|
||||
"flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
7
src/lib/components/ui/switch/index.ts
Normal file
7
src/lib/components/ui/switch/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import Root from "./switch.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Switch,
|
||||
};
|
||||
29
src/lib/components/ui/switch/switch.svelte
Normal file
29
src/lib/components/ui/switch/switch.svelte
Normal file
@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { Switch as SwitchPrimitive } from "bits-ui";
|
||||
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
checked = $bindable(false),
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<SwitchPrimitive.Root
|
||||
bind:ref
|
||||
bind:checked
|
||||
data-slot="switch"
|
||||
class={cn(
|
||||
"data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 shadow-xs peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent outline-none transition-all focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
class={cn(
|
||||
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
@ -1,20 +1,57 @@
|
||||
import type { Component } from "svelte";
|
||||
import { reverseGeocode } from "./services/Search";
|
||||
import {
|
||||
BriefcaseIcon,
|
||||
HomeIcon,
|
||||
MapPinIcon,
|
||||
SchoolIcon,
|
||||
type IconProps,
|
||||
} from "@lucide/svelte";
|
||||
import { stores, storeSnapshot } from "./services/stores.svelte";
|
||||
|
||||
/**
|
||||
* @deprecated Use stores instead.
|
||||
*/
|
||||
export const saved: Record<string, WorldLocation> = $state(
|
||||
JSON.parse(localStorage.getItem("saved") ?? "{}"),
|
||||
);
|
||||
|
||||
export const savedLocations = stores<Location>("location");
|
||||
|
||||
/**
|
||||
* @deprecated Use stores instead.
|
||||
*/
|
||||
export function saveLocations() {
|
||||
localStorage.setItem("saved", JSON.stringify(saved));
|
||||
}
|
||||
|
||||
export async function geocode(name: string) {
|
||||
const loc = saved[name];
|
||||
const loc = await storeSnapshot<Location>({ name, type: "location" });
|
||||
if (!loc) return;
|
||||
const geocode = await reverseGeocode(loc);
|
||||
const geocode = await reverseGeocode({ lat: loc.lat, lon: loc.lng });
|
||||
if (geocode.length == 0) {
|
||||
return;
|
||||
}
|
||||
const feature = geocode[0];
|
||||
return `${feature.properties.street}${feature.properties.housenumber ? " " + feature.properties.housenumber : ""}, ${feature.properties.city}`;
|
||||
}
|
||||
|
||||
export const ICONS: Record<string, Component<IconProps>> = {
|
||||
home: HomeIcon,
|
||||
work: BriefcaseIcon,
|
||||
school: SchoolIcon,
|
||||
pin: MapPinIcon,
|
||||
};
|
||||
|
||||
export const MAP_ICONS: Record<string, string> = {
|
||||
home: "home.png",
|
||||
school: "school.png",
|
||||
work: "work.png",
|
||||
}; // TODO: add generic pin icon
|
||||
|
||||
export interface Location {
|
||||
lat: number;
|
||||
lng: number;
|
||||
name: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
47
src/lib/services/CalDAV.ts
Normal file
47
src/lib/services/CalDAV.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export type AuthScheme = "Digest" | "Basic";
|
||||
|
||||
export interface DAVCredentials {
|
||||
scheme: AuthScheme;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface DAVCalendar {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface DAVEvent {
|
||||
summary: string;
|
||||
start?: Date;
|
||||
end?: Date;
|
||||
description?: string;
|
||||
location?: string;
|
||||
}
|
||||
|
||||
export async function findScheme(url: string): Promise<AuthScheme> {
|
||||
const scheme = await invoke("dav_find_scheme", { url });
|
||||
return scheme as AuthScheme;
|
||||
}
|
||||
|
||||
export async function fetchCalendars(
|
||||
url: string,
|
||||
credentials: DAVCredentials,
|
||||
): Promise<DAVCalendar[]> {
|
||||
const calendars = await invoke("dav_fetch_calendars", { url, credentials });
|
||||
return calendars as DAVCalendar[];
|
||||
}
|
||||
|
||||
export async function fetchEvents(
|
||||
calendar: DAVCalendar,
|
||||
credentials: DAVCredentials,
|
||||
): Promise<DAVEvent[]> {
|
||||
const events = await invoke("dav_fetch_events", { calendar, credentials });
|
||||
return (events as DAVEvent[]).map((e) => {
|
||||
if (e.start) e.start = new Date(e.start);
|
||||
if (e.end) e.end = new Date(e.end);
|
||||
return e;
|
||||
}) as DAVEvent[];
|
||||
}
|
||||
@ -47,6 +47,8 @@ export async function fetchPOI(lat: number, lon: number, radius: number) {
|
||||
(
|
||||
node(around:${radius}, ${lat}, ${lon})["amenity"]["name"];
|
||||
way(around:${radius}, ${lat}, ${lon})["amenity"]["name"];
|
||||
node(around:${radius}, ${lat}, ${lon})["tourism"]["name"];
|
||||
way(around:${radius}, ${lat}, ${lon})["tourism"]["name"];
|
||||
relation(around:${radius}, ${lat}, ${lon})["amenity"]["name"];
|
||||
node(around:${radius}, ${lat}, ${lon})["shop"]["name"];
|
||||
way(around:${radius}, ${lat}, ${lon})["shop"]["name"];
|
||||
|
||||
171
src/lib/services/TileMeta.ts
Normal file
171
src/lib/services/TileMeta.ts
Normal file
@ -0,0 +1,171 @@
|
||||
import { lineString, pointToLineDistance } from "@turf/turf";
|
||||
import { FSSource, hasPMTiles } from "./OfflineTiles";
|
||||
import { PMTiles } from "pmtiles";
|
||||
import { VectorTile } from "@mapbox/vector-tile";
|
||||
import Protobuf from "pbf";
|
||||
import { location } from "$lib/components/lnv/location.svelte";
|
||||
|
||||
function getFeatureDistance(f: GeoJSON.Feature, point: [number, number]) {
|
||||
if (f.geometry.type === "LineString") {
|
||||
return pointToLineDistance(point, lineString(f.geometry.coordinates), {
|
||||
units: "meters",
|
||||
});
|
||||
} else if (f.geometry.type === "MultiLineString") {
|
||||
// Compute the min distance across all parts
|
||||
return Math.min(
|
||||
...f.geometry.coordinates.map((coords) =>
|
||||
pointToLineDistance(point, lineString(coords), { units: "meters" }),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
interface GetFeatureOptions {
|
||||
lastId?: string;
|
||||
filter?: (feature: GeoJSON.Feature) => boolean;
|
||||
}
|
||||
|
||||
function getBias() {
|
||||
if (!location.speed) return 5;
|
||||
return Math.max(5, Math.min(15, location.speed * 0.5)); // Bias increases with speed, min 5, max 15, 0.5 per km/h
|
||||
}
|
||||
|
||||
export async function getFeature(
|
||||
coord: WorldLocation,
|
||||
layer: string,
|
||||
{ lastId, filter }: GetFeatureOptions = {},
|
||||
) {
|
||||
const zxy = coordToTile(coord, 14);
|
||||
const tile = await fetchTile(zxy.z, zxy.x, zxy.y);
|
||||
const layerData = tile.layers[layer];
|
||||
if (!layerData) return null;
|
||||
|
||||
const features: GeoJSON.Feature[] = [];
|
||||
for (let i = 0; i < layerData.length; i++) {
|
||||
const feature = layerData.feature(i);
|
||||
features.push(feature.toGeoJSON(zxy.x, zxy.y, zxy.z));
|
||||
}
|
||||
const filtered = features
|
||||
.filter(
|
||||
(f) =>
|
||||
f.geometry.type === "LineString" ||
|
||||
f.geometry.type == "MultiLineString",
|
||||
)
|
||||
.filter((f) => (filter ? filter(f) : true));
|
||||
if (filtered.length === 0) return null;
|
||||
const nearest = filtered.reduce((a, b) => {
|
||||
let distA = getFeatureDistance(a, [coord.lon, coord.lat]);
|
||||
let distB = getFeatureDistance(b, [coord.lon, coord.lat]);
|
||||
|
||||
const STAY_BIAS = getBias();
|
||||
|
||||
// TODO: don't bias if feature is continuing straight (split ways vs turn)
|
||||
if (lastId && String(a.id) == lastId) {
|
||||
distB += STAY_BIAS;
|
||||
}
|
||||
if (lastId && String(b.id) == lastId) {
|
||||
distA += STAY_BIAS;
|
||||
}
|
||||
|
||||
return distA < distB ? a : b;
|
||||
});
|
||||
return nearest;
|
||||
}
|
||||
|
||||
export async function getMeta(
|
||||
coord: WorldLocation,
|
||||
layer: string,
|
||||
options?: GetFeatureOptions,
|
||||
) {
|
||||
const nearest = await getFeature(coord, layer, options);
|
||||
return nearest ? nearest.properties : null;
|
||||
}
|
||||
|
||||
const IMPLICIT_SPEEDS: Record<string, number> = {
|
||||
"DE:urban": 50,
|
||||
"DE:rural": 100, // TODO: 80 (hgv weight > 3.5t, or trailer), 60 (weight > 7.5t)
|
||||
"DE:living_street": 7,
|
||||
"DE:bicycle_road": 30,
|
||||
};
|
||||
|
||||
export function getSpeed(maxspeed: string): number | null {
|
||||
if (!isNaN(parseInt(maxspeed))) return parseInt(maxspeed);
|
||||
if (maxspeed.endsWith(" mph")) {
|
||||
const val = parseInt(maxspeed.replace(" mph", ""));
|
||||
if (!isNaN(val)) return Math.round(val * 1.60934); // Convert to km/h
|
||||
}
|
||||
if (maxspeed === "walk") return 7; // https://wiki.openstreetmap.org/wiki/Proposed_features/maxspeed_walk
|
||||
return IMPLICIT_SPEEDS[maxspeed as keyof typeof IMPLICIT_SPEEDS] || null;
|
||||
}
|
||||
|
||||
function coordToTile(coord: WorldLocation, zoom: number) {
|
||||
const z = zoom;
|
||||
const x = Math.floor(((coord.lon + 180) / 360) * Math.pow(2, z));
|
||||
const y = Math.floor(
|
||||
((1 -
|
||||
Math.log(
|
||||
Math.tan((coord.lat * Math.PI) / 180) +
|
||||
1 / Math.cos((coord.lat * Math.PI) / 180),
|
||||
) /
|
||||
Math.PI) /
|
||||
2) *
|
||||
Math.pow(2, z),
|
||||
);
|
||||
return { z, x, y };
|
||||
}
|
||||
|
||||
class Cache<T> {
|
||||
data: Map<string, T>;
|
||||
size: number;
|
||||
|
||||
constructor(size: number) {
|
||||
this.data = new Map<string, T>();
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
get(key: string): T | undefined {
|
||||
if (this.data.has(key)) {
|
||||
const value = this.data.get(key)!;
|
||||
this.data.delete(key);
|
||||
this.data.set(key, value);
|
||||
return value;
|
||||
}
|
||||
return this.data.get(key);
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
return this.data.has(key);
|
||||
}
|
||||
|
||||
set(key: string, value: T): void {
|
||||
this.data.set(key, value);
|
||||
if (this.data.size > this.size) {
|
||||
const firstKey = this.data.keys().next().value;
|
||||
if (firstKey) {
|
||||
this.data.delete(firstKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tileCache = new Cache<VectorTile>(5);
|
||||
|
||||
export async function fetchTile(z: number, x: number, y: number) {
|
||||
const cacheKey = `${z}/${x}/${y}`;
|
||||
if (tileCache.has(cacheKey)) {
|
||||
return tileCache.get(cacheKey)!;
|
||||
}
|
||||
const pmtiles = (await hasPMTiles("tiles"))
|
||||
? new PMTiles(new FSSource("tiles"))
|
||||
: new PMTiles("https://trafficcue-tiles.picoscratch.de/germany.pmtiles");
|
||||
const tile = await pmtiles.getZxy(z, x, y);
|
||||
if (!tile) {
|
||||
throw new Error(`Tile not found: z${z} x${x} y${y}`);
|
||||
}
|
||||
const data = tile.data;
|
||||
const vectorTile = new VectorTile(new Protobuf(data));
|
||||
tileCache.set(cacheKey, vectorTile);
|
||||
return vectorTile;
|
||||
}
|
||||
@ -9,4 +9,4 @@ export const LNV_SERVER =
|
||||
? "http://localhost:3000/api"
|
||||
: location.hostname.includes("staging")
|
||||
? "https://staging-trafficcue-api.picoscratch.de/api"
|
||||
: "https://trafficcue-api.picoscratch.de/api";
|
||||
: "https://staging-trafficcue-api.picoscratch.de/api";
|
||||
|
||||
@ -280,3 +280,48 @@ export async function ping() {
|
||||
const res = await fetch(LNV_SERVER + "/ping").catch(() => ({ ok: false }));
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
username: string;
|
||||
createdAt: string;
|
||||
reviewsCount: number;
|
||||
hazardsCount: number;
|
||||
followers: number;
|
||||
following: number;
|
||||
}
|
||||
|
||||
export async function fetchMyUser(): Promise<UserInfo> {
|
||||
const res = await authFetch(LNV_SERVER + "/user/me");
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to fetch user: ${res.statusText}`);
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
export async function followUser(username: string) {
|
||||
const res = await authFetch(LNV_SERVER + "/user/follow", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to follow user: ${res.statusText}`);
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
export async function unfollowUser(username: string) {
|
||||
const res = await authFetch(LNV_SERVER + "/user/unfollow", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to unfollow user: ${res.statusText}`);
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
34
src/lib/services/localStore.svelte.ts
Normal file
34
src/lib/services/localStore.svelte.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import type { WrappedValue } from "$lib/services/stores.svelte";
|
||||
|
||||
export const eventTarget = new EventTarget();
|
||||
|
||||
export function localStore<T>(key: string, defaultValue: T): WrappedValue<T> {
|
||||
const storedValue = localStorage.getItem(key);
|
||||
const initialValue =
|
||||
storedValue !== null ? JSON.parse(storedValue) : defaultValue;
|
||||
const state = $state<WrappedValue<T>>({ current: initialValue });
|
||||
eventTarget.addEventListener("localStorageChanged", (event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
console.log("localStorageChanged event received", customEvent.detail);
|
||||
if (customEvent.detail.key === key) {
|
||||
const newValue = customEvent.detail.value as T;
|
||||
console.log(`localStore: ${key} updated from another tab`, newValue);
|
||||
if (JSON.stringify(state.current) === JSON.stringify(newValue)) return;
|
||||
state.current = newValue;
|
||||
}
|
||||
});
|
||||
return {
|
||||
get current() {
|
||||
return state.current;
|
||||
},
|
||||
set current(newValue: T) {
|
||||
state.current = newValue;
|
||||
eventTarget.dispatchEvent(
|
||||
new CustomEvent("localStorageChanged", {
|
||||
detail: { key, value: newValue },
|
||||
}),
|
||||
);
|
||||
localStorage.setItem(key, JSON.stringify(newValue));
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
import { duck, unduck } from "tauri-plugin-duck-api";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { m } from "$lang/messages";
|
||||
import { locales, type Locale } from "$lang/runtime";
|
||||
|
||||
export let tts: "tauri" | "web" | null = null;
|
||||
|
||||
@ -13,25 +15,39 @@ export async function initTTS() {
|
||||
}
|
||||
}
|
||||
|
||||
export default async function say(text: string) {
|
||||
export function findLocaleForValhallaLanguage(language: string) {
|
||||
for (const locale of locales) {
|
||||
if (m["language.valhalla"]({ language }, { locale }) === language) {
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default async function say(text: string, language?: Locale) {
|
||||
if (!tts) {
|
||||
// alert("TTS not initialized");
|
||||
// console.error("TTS not initialized");
|
||||
await initTTS();
|
||||
// return;
|
||||
}
|
||||
duck();
|
||||
await duck();
|
||||
await new Promise((resolve) => setTimeout(resolve, 500)); // wait a bit for ducking to take effect
|
||||
if (tts !== "web") {
|
||||
try {
|
||||
await invoke("plugin:tts|speak", { text });
|
||||
await invoke("plugin:tts|speak", {
|
||||
text,
|
||||
language: language
|
||||
? m["language.tts"]({}, { locale: language })
|
||||
: m["language.tts"](),
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Error speaking text", e);
|
||||
alert(e);
|
||||
}
|
||||
} else {
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.lang = "de-DE";
|
||||
utterance.lang = m["language.speechSynthesis"]();
|
||||
window.speechSynthesis.speak(utterance);
|
||||
}
|
||||
unduck();
|
||||
await unduck();
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { location } from "$lib/components/lnv/location.svelte";
|
||||
import { map } from "$lib/components/lnv/map.svelte";
|
||||
import say from "./TTS";
|
||||
import say, { findLocaleForValhallaLanguage } from "./TTS";
|
||||
import type { ValhallaRequest } from "./ValhallaRequest";
|
||||
import type { LngLatBoundsLike } from "maplibre-gl";
|
||||
import { generateVoiceGuidance } from "./VoiceGuidance";
|
||||
import { keepScreenOn } from "tauri-plugin-keep-screen-on-api";
|
||||
import { m } from "$lang/messages";
|
||||
import { getFeature, getSpeed } from "../TileMeta";
|
||||
|
||||
export const routing = $state({
|
||||
geojson: {
|
||||
@ -149,6 +151,28 @@ export async function startRoute(trip: Trip) {
|
||||
let hasAnnouncedPreInstruction = false;
|
||||
const USE_LANDMARK_INSTRUCTIONS = false;
|
||||
|
||||
let maneuverSpeed = { idx: -1, speed: 50 };
|
||||
|
||||
async function getSpeedForManeuver(idx: number) {
|
||||
if (maneuverSpeed.idx === idx) {
|
||||
return maneuverSpeed.speed;
|
||||
}
|
||||
|
||||
if (!routing.currentTrip) return 50;
|
||||
|
||||
const esi = routing.currentTrip!.legs[0].maneuvers[idx].end_shape_index;
|
||||
const shape = routing.currentTrip!.legs[0].shape;
|
||||
const polyline = decodePolyline(shape);
|
||||
const point = polyline[esi];
|
||||
|
||||
const feature = await getFeature(point, "transportation");
|
||||
if (!feature || !feature.properties) return 50;
|
||||
|
||||
const speed = getSpeed(feature.properties.maxspeed || "50") || 50;
|
||||
maneuverSpeed = { idx, speed };
|
||||
return speed;
|
||||
}
|
||||
|
||||
async function tickRoute() {
|
||||
const trip = routing.currentTrip;
|
||||
const info = routing.currentTripInfo;
|
||||
@ -177,17 +201,17 @@ async function tickRoute() {
|
||||
}
|
||||
|
||||
// Check if the user is on the route
|
||||
if (!isOnShape(loc, polyline)) {
|
||||
if (!isOnShape(loc, polyline, 30)) {
|
||||
console.log("Off route!");
|
||||
if (!info.isOffRoute) {
|
||||
say("You went off route.");
|
||||
say(m["routing.off-route"]());
|
||||
}
|
||||
info.isOffRoute = true;
|
||||
// TODO: Implement re-routing logic
|
||||
return;
|
||||
} else {
|
||||
if (info.isOffRoute) {
|
||||
say("You are back on route.");
|
||||
say(m["routing.back-on-route"]());
|
||||
}
|
||||
info.isOffRoute = false;
|
||||
}
|
||||
@ -200,7 +224,7 @@ async function tickRoute() {
|
||||
// console.log("Distance to end of current maneuver: ", distanceToEnd, " meters");
|
||||
// console.log("Speed: ", location.speed, " km/h");
|
||||
const verbalDistance = verbalPreInstructionDistance(
|
||||
location.speed || 50, // Assuming location has a speed property
|
||||
await getSpeedForManeuver(routing.currentTripInfo.maneuverIdx),
|
||||
);
|
||||
if (distanceToEnd <= verbalDistance) {
|
||||
hasAnnouncedPreInstruction = true;
|
||||
@ -212,7 +236,8 @@ async function tickRoute() {
|
||||
// currentManeuver.verbal_pre_transition_instruction,
|
||||
instruction,
|
||||
);
|
||||
say(instruction);
|
||||
const locale = findLocaleForValhallaLanguage(trip.language);
|
||||
say(instruction, locale);
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +269,8 @@ async function tickRoute() {
|
||||
"[Verbal instruction] ",
|
||||
currentManeuver.verbal_post_transition_instruction,
|
||||
);
|
||||
say(currentManeuver.verbal_post_transition_instruction);
|
||||
const locale = findLocaleForValhallaLanguage(trip.language);
|
||||
say(currentManeuver.verbal_post_transition_instruction, locale);
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,7 +291,7 @@ async function tickRoute() {
|
||||
}
|
||||
|
||||
function verbalPreInstructionDistance(speed: number): number {
|
||||
return Math.min(speed, 30) * 2.222 + 37.144;
|
||||
return (Math.min(speed, 30) * 2.222 + 37.144) * 2;
|
||||
}
|
||||
|
||||
export function stopNavigation() {
|
||||
@ -300,9 +326,10 @@ function isOnLine(
|
||||
location: WorldLocation,
|
||||
from: WorldLocation,
|
||||
to: WorldLocation,
|
||||
toleranceMeters = 12,
|
||||
) {
|
||||
// Convert the 12-meter tolerance to degrees (approximation)
|
||||
const tolerance = 12 / 111320; // 1 degree latitude ≈ 111.32 km
|
||||
// Convert the tolerance to degrees (approximation)
|
||||
const tolerance = toleranceMeters / 111320; // 1 degree latitude ≈ 111.32 km
|
||||
|
||||
// Calculate the vector components
|
||||
const dx = to.lon - from.lon;
|
||||
@ -344,10 +371,13 @@ function isOnPoint(location: WorldLocation, point: WorldLocation) {
|
||||
return distance <= tolerance;
|
||||
}
|
||||
|
||||
function isOnShape(location: WorldLocation, shape: WorldLocation[]) {
|
||||
// Check if the location is on the line between from and to (3 meter tolerance)
|
||||
function isOnShape(
|
||||
location: WorldLocation,
|
||||
shape: WorldLocation[],
|
||||
toleranceMeters = 12,
|
||||
) {
|
||||
for (let i = 0; i < shape.length - 1; i++) {
|
||||
if (isOnLine(location, shape[i], shape[i + 1])) {
|
||||
if (isOnLine(location, shape[i], shape[i + 1], toleranceMeters)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,6 +203,18 @@ export async function hasStore(info: StoreInfo) {
|
||||
return store !== undefined;
|
||||
}
|
||||
|
||||
export async function storeSnapshot<T extends object>(info: StoreInfo) {
|
||||
const db = await getDB();
|
||||
const store = await db.getFromIndex("stores", "by-name-and-type", [
|
||||
info.name,
|
||||
info.type,
|
||||
]);
|
||||
if (!store) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(store.data) as T;
|
||||
}
|
||||
|
||||
// export async function store<T extends object>(info: StoreInfo) {
|
||||
// const store = await db.getFromIndex("stores", "by-name-and-type", [info.name, info.type]);
|
||||
// if (!store) {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { m } from "$lang/messages";
|
||||
import type {
|
||||
ValhallaCosting,
|
||||
ValhallaCostingOptions,
|
||||
@ -63,7 +64,7 @@ export function createValhallaRequest(
|
||||
costing,
|
||||
units: "kilometers",
|
||||
alternates: 2,
|
||||
language: "de-DE",
|
||||
language: m["language.valhalla"](),
|
||||
costing_options: costingOptions,
|
||||
turn_lanes: true,
|
||||
};
|
||||
|
||||
@ -78,16 +78,11 @@ interface StateValue<T> {
|
||||
v: T;
|
||||
}
|
||||
export const vehicles: WrappedValue<StoreValue<Vehicle>[]> = stores("vehicle");
|
||||
export const selectedVehicleIdx: StateValue<number | null> = $state({
|
||||
v: localStorage.getItem("selectedVehicle")
|
||||
? parseInt(localStorage.getItem("selectedVehicle")!)
|
||||
: null,
|
||||
export const selectedVehicleId: StateValue<string | null> = $state({
|
||||
v: localStorage.getItem("selectedVehicle") ?? null,
|
||||
});
|
||||
export const selectedVehicle: () => Vehicle | null = () => {
|
||||
return (
|
||||
vehicles.current[selectedVehicleIdx.v !== null ? selectedVehicleIdx.v : 0]
|
||||
?.data ?? null
|
||||
);
|
||||
export const selectedVehicle: () => StoreValue<Vehicle> | null = () => {
|
||||
return vehicles.current.find((v) => v.id === selectedVehicleId.v) ?? null;
|
||||
};
|
||||
|
||||
// export function setVehicles(_vehicles: Vehicle[]) {
|
||||
@ -104,18 +99,14 @@ export async function addVehicle(vehicle: Vehicle) {
|
||||
|
||||
export function selectVehicle(vehicle: Vehicle | null) {
|
||||
if (vehicle == null) {
|
||||
selectedVehicleIdx.v = null;
|
||||
selectedVehicleId.v = null;
|
||||
} else {
|
||||
selectedVehicleIdx.v = vehicles.current.findIndex(
|
||||
(v) => v.name === vehicle.name,
|
||||
);
|
||||
if (selectedVehicleIdx.v === -1) {
|
||||
selectedVehicleIdx.v = null;
|
||||
}
|
||||
selectedVehicleId.v =
|
||||
vehicles.current.find((v) => v.name === vehicle.name)?.id ?? null;
|
||||
}
|
||||
localStorage.setItem(
|
||||
"selectedVehicle",
|
||||
selectedVehicleIdx.v !== null ? selectedVehicleIdx.v.toString() : "",
|
||||
selectedVehicleId.v !== null ? selectedVehicleId.v.toString() : "",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user