diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..68997aa
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1,7 @@
+node_modules
+build
+.svelte-kit
+.env
+.env.*
+!.env.example
+.vite
diff --git a/app/LICENSE b/app/LICENSE
new file mode 100644
index 0000000..c05a2fa
--- /dev/null
+++ b/app/LICENSE
@@ -0,0 +1,28 @@
+MIT License
+
+Copyright (c) 2026 ORGANISATION KA INTERNATIONALE (OKI) & contributeurs JWE
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+Note : les photographies affichées par l'application restent la propriété de
+leurs auteurs respectifs et sont distribuées sous leurs licences Wikimedia
+Commons (voir content/credits.md). Les extraits d'articles Wikipédia sont
+sous licence CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/deed.fr).
diff --git a/app/package-lock.json b/app/package-lock.json
new file mode 100644
index 0000000..b0582b5
--- /dev/null
+++ b/app/package-lock.json
@@ -0,0 +1,2140 @@
+{
+ "name": "jwe",
+ "version": "2.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "jwe",
+ "version": "2.0.0",
+ "dependencies": {
+ "maplibre-gl": "^5.1.0"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-node": "^5.2.0",
+ "@sveltejs/kit": "^2.16.0",
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
+ "svelte": "^5.0.0",
+ "svelte-check": "^4.1.0",
+ "typescript": "^5.6.0",
+ "vite": "^6.0.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mapbox/jsonlint-lines-primitives": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz",
+ "integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 22"
+ }
+ },
+ "node_modules/@mapbox/point-geometry": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz",
+ "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
+ "license": "ISC"
+ },
+ "node_modules/@mapbox/tiny-sdf": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz",
+ "integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/@mapbox/unitbezier": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
+ "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/@mapbox/vector-tile": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz",
+ "integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@mapbox/point-geometry": "~1.1.0",
+ "@types/geojson": "^7946.0.16",
+ "pbf": "^4.0.2"
+ }
+ },
+ "node_modules/@mapbox/whoots-js": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
+ "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@maplibre/geojson-vt": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.1.tgz",
+ "integrity": "sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==",
+ "license": "ISC",
+ "dependencies": {
+ "kdbush": "^4.1.0"
+ }
+ },
+ "node_modules/@maplibre/maplibre-gl-style-spec": {
+ "version": "24.10.0",
+ "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.10.0.tgz",
+ "integrity": "sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==",
+ "license": "ISC",
+ "dependencies": {
+ "@mapbox/jsonlint-lines-primitives": "~2.0.2",
+ "@mapbox/unitbezier": "^1.0.0",
+ "json-stringify-pretty-compact": "^4.0.0",
+ "minimist": "^1.2.8",
+ "quickselect": "^3.0.0",
+ "tinyqueue": "^3.0.0"
+ },
+ "bin": {
+ "gl-style-format": "dist/gl-style-format.mjs",
+ "gl-style-migrate": "dist/gl-style-migrate.mjs",
+ "gl-style-validate": "dist/gl-style-validate.mjs"
+ }
+ },
+ "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz",
+ "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/@maplibre/mlt": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.12.tgz",
+ "integrity": "sha512-ZeK5w2TTeHOajcLaEQs1KZXw2V9wIKo1PmThlxlsHoXsQsYlBqLJzPOd6tJHRtGTChUY3DPPmjXRArYVvAbmZw==",
+ "license": "(MIT OR Apache-2.0)",
+ "dependencies": {
+ "@mapbox/point-geometry": "^1.1.0"
+ }
+ },
+ "node_modules/@maplibre/vt-pbf": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz",
+ "integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@mapbox/point-geometry": "^1.1.0",
+ "@types/geojson": "^7946.0.16",
+ "pbf": "^5.1.0"
+ }
+ },
+ "node_modules/@maplibre/vt-pbf/node_modules/pbf": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz",
+ "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "resolve-protobuf-schema": "^2.1.0"
+ },
+ "bin": {
+ "pbf": "bin/pbf"
+ }
+ },
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.29",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
+ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/plugin-commonjs": {
+ "version": "29.0.3",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz",
+ "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "commondir": "^1.0.1",
+ "estree-walker": "^2.0.2",
+ "fdir": "^6.2.0",
+ "is-reference": "1.2.1",
+ "magic-string": "^0.30.3",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=16.0.0 || 14 >= 14.17"
+ },
+ "peerDependencies": {
+ "rollup": "^2.68.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-json": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
+ "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-node-resolve": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz",
+ "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/resolve": "1.20.2",
+ "deepmerge": "^4.2.2",
+ "is-module": "^1.0.0",
+ "resolve": "^1.22.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.78.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-replace": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz",
+ "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "magic-string": "^0.30.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz",
+ "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
+ "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
+ "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
+ "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
+ "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
+ "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
+ "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
+ "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
+ "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
+ "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
+ "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
+ "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
+ "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
+ "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
+ "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
+ "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
+ "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
+ "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
+ "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
+ "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
+ "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
+ "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
+ "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
+ "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@sveltejs/acorn-typescript": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.11.tgz",
+ "integrity": "sha512-LFuZUkjJ9iF7JZye/aG5XM0SFcQ5VyL0oVX4WJ9dc0Va3R3s0OauX1BESVCb+YN/ol8TAfqGDDAQsTG627Y5kw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^8.9.0"
+ }
+ },
+ "node_modules/@sveltejs/adapter-node": {
+ "version": "5.5.7",
+ "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.5.7.tgz",
+ "integrity": "sha512-uOfc9eVlI3A37RRSaKcgrheBYPrfJwC9VMqDp8x/O6tlKdcLLvHThSWD0KNIbjQ/d+7bwLGx3vx6aowAcRfd2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/plugin-commonjs": "^29.0.0",
+ "@rollup/plugin-json": "^6.1.0",
+ "@rollup/plugin-node-resolve": "^16.0.0",
+ "@rollup/plugin-replace": "^6.0.3",
+ "rollup": "^4.59.0"
+ },
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.4.0"
+ }
+ },
+ "node_modules/@sveltejs/kit": {
+ "version": "2.69.3",
+ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.69.3.tgz",
+ "integrity": "sha512-cphwqMRcE19/9VkrIPr5qZhQ0SptSSDfDzRUpYHu9OJDFGuYBFyJzK+KQA27wB4YG32O/yF2QjBkDmTyo0vtCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@sveltejs/acorn-typescript": "^1.0.9",
+ "@types/cookie": "^0.6.0",
+ "acorn": "^8.16.0",
+ "cookie": "^0.6.0",
+ "devalue": "^5.8.1",
+ "esm-env": "^1.2.2",
+ "kleur": "^4.1.5",
+ "magic-string": "^0.30.5",
+ "mrmime": "^2.0.0",
+ "set-cookie-parser": "^3.0.0",
+ "sirv": "^3.0.0"
+ },
+ "bin": {
+ "svelte-kit": "svelte-kit.js"
+ },
+ "engines": {
+ "node": ">=18.13"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0",
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "typescript": "^5.3.3 || ^6.0.0",
+ "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@sveltejs/load-config": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz",
+ "integrity": "sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.0.0"
+ }
+ },
+ "node_modules/@sveltejs/vite-plugin-svelte": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz",
+ "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
+ "debug": "^4.4.1",
+ "deepmerge": "^4.3.1",
+ "kleur": "^4.1.5",
+ "magic-string": "^0.30.17",
+ "vitefu": "^1.0.6"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22"
+ },
+ "peerDependencies": {
+ "svelte": "^5.0.0",
+ "vite": "^6.0.0"
+ }
+ },
+ "node_modules/@sveltejs/vite-plugin-svelte-inspector": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz",
+ "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.7"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22"
+ },
+ "peerDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
+ "svelte": "^5.0.0",
+ "vite": "^6.0.0"
+ }
+ },
+ "node_modules/@types/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/resolve": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
+ "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/devalue": {
+ "version": "5.8.1",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz",
+ "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/earcut": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.3.tgz",
+ "integrity": "sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==",
+ "license": "ISC"
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/esm-env": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
+ "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esrap": {
+ "version": "2.2.13",
+ "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.13.tgz",
+ "integrity": "sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/types": "^8.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/types": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gl-matrix": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz",
+ "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==",
+ "license": "MIT"
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.2",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+ "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-reference": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+ "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/json-stringify-pretty-compact": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
+ "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
+ "license": "MIT"
+ },
+ "node_modules/kdbush": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz",
+ "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==",
+ "license": "ISC"
+ },
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/locate-character": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
+ "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/maplibre-gl": {
+ "version": "5.24.0",
+ "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz",
+ "integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@mapbox/jsonlint-lines-primitives": "^2.0.2",
+ "@mapbox/point-geometry": "^1.1.0",
+ "@mapbox/tiny-sdf": "^2.1.0",
+ "@mapbox/unitbezier": "^0.0.1",
+ "@mapbox/vector-tile": "^2.0.4",
+ "@mapbox/whoots-js": "^3.1.0",
+ "@maplibre/geojson-vt": "^6.1.0",
+ "@maplibre/maplibre-gl-style-spec": "^24.8.1",
+ "@maplibre/mlt": "^1.1.8",
+ "@maplibre/vt-pbf": "^4.3.0",
+ "@types/geojson": "^7946.0.16",
+ "earcut": "^3.0.2",
+ "gl-matrix": "^3.4.4",
+ "kdbush": "^4.0.2",
+ "murmurhash-js": "^1.0.0",
+ "pbf": "^4.0.1",
+ "potpack": "^2.1.0",
+ "quickselect": "^3.0.0",
+ "tinyqueue": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.14.0",
+ "npm": ">=8.1.0"
+ },
+ "funding": {
+ "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mri": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
+ "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/murmurhash-js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
+ "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pbf": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz",
+ "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "resolve-protobuf-schema": "^2.1.0"
+ },
+ "bin": {
+ "pbf": "bin/pbf"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
+ "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/potpack": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz",
+ "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==",
+ "license": "ISC"
+ },
+ "node_modules/protocol-buffers-schema": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz",
+ "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==",
+ "license": "MIT"
+ },
+ "node_modules/quickselect": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
+ "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
+ "license": "ISC"
+ },
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-protobuf-schema": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
+ "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "protocol-buffers-schema": "^3.3.1"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
+ "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.62.2",
+ "@rollup/rollup-android-arm64": "4.62.2",
+ "@rollup/rollup-darwin-arm64": "4.62.2",
+ "@rollup/rollup-darwin-x64": "4.62.2",
+ "@rollup/rollup-freebsd-arm64": "4.62.2",
+ "@rollup/rollup-freebsd-x64": "4.62.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.2",
+ "@rollup/rollup-linux-arm64-musl": "4.62.2",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.2",
+ "@rollup/rollup-linux-loong64-musl": "4.62.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.2",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-musl": "4.62.2",
+ "@rollup/rollup-openbsd-x64": "4.62.2",
+ "@rollup/rollup-openharmony-arm64": "4.62.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.2",
+ "@rollup/rollup-win32-x64-gnu": "4.62.2",
+ "@rollup/rollup-win32-x64-msvc": "4.62.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/sade": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
+ "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mri": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz",
+ "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sirv": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
+ "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/svelte": {
+ "version": "5.56.6",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.6.tgz",
+ "integrity": "sha512-p4HDLDogGHKRKCrgckQHNs5PEfXkju6JI5jTywueaKJI5hAdjPohEhRtQ0M1SWC/+TA73SPln+r7srr+7e4nZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@sveltejs/acorn-typescript": "^1.0.10",
+ "@types/estree": "^1.0.5",
+ "@types/trusted-types": "^2.0.7",
+ "acorn": "^8.12.1",
+ "aria-query": "5.3.1",
+ "axobject-query": "^4.1.0",
+ "clsx": "^2.1.1",
+ "devalue": "^5.8.1",
+ "esm-env": "^1.2.1",
+ "esrap": "^2.2.12",
+ "is-reference": "^3.0.3",
+ "locate-character": "^3.0.0",
+ "magic-string": "^0.30.11",
+ "zimmerframe": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/svelte-check": {
+ "version": "4.7.3",
+ "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.3.tgz",
+ "integrity": "sha512-DHdTCGX62R0fCxBEaT+USdASAnoaRBaaNczkRJl0K7o3WyoCeVUbVxo6fKqpOll/B+WMWCsiFK0eFrJSNBKZIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "@sveltejs/load-config": "^0.2.0",
+ "chokidar": "^4.0.1",
+ "fdir": "^6.2.0",
+ "picocolors": "^1.0.0",
+ "sade": "^1.7.4"
+ },
+ "bin": {
+ "svelte-check": "bin/svelte-check"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "peerDependencies": {
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "typescript": ">=5.0.0"
+ }
+ },
+ "node_modules/svelte/node_modules/is-reference": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
+ "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.6"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyqueue": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz",
+ "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
+ "license": "ISC"
+ },
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitefu": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz",
+ "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==",
+ "dev": true,
+ "license": "MIT",
+ "workspaces": [
+ "tests/deps/*",
+ "tests/projects/*",
+ "tests/projects/workspace/packages/*"
+ ],
+ "peerDependencies": {
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/zimmerframe": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
+ "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
+ "dev": true,
+ "license": "MIT"
+ }
+ }
+}
diff --git a/app/package.json b/app/package.json
new file mode 100644
index 0000000..94dc567
--- /dev/null
+++ b/app/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "jwe",
+ "version": "2.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite dev",
+ "build": "vite build",
+ "preview": "vite preview",
+ "start": "node build",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-node": "^5.2.0",
+ "@sveltejs/kit": "^2.16.0",
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
+ "svelte": "^5.0.0",
+ "svelte-check": "^4.1.0",
+ "typescript": "^5.6.0",
+ "vite": "^6.0.0"
+ },
+ "dependencies": {
+ "maplibre-gl": "^5.1.0"
+ }
+}
diff --git a/app/src/app.d.ts b/app/src/app.d.ts
new file mode 100644
index 0000000..0d053c2
--- /dev/null
+++ b/app/src/app.d.ts
@@ -0,0 +1,12 @@
+// See https://svelte.dev/docs/kit/types#app.d.ts
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/app/src/app.html b/app/src/app.html
new file mode 100644
index 0000000..cc0701d
--- /dev/null
+++ b/app/src/app.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
+
diff --git a/app/src/lib/components/GameMap.svelte b/app/src/lib/components/GameMap.svelte
new file mode 100644
index 0000000..64a8375
--- /dev/null
+++ b/app/src/lib/components/GameMap.svelte
@@ -0,0 +1,421 @@
+
+
+
+
+
+
+
+ {#each REGIONS as region (region)}
+
+ {/each}
+
+
+
+
diff --git a/app/src/lib/components/LangSwitch.svelte b/app/src/lib/components/LangSwitch.svelte
new file mode 100644
index 0000000..e4f07fd
--- /dev/null
+++ b/app/src/lib/components/LangSwitch.svelte
@@ -0,0 +1,43 @@
+
+
+
+ {#each ['fr', 'gcf'] as l (l)}
+
+ {/each}
+
+
+
diff --git a/app/src/lib/components/PhotoPanel.svelte b/app/src/lib/components/PhotoPanel.svelte
new file mode 100644
index 0000000..bdbdd34
--- /dev/null
+++ b/app/src/lib/components/PhotoPanel.svelte
@@ -0,0 +1,47 @@
+
+
+
+
+ {#if revealed}
+ {photo.credit}
+ {/if}
+
+
+
diff --git a/app/src/lib/components/ResultPanel.svelte b/app/src/lib/components/ResultPanel.svelte
new file mode 100644
index 0000000..cc3bd89
--- /dev/null
+++ b/app/src/lib/components/ResultPanel.svelte
@@ -0,0 +1,114 @@
+
+
+
+
+ {result.score > 0 ? t().micro.bravo : t().micro.kiteSaYe}
+
+ {result.score}
+ {t().resultat.points}
+
+ {#if result.distanceKm !== null}
+ {t().resultat.distance} : {result.distanceKm} {t().resultat.km}
+ {/if}
+
+ {result.nom}
+ {#if result.nom_creole && result.nom_creole !== result.nom}
+ · {result.nom_creole}
+ {/if}
+
+ {t().resultat.commune} : {result.commune} — {t().regions[result.region]}
+
+
+
+ {#key result.nom}
+ 0 ? t().resultat.trouve : t().resultat.aDecouvrir}
+ />
+ {/key}
+
+ {#if creditPhoto}
+ {creditPhoto}
+ {/if}
+
+ {#if onsuivant}
+
+ {/if}
+
+
+
diff --git a/app/src/lib/components/TimerRing.svelte b/app/src/lib/components/TimerRing.svelte
new file mode 100644
index 0000000..49718b7
--- /dev/null
+++ b/app/src/lib/components/TimerRing.svelte
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
diff --git a/app/src/lib/components/WikiFiche.svelte b/app/src/lib/components/WikiFiche.svelte
new file mode 100644
index 0000000..ba7c496
--- /dev/null
+++ b/app/src/lib/components/WikiFiche.svelte
@@ -0,0 +1,182 @@
+
+
+{#if wiki === undefined}
+
+
{t().explorer.chargerFiche}
+
+{:else if wiki}
+
+
+
+ {#if tonPositif || tonNeutre}
+ {tonPositif ?? tonNeutre}
+ {/if}
+ {#if wiki.thumbnail}
+
+ {/if}
+
+
+ {t().resultat.lireWiki} ↗
+
+
+{:else}
+
+
+ ◌ {t().lacune.titre}
+ {t().lacune.texte}
+
+ {t().lacune.cta} ↗
+
+
+ {t().lacune.guide}
+
+ {t().lacune.guideLien} ↗
+
+
+
+{/if}
+
+
diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts
new file mode 100644
index 0000000..5ece930
--- /dev/null
+++ b/app/src/lib/i18n/fr.ts
@@ -0,0 +1,109 @@
+const fr = {
+ app: {
+ titre: 'JWE',
+ sousTitre: 'Jeu de géolocalisation — Guadeloupe, Martinique, Guyane, La Réunion'
+ },
+ regions: {
+ GUADELOUPE: 'Guadeloupe',
+ MARTINIQUE: 'Martinique',
+ GUYANE: 'Guyane',
+ REUNION: 'La Réunion',
+ TOUTES: 'Toutes les régions'
+ },
+ modes: {
+ monument: 'Konnèt moniman',
+ monumentDesc: 'Reconnais le monument photographié et place-le sur la carte.',
+ lieu: 'Kote mwen ye ?',
+ lieuDesc: 'Une rue, un bord de mer, une forêt… devine où tu es.',
+ defi: 'Défi 5 rounds',
+ defiDesc: '5 lieux d’affilée, monuments et lieux mélangés.',
+ explorer: 'Aprann',
+ explorerDesc: 'Explore la carte librement et lis les fiches Wikipédia. Sans score.'
+ },
+ accueil: {
+ choixRegion: 'Choisis ta région',
+ choixMode: 'Choisis ton mode',
+ options: 'Options',
+ timer: 'Minuteur (60 s, bonus de score)',
+ jouer: 'Jouer'
+ },
+ jeu: {
+ charger: 'Chargement du lieu…',
+ erreur: 'Impossible de charger un lieu. Réessaie.',
+ reessayer: 'Réessayer',
+ valider: 'Valider ma position',
+ passer: 'Passer',
+ indice: 'Indice',
+ indicePenalite: '−10 % de score',
+ indiceRestant: 'indice restant',
+ indicesRestants: 'indices restants',
+ plusDIndice: 'Plus d’indice disponible',
+ poserMarqueur: 'Clique sur la carte pour placer ton marqueur',
+ round: 'Manche',
+ sur: 'sur',
+ difficulte: {
+ FACILE: 'Facile',
+ MOYEN: 'Moyen',
+ DIFFICILE: 'Difficile',
+ EXPERT: 'Expert'
+ }
+ },
+ resultat: {
+ bravo: 'Bravo !',
+ score: 'Score',
+ points: 'points',
+ distance: 'Distance',
+ km: 'km',
+ trouve: 'Tu as trouvé ! Apprends-en plus sur ce lieu.',
+ aDecouvrir: 'Le lieu était ici. Apprends-en plus sur lui.',
+ lireWiki: 'Lire la suite sur Wikipédia',
+ commune: 'Commune',
+ region: 'Région',
+ suivant: 'Lieu suivant',
+ voirBilan: 'Voir le bilan',
+ rejouer: 'Rejouer',
+ retourAccueil: 'Retour à l’accueil'
+ },
+ lacune: {
+ titre: 'Pwen blindé — Sa pa dokimanté',
+ texte:
+ 'Ce lieu existe, mais il n’a pas de page sur Wikipédia ni de fiche sur Wikidata. Pour nos régions, c’est un enjeu majeur : si nous ne documentons pas nos propres territoires, notre histoire et notre géographie disparaissent des cartes numériques et des bases de données mondiales (qui alimentent même l’intelligence artificielle).',
+ cta: 'Kontribyé sou Wikipédia',
+ guide: 'Besoin d’aide pour démarrer ? Suis le guide OKI « Comment contribuer à Wikipédia ».',
+ guideLien: 'Ouvrir le guide de contribution'
+ },
+ defi: {
+ bilan: 'Bilan du défi',
+ scoreTotal: 'Score total',
+ partager: 'Partager',
+ telecharger: 'Télécharger l’image',
+ taPosition: 'Ta position',
+ positionReelle: 'Position réelle',
+ manche: 'Manche'
+ },
+ explorer: {
+ titre: 'Aprann — Explorer',
+ filtrer: 'Filtrer par région',
+ chargerFiche: 'Chargement de la fiche…',
+ aucuneFiche: 'Sélectionne un marqueur sur la carte.',
+ fermer: 'Fermer le panneau'
+ },
+ carte: {
+ ariaLabel: 'Carte de jeu. Flèches pour déplacer le marqueur, Entrée pour valider.',
+ sautRegion: 'Aller à la région',
+ zoomIn: 'Zoomer',
+ zoomOut: 'Dézoomer'
+ },
+ micro: {
+ bravo: 'Bravo !',
+ kiteSaYe: 'Kité sa ye ?',
+ pwenBlinde: 'Pwen blindé'
+ },
+ langue: {
+ fr: 'Français',
+ gcf: 'Kreyòl'
+ }
+};
+
+export default fr;
+export type Dict = typeof fr;
diff --git a/app/src/lib/i18n/gcf.ts b/app/src/lib/i18n/gcf.ts
new file mode 100644
index 0000000..772c3d8
--- /dev/null
+++ b/app/src/lib/i18n/gcf.ts
@@ -0,0 +1,110 @@
+import type { Dict } from './fr';
+
+const gcf: Dict = {
+ app: {
+ titre: 'JWE',
+ sousTitre: 'Jwé jéyolokalizasyon — Gwadloup, Matinik, Giyan, Léryinyon'
+ },
+ regions: {
+ GUADELOUPE: 'Gwadloup',
+ MARTINIQUE: 'Matinik',
+ GUYANE: 'Giyan',
+ REUNION: 'Léryinyon',
+ TOUTES: 'Tout péyi yo'
+ },
+ modes: {
+ monument: 'Konnèt moniman',
+ monumentDesc: 'Roukonèt moniman-an asou foto-a é mété’y asou kat-la.',
+ lieu: 'Kote mwen ye ?',
+ lieuDesc: 'On lari, on bodlanmè, on fowé… chwé kote ou yé.',
+ defi: 'Défi 5 rounds',
+ defiDesc: '5 koté youn dèyè ròt, moniman épi plas mélanjé.',
+ explorer: 'Aprann',
+ explorerDesc: 'Flanné asou kat-la é li fich Wikipédia yo. San pwen.'
+ },
+ accueil: {
+ choixRegion: 'Chwazi péyi ou',
+ choixMode: 'Chwazi jan ou vlé jwé',
+ options: 'Règlaj',
+ timer: 'Kronomèt (60 s, bonus pwen)',
+ jouer: 'Jwé'
+ },
+ jeu: {
+ charger: 'Nou ka chajé koté-la…',
+ erreur: 'Nou pa rivé chajé on koté. Éséyé ankò.',
+ reessayer: 'Éséyé ankò',
+ valider: 'Validé pozisyon mwen',
+ passer: 'Pasé',
+ indice: 'Endis',
+ indicePenalite: '−10 % pwen',
+ indiceRestant: 'endis ki rété',
+ indicesRestants: 'endis ki rété',
+ plusDIndice: 'Pa ni endis ankò',
+ poserMarqueur: 'Kwiké asou kat-la pou mété makò-a',
+ round: 'Mannèk',
+ sur: 'asou',
+ difficulte: {
+ FACILE: 'Fasil',
+ MOYEN: 'Mwayen',
+ DIFFICILE: 'Difisil',
+ EXPERT: 'Espè'
+ }
+ },
+ resultat: {
+ bravo: 'Bravo !',
+ score: 'Pwen',
+ points: 'pwen',
+ distance: 'Distans',
+ km: 'km',
+ trouve: 'Ou trouvé ! Aprann pli plis asou koté sa-a.',
+ aDecouvrir: 'Koté-la té la. Aprann pli plis asou’y.',
+ lireWiki: 'Li swit-la asou Wikipédia',
+ commune: 'Komin',
+ region: 'Péyi',
+ suivant: 'Koté swivan',
+ voirBilan: 'Wè bilan-an',
+ rejouer: 'Rèjwé',
+ retourAccueil: 'Tounen an lakay'
+ },
+ lacune: {
+ titre: 'Pwen blindé — Sa pa dokimanté',
+ texte:
+ 'Koté sa-a égzisté, men i pa ni on paj asou Wikipédia ni on fich asou Wikidata. Pou péyi nou, sé on zafè enpòtan anpil : si nou pa dokimanté pwòp téritwa nou, listwa épi jéwografi nou kay disparèt asou kat nimérik épi labaz donné lemonn yo (ki manm ka bay manjé a lintelijans atifisyèl).',
+ cta: 'Kontribyé sou Wikipédia',
+ guide: 'Ou bizwen èd pou koumansé ? Swiv gid OKI « Kijan pou kontribyé sou Wikipédia ».',
+ guideLien: 'Ouvri gid kontribisyon-an'
+ },
+ defi: {
+ bilan: 'Bilan défi-a',
+ scoreTotal: 'Total pwen',
+ partager: 'Palé di moun',
+ telecharger: 'Téléchajé foto-a',
+ taPosition: 'Pozisyon ou',
+ positionReelle: 'Rèl pozisyon-an',
+ manche: 'Mannèk'
+ },
+ explorer: {
+ titre: 'Aprann — Flanné',
+ filtrer: 'Triyé pa péyi',
+ chargerFiche: 'Nou ka chajé fich-la…',
+ aucuneFiche: 'Kwiké asou on makò asou kat-la.',
+ fermer: 'Fèmé pano-a'
+ },
+ carte: {
+ ariaLabel: 'Kat jwé-a. Flèch yo pou déplasé makò-a, Antré pou validé.',
+ sautRegion: 'Alé o péyi',
+ zoomIn: 'Zoumé',
+ zoomOut: 'Dézoumé'
+ },
+ micro: {
+ bravo: 'Bravo !',
+ kiteSaYe: 'Kité sa ye ?',
+ pwenBlinde: 'Pwen blindé'
+ },
+ langue: {
+ fr: 'Français',
+ gcf: 'Kreyòl'
+ }
+};
+
+export default gcf;
diff --git a/app/src/lib/i18n/store.svelte.ts b/app/src/lib/i18n/store.svelte.ts
new file mode 100644
index 0000000..bfbc177
--- /dev/null
+++ b/app/src/lib/i18n/store.svelte.ts
@@ -0,0 +1,38 @@
+import { browser } from '$app/environment';
+import fr, { type Dict } from './fr';
+import gcf from './gcf';
+
+export type Lang = 'fr' | 'gcf';
+
+const dicts: Record = { fr, gcf };
+const STORAGE_KEY = 'jwe-lang';
+
+let lang = $state('fr');
+
+/** Dictionnaire courant — fonction réactive : lit le `$state`, donc les templates se mettent à jour. */
+export function t(): Dict {
+ return dicts[lang];
+}
+
+export function getLang(): Lang {
+ return lang;
+}
+
+export function setLang(next: Lang): void {
+ lang = next;
+ if (browser) {
+ localStorage.setItem(STORAGE_KEY, next);
+ document.documentElement.lang = next;
+ }
+}
+
+/** À appeler une fois au montage du layout : restaure la langue persistée. */
+export function initLang(): void {
+ if (!browser) return;
+ const saved = localStorage.getItem(STORAGE_KEY);
+ if (saved === 'fr' || saved === 'gcf') {
+ setLang(saved);
+ } else {
+ document.documentElement.lang = lang;
+ }
+}
diff --git a/app/src/lib/map/regions.ts b/app/src/lib/map/regions.ts
new file mode 100644
index 0000000..2fbf44e
--- /dev/null
+++ b/app/src/lib/map/regions.ts
@@ -0,0 +1,16 @@
+import type { Region } from '$lib/types';
+
+/** Bornes de chaque région : [lonOuest, latSud, lonEst, latNord]. */
+export const REGION_BOUNDS: Record = {
+ GUADELOUPE: [-61.9, 15.8, -60.95, 16.55],
+ MARTINIQUE: [-61.25, 14.35, -60.75, 14.95],
+ GUYANE: [-54.7, 1.9, -51.5, 6.0],
+ REUNION: [55.15, -21.42, 55.9, -20.82]
+};
+
+/** Enveloppe union des 4 régions (≈ lon −63…57, lat −23…18) : interdit de sortir du terrain de jeu. */
+export const MAX_BOUNDS: [number, number, number, number] = [-63, -23, 57, 18];
+
+export const MIN_ZOOM = 4;
+
+export const REGIONS: Region[] = ['GUADELOUPE', 'MARTINIQUE', 'GUYANE', 'REUNION'];
diff --git a/app/src/lib/map/style-oki.json b/app/src/lib/map/style-oki.json
new file mode 100644
index 0000000..6d98125
--- /dev/null
+++ b/app/src/lib/map/style-oki.json
@@ -0,0 +1,3346 @@
+{
+ "version": 8,
+ "sources": {
+ "ne2_shaded": {
+ "maxzoom": 6,
+ "tileSize": 256,
+ "tiles": [
+ "https://tiles.openfreemap.org/natural_earth/ne2sr/{z}/{x}/{y}.png"
+ ],
+ "type": "raster"
+ },
+ "openmaptiles": {
+ "type": "vector",
+ "url": "https://tiles.openfreemap.org/planet",
+ "attribution": "© OpenStreetMap contributors"
+ }
+ },
+ "sprite": "https://tiles.openfreemap.org/sprites/ofm_f384/ofm",
+ "glyphs": "https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "rgb(245, 239, 226)"
+ }
+ },
+ {
+ "id": "park",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "park",
+ "filter": [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ "paint": {
+ "fill-color": "rgb(128, 178, 116)"
+ }
+ },
+ {
+ "id": "water",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "water",
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ [
+ "!=",
+ [
+ "get",
+ "brunnel"
+ ],
+ "tunnel"
+ ]
+ ],
+ "paint": {
+ "fill-antialias": true,
+ "fill-color": "rgb(23, 94, 112)"
+ }
+ },
+ {
+ "id": "landcover_ice_shelf",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "landcover",
+ "maxzoom": 8,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "subclass"
+ ],
+ "ice_shelf"
+ ]
+ ],
+ "paint": {
+ "fill-color": "hsl(45, 40%, 96%)",
+ "fill-opacity": 0.7
+ }
+ },
+ {
+ "id": "landcover_glacier",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "landcover",
+ "maxzoom": 8,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "subclass"
+ ],
+ "glacier"
+ ]
+ ],
+ "paint": {
+ "fill-color": "hsl(45, 40%, 96%)",
+ "fill-opacity": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 0,
+ 1,
+ 8,
+ 0.5
+ ]
+ }
+ },
+ {
+ "id": "landuse_residential",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "landuse",
+ "maxzoom": 16,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "residential"
+ ]
+ ],
+ "paint": {
+ "fill-color": "rgb(238, 228, 207)",
+ "fill-opacity": [
+ "interpolate",
+ [
+ "exponential",
+ 0.6
+ ],
+ [
+ "zoom"
+ ],
+ 8,
+ 0.8,
+ 9,
+ 0.6
+ ]
+ }
+ },
+ {
+ "id": "landcover_wood",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "landcover",
+ "minzoom": 10,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "wood"
+ ]
+ ],
+ "paint": {
+ "fill-color": "rgb(96, 156, 92)",
+ "fill-opacity": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 8,
+ 0,
+ 12,
+ 1
+ ]
+ }
+ },
+ {
+ "id": "waterway",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "waterway",
+ "filter": [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ "paint": {
+ "line-color": "hsl(192, 60%, 38%)"
+ }
+ },
+ {
+ "id": "building",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "building",
+ "minzoom": 12,
+ "paint": {
+ "fill-antialias": true,
+ "fill-color": "rgb(238, 228, 207)",
+ "fill-outline-color": "rgb(214, 200, 174)"
+ }
+ },
+ {
+ "id": "tunnel_motorway_casing",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 6,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "brunnel"
+ ],
+ "tunnel"
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "motorway"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "rgb(216, 202, 176)",
+ "line-opacity": 1,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.4
+ ],
+ [
+ "zoom"
+ ],
+ 5.8,
+ 0,
+ 6,
+ 3,
+ 20,
+ 40
+ ]
+ }
+ },
+ {
+ "id": "tunnel_motorway_inner",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 6,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "brunnel"
+ ],
+ "tunnel"
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "motorway"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "rgb(236, 226, 206)",
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.4
+ ],
+ [
+ "zoom"
+ ],
+ 4,
+ 2,
+ 6,
+ 1.3,
+ 20,
+ 30
+ ]
+ }
+ },
+ {
+ "id": "aeroway-taxiway",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "aeroway",
+ "minzoom": 12,
+ "filter": [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "taxiway"
+ ],
+ true,
+ false
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "hsl(40, 30%, 80%)",
+ "line-opacity": 1,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.55
+ ],
+ [
+ "zoom"
+ ],
+ 13,
+ 1.8,
+ 20,
+ 20
+ ]
+ }
+ },
+ {
+ "id": "aeroway-runway-casing",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "aeroway",
+ "minzoom": 11,
+ "filter": [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "runway"
+ ],
+ true,
+ false
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "hsl(40, 30%, 80%)",
+ "line-opacity": 1,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.5
+ ],
+ [
+ "zoom"
+ ],
+ 11,
+ 6,
+ 17,
+ 55
+ ]
+ }
+ },
+ {
+ "id": "aeroway-area",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "aeroway",
+ "minzoom": 4,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "runway",
+ "taxiway"
+ ],
+ true,
+ false
+ ]
+ ],
+ "paint": {
+ "fill-color": "rgba(255, 255, 255, 1)",
+ "fill-opacity": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 13,
+ 0,
+ 14,
+ 1
+ ]
+ }
+ },
+ {
+ "id": "aeroway-runway",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "aeroway",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "runway"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "rgba(255, 255, 255, 1)",
+ "line-opacity": 1,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.5
+ ],
+ [
+ "zoom"
+ ],
+ 11,
+ 4,
+ 17,
+ 50
+ ]
+ }
+ },
+ {
+ "id": "road_area_pier",
+ "type": "fill",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPolygon",
+ "Polygon"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "pier"
+ ]
+ ],
+ "paint": {
+ "fill-antialias": true,
+ "fill-color": "rgb(245, 239, 226)"
+ }
+ },
+ {
+ "id": "road_pier",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "pier"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "rgb(245, 239, 226)",
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.2
+ ],
+ [
+ "zoom"
+ ],
+ 15,
+ 1,
+ 17,
+ 4
+ ]
+ }
+ },
+ {
+ "id": "highway_path",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "path"
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "rgb(236, 226, 206)",
+ "line-opacity": 0.9,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.2
+ ],
+ [
+ "zoom"
+ ],
+ 13,
+ 1,
+ 20,
+ 10
+ ]
+ }
+ },
+ {
+ "id": "highway_minor",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 8,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "minor",
+ "service",
+ "track"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "hsl(40, 30%, 80%)",
+ "line-opacity": 0.9,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.55
+ ],
+ [
+ "zoom"
+ ],
+ 13,
+ 1.8,
+ 20,
+ 20
+ ]
+ }
+ },
+ {
+ "id": "highway_major_casing",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "rgb(216, 202, 176)",
+ "line-dasharray": [
+ 12,
+ 0
+ ],
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.3
+ ],
+ [
+ "zoom"
+ ],
+ 10,
+ 3,
+ 20,
+ 23
+ ]
+ }
+ },
+ {
+ "id": "highway_major_inner",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#fff",
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.3
+ ],
+ [
+ "zoom"
+ ],
+ 10,
+ 2,
+ 20,
+ 20
+ ]
+ }
+ },
+ {
+ "id": "highway_major_subtle",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "maxzoom": 11,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "hsla(40, 30%, 78%, 0.69)",
+ "line-width": 2
+ }
+ },
+ {
+ "id": "highway_motorway_casing",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 6,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "match",
+ [
+ "get",
+ "brunnel"
+ ],
+ [
+ "bridge",
+ "tunnel"
+ ],
+ false,
+ true
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "motorway"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "rgb(216, 202, 176)",
+ "line-dasharray": [
+ 2,
+ 0
+ ],
+ "line-opacity": 1,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.4
+ ],
+ [
+ "zoom"
+ ],
+ 5.8,
+ 0,
+ 6,
+ 3,
+ 20,
+ 40
+ ]
+ }
+ },
+ {
+ "id": "highway_motorway_inner",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 6,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "match",
+ [
+ "get",
+ "brunnel"
+ ],
+ [
+ "bridge",
+ "tunnel"
+ ],
+ false,
+ true
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "motorway"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 5.8,
+ "hsla(40, 30%, 78%, 0.53)",
+ 6,
+ "#fff"
+ ],
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.4
+ ],
+ [
+ "zoom"
+ ],
+ 4,
+ 2,
+ 6,
+ 1.3,
+ 20,
+ 30
+ ]
+ }
+ },
+ {
+ "id": "highway_motorway_subtle",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "maxzoom": 6,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "motorway"
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "hsla(40, 30%, 78%, 0.53)",
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.4
+ ],
+ [
+ "zoom"
+ ],
+ 4,
+ 2,
+ 6,
+ 1.3
+ ]
+ }
+ },
+ {
+ "id": "railway_transit",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "transit"
+ ],
+ [
+ "match",
+ [
+ "get",
+ "brunnel"
+ ],
+ [
+ "tunnel"
+ ],
+ false,
+ true
+ ]
+ ]
+ ],
+ "layout": {
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#dddddd",
+ "line-width": 3
+ }
+ },
+ {
+ "id": "railway_transit_dashline",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "transit"
+ ],
+ [
+ "match",
+ [
+ "get",
+ "brunnel"
+ ],
+ [
+ "tunnel"
+ ],
+ false,
+ true
+ ]
+ ]
+ ],
+ "layout": {
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#fafafa",
+ "line-dasharray": [
+ 3,
+ 3
+ ],
+ "line-width": 2
+ }
+ },
+ {
+ "id": "railway_service",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "rail"
+ ],
+ [
+ "has",
+ "service"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#dddddd",
+ "line-width": 3
+ }
+ },
+ {
+ "id": "railway_service_dashline",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "rail"
+ ],
+ [
+ "has",
+ "service"
+ ]
+ ],
+ "layout": {
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#fafafa",
+ "line-dasharray": [
+ 3,
+ 3
+ ],
+ "line-width": 2
+ }
+ },
+ {
+ "id": "railway",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "!",
+ [
+ "has",
+ "service"
+ ]
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "rail"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#dddddd",
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.3
+ ],
+ [
+ "zoom"
+ ],
+ 16,
+ 3,
+ 20,
+ 7
+ ]
+ }
+ },
+ {
+ "id": "railway_dashline",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "!",
+ [
+ "has",
+ "service"
+ ]
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "rail"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "#fafafa",
+ "line-dasharray": [
+ 3,
+ 3
+ ],
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.3
+ ],
+ [
+ "zoom"
+ ],
+ 16,
+ 2,
+ 20,
+ 6
+ ]
+ }
+ },
+ {
+ "id": "highway_motorway_bridge_casing",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 6,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "brunnel"
+ ],
+ "bridge"
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "motorway"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "butt",
+ "line-join": "miter"
+ },
+ "paint": {
+ "line-color": "rgb(216, 202, 176)",
+ "line-dasharray": [
+ 2,
+ 0
+ ],
+ "line-opacity": 1,
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.4
+ ],
+ [
+ "zoom"
+ ],
+ 5.8,
+ 0,
+ 6,
+ 5,
+ 20,
+ 45
+ ]
+ }
+ },
+ {
+ "id": "highway_motorway_bridge_inner",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "transportation",
+ "minzoom": 6,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "brunnel"
+ ],
+ "bridge"
+ ],
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "motorway"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 5.8,
+ "hsla(40, 30%, 78%, 0.53)",
+ 6,
+ "#fff"
+ ],
+ "line-width": [
+ "interpolate",
+ [
+ "exponential",
+ 1.4
+ ],
+ [
+ "zoom"
+ ],
+ 4,
+ 2,
+ 6,
+ 1.3,
+ 20,
+ 30
+ ]
+ }
+ },
+ {
+ "id": "boundary_3",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "boundary",
+ "minzoom": 8,
+ "filter": [
+ "all",
+ [
+ ">=",
+ [
+ "get",
+ "admin_level"
+ ],
+ 3
+ ],
+ [
+ "<=",
+ [
+ "get",
+ "admin_level"
+ ],
+ 6
+ ],
+ [
+ "!=",
+ [
+ "get",
+ "maritime"
+ ],
+ 1
+ ],
+ [
+ "!=",
+ [
+ "get",
+ "disputed"
+ ],
+ 1
+ ],
+ [
+ "!",
+ [
+ "has",
+ "claimed_by"
+ ]
+ ]
+ ],
+ "paint": {
+ "line-color": "hsl(35, 12%, 55%)",
+ "line-dasharray": [
+ 1,
+ 1
+ ],
+ "line-width": [
+ "interpolate",
+ [
+ "linear",
+ 1
+ ],
+ [
+ "zoom"
+ ],
+ 7,
+ 1,
+ 11,
+ 2
+ ]
+ }
+ },
+ {
+ "id": "boundary_2",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "boundary",
+ "filter": [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "admin_level"
+ ],
+ 2
+ ],
+ [
+ "!=",
+ [
+ "get",
+ "maritime"
+ ],
+ 1
+ ],
+ [
+ "!=",
+ [
+ "get",
+ "disputed"
+ ],
+ 1
+ ],
+ [
+ "!",
+ [
+ "has",
+ "claimed_by"
+ ]
+ ]
+ ],
+ "layout": {
+ "line-cap": "round",
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "hsl(35, 12%, 55%)",
+ "line-opacity": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 0,
+ 0.4,
+ 4,
+ 1
+ ],
+ "line-width": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 3,
+ 1,
+ 5,
+ 1.2,
+ 12,
+ 3
+ ]
+ }
+ },
+ {
+ "id": "boundary_disputed",
+ "type": "line",
+ "source": "openmaptiles",
+ "source-layer": "boundary",
+ "filter": [
+ "all",
+ [
+ "!=",
+ [
+ "get",
+ "maritime"
+ ],
+ 1
+ ],
+ [
+ "==",
+ [
+ "get",
+ "disputed"
+ ],
+ 1
+ ]
+ ],
+ "paint": {
+ "line-color": "hsl(35, 12%, 55%)",
+ "line-dasharray": [
+ 1,
+ 2
+ ],
+ "line-width": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 3,
+ 1,
+ 5,
+ 1.2,
+ 12,
+ 3
+ ]
+ }
+ },
+ {
+ "id": "waterway_line_label",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "waterway",
+ "minzoom": 10,
+ "filter": [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "symbol-spacing": 350,
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ " ",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Italic"
+ ],
+ "text-letter-spacing": 0.2,
+ "text-max-width": 5,
+ "text-size": 14
+ },
+ "paint": {
+ "text-color": "hsl(35, 12%, 52%)",
+ "text-halo-color": "rgba(255,255,255,0.7)",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "water_name_point_label",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "water_name",
+ "filter": [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "MultiPoint",
+ "Point"
+ ],
+ true,
+ false
+ ],
+ "layout": {
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Italic"
+ ],
+ "text-letter-spacing": 0.2,
+ "text-max-width": 5,
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 0,
+ 10,
+ 8,
+ 14
+ ]
+ },
+ "paint": {
+ "text-color": "#495e91",
+ "text-halo-color": "rgba(255,255,255,0.7)",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "water_name_line_label",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "water_name",
+ "filter": [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "symbol-spacing": 350,
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ " ",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Italic"
+ ],
+ "text-letter-spacing": 0.2,
+ "text-max-width": 5,
+ "text-size": 14
+ },
+ "paint": {
+ "text-color": "#495e91",
+ "text-halo-color": "rgba(255,255,255,0.7)",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "highway-name-path",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "transportation_name",
+ "minzoom": 15.5,
+ "filter": [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "path"
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ " ",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-rotation-alignment": "map",
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 13,
+ 12,
+ 14,
+ 13
+ ]
+ },
+ "paint": {
+ "text-color": "hsl(30, 15%, 55%)",
+ "text-halo-color": "#f8f4f0",
+ "text-halo-width": 0.5
+ }
+ },
+ {
+ "id": "highway-name-minor",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "transportation_name",
+ "minzoom": 15,
+ "filter": [
+ "all",
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "minor",
+ "service",
+ "track"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ " ",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-rotation-alignment": "map",
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 13,
+ 12,
+ 14,
+ 13
+ ]
+ },
+ "paint": {
+ "text-color": "#666",
+ "text-halo-blur": 0.5,
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "highway-name-major",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "transportation_name",
+ "minzoom": 12.2,
+ "filter": [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "primary",
+ "secondary",
+ "tertiary",
+ "trunk"
+ ],
+ true,
+ false
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ " ",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-rotation-alignment": "map",
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 13,
+ 12,
+ 14,
+ 13
+ ]
+ },
+ "paint": {
+ "text-color": "#666",
+ "text-halo-blur": 0.5,
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "highway-shield-non-us",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "transportation_name",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [
+ "<=",
+ [
+ "get",
+ "ref_length"
+ ],
+ 6
+ ],
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "network"
+ ],
+ [
+ "us-highway",
+ "us-interstate",
+ "us-state"
+ ],
+ false,
+ true
+ ]
+ ],
+ "layout": {
+ "icon-image": [
+ "concat",
+ "road_",
+ [
+ "get",
+ "ref_length"
+ ]
+ ],
+ "icon-rotation-alignment": "viewport",
+ "icon-size": 1,
+ "symbol-placement": [
+ "step",
+ [
+ "zoom"
+ ],
+ "point",
+ 11,
+ "line"
+ ],
+ "symbol-spacing": 200,
+ "text-field": [
+ "to-string",
+ [
+ "get",
+ "ref"
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-rotation-alignment": "viewport",
+ "text-size": 10
+ }
+ },
+ {
+ "id": "highway-shield-us-interstate",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "transportation_name",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [
+ "<=",
+ [
+ "get",
+ "ref_length"
+ ],
+ 6
+ ],
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "network"
+ ],
+ [
+ "us-interstate"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "icon-image": [
+ "concat",
+ [
+ "get",
+ "network"
+ ],
+ "_",
+ [
+ "get",
+ "ref_length"
+ ]
+ ],
+ "icon-rotation-alignment": "viewport",
+ "icon-size": 1,
+ "symbol-placement": [
+ "step",
+ [
+ "zoom"
+ ],
+ "point",
+ 7,
+ "line",
+ 8,
+ "line"
+ ],
+ "symbol-spacing": 200,
+ "text-field": [
+ "to-string",
+ [
+ "get",
+ "ref"
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-rotation-alignment": "viewport",
+ "text-size": 10
+ }
+ },
+ {
+ "id": "road_shield_us",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "transportation_name",
+ "minzoom": 12,
+ "filter": [
+ "all",
+ [
+ "<=",
+ [
+ "get",
+ "ref_length"
+ ],
+ 6
+ ],
+ [
+ "match",
+ [
+ "geometry-type"
+ ],
+ [
+ "LineString",
+ "MultiLineString"
+ ],
+ true,
+ false
+ ],
+ [
+ "match",
+ [
+ "get",
+ "network"
+ ],
+ [
+ "us-highway",
+ "us-state"
+ ],
+ true,
+ false
+ ]
+ ],
+ "layout": {
+ "icon-image": [
+ "concat",
+ [
+ "get",
+ "network"
+ ],
+ "_",
+ [
+ "get",
+ "ref_length"
+ ]
+ ],
+ "icon-rotation-alignment": "viewport",
+ "icon-size": 1,
+ "symbol-placement": [
+ "step",
+ [
+ "zoom"
+ ],
+ "point",
+ 11,
+ "line"
+ ],
+ "symbol-spacing": 200,
+ "text-field": [
+ "to-string",
+ [
+ "get",
+ "ref"
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-rotation-alignment": "viewport",
+ "text-size": 10
+ }
+ },
+ {
+ "id": "airport",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "aerodrome_label",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [
+ "has",
+ "iata"
+ ]
+ ],
+ "layout": {
+ "icon-image": "airport_11",
+ "icon-size": 1,
+ "text-anchor": "top",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-max-width": 9,
+ "text-offset": [
+ 0,
+ 0.6
+ ],
+ "text-optional": true,
+ "text-padding": 2,
+ "text-size": 12
+ },
+ "paint": {
+ "text-color": "#666",
+ "text-halo-blur": 0.5,
+ "text-halo-color": "#ffffff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_other",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "minzoom": 8,
+ "filter": [
+ "match",
+ [
+ "get",
+ "class"
+ ],
+ [
+ "city",
+ "continent",
+ "country",
+ "state",
+ "town",
+ "village"
+ ],
+ false,
+ true
+ ],
+ "layout": {
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Italic"
+ ],
+ "text-letter-spacing": 0.1,
+ "text-max-width": 9,
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 8,
+ 9,
+ 12,
+ 10
+ ],
+ "text-transform": "uppercase"
+ },
+ "paint": {
+ "text-color": "#333",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_village",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "minzoom": 9,
+ "filter": [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "village"
+ ],
+ "layout": {
+ "icon-allow-overlap": true,
+ "icon-image": [
+ "step",
+ [
+ "zoom"
+ ],
+ "circle_11_black",
+ 10,
+ ""
+ ],
+ "icon-optional": false,
+ "icon-size": 0.2,
+ "text-anchor": "bottom",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-max-width": 8,
+ "text-size": [
+ "interpolate",
+ [
+ "exponential",
+ 1.2
+ ],
+ [
+ "zoom"
+ ],
+ 7,
+ 10,
+ 11,
+ 12
+ ]
+ },
+ "paint": {
+ "text-color": "#000",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_town",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "minzoom": 6,
+ "filter": [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "town"
+ ],
+ "layout": {
+ "icon-allow-overlap": true,
+ "icon-image": [
+ "step",
+ [
+ "zoom"
+ ],
+ "circle_11_black",
+ 10,
+ ""
+ ],
+ "icon-optional": false,
+ "icon-size": 0.2,
+ "text-anchor": "bottom",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-max-width": 8,
+ "text-size": [
+ "interpolate",
+ [
+ "exponential",
+ 1.2
+ ],
+ [
+ "zoom"
+ ],
+ 7,
+ 12,
+ 11,
+ 14
+ ]
+ },
+ "paint": {
+ "text-color": "#000",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_state",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "minzoom": 5,
+ "maxzoom": 8,
+ "filter": [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "state"
+ ],
+ "layout": {
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Italic"
+ ],
+ "text-letter-spacing": 0.2,
+ "text-max-width": 9,
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 5,
+ 10,
+ 8,
+ 14
+ ],
+ "text-transform": "uppercase"
+ },
+ "paint": {
+ "text-color": "#333",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_city",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "minzoom": 3,
+ "filter": [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "city"
+ ],
+ [
+ "!=",
+ [
+ "get",
+ "capital"
+ ],
+ 2
+ ]
+ ],
+ "layout": {
+ "icon-allow-overlap": true,
+ "icon-image": [
+ "step",
+ [
+ "zoom"
+ ],
+ "circle_11_black",
+ 9,
+ ""
+ ],
+ "icon-optional": false,
+ "icon-size": 0.4,
+ "text-anchor": "bottom",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Regular"
+ ],
+ "text-max-width": 8,
+ "text-offset": [
+ 0,
+ -0.1
+ ],
+ "text-size": [
+ "interpolate",
+ [
+ "exponential",
+ 1.2
+ ],
+ [
+ "zoom"
+ ],
+ 4,
+ 11,
+ 7,
+ 13,
+ 11,
+ 18
+ ]
+ },
+ "paint": {
+ "text-color": "#000",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_city_capital",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "minzoom": 3,
+ "filter": [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "city"
+ ],
+ [
+ "==",
+ [
+ "get",
+ "capital"
+ ],
+ 2
+ ]
+ ],
+ "layout": {
+ "icon-allow-overlap": true,
+ "icon-image": [
+ "step",
+ [
+ "zoom"
+ ],
+ "circle_11_black",
+ 9,
+ ""
+ ],
+ "icon-optional": false,
+ "icon-size": 0.5,
+ "text-anchor": "bottom",
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Bold"
+ ],
+ "text-max-width": 8,
+ "text-offset": [
+ 0,
+ -0.2
+ ],
+ "text-size": [
+ "interpolate",
+ [
+ "exponential",
+ 1.2
+ ],
+ [
+ "zoom"
+ ],
+ 4,
+ 12,
+ 7,
+ 14,
+ 11,
+ 20
+ ]
+ },
+ "paint": {
+ "text-color": "#000",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_country_3",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "minzoom": 2,
+ "maxzoom": 9,
+ "filter": [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "country"
+ ],
+ [
+ ">=",
+ [
+ "get",
+ "rank"
+ ],
+ 3
+ ]
+ ],
+ "layout": {
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Bold"
+ ],
+ "text-max-width": 6.25,
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 3,
+ 9,
+ 7,
+ 17
+ ]
+ },
+ "paint": {
+ "text-color": "#000",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_country_2",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "maxzoom": 9,
+ "filter": [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "country"
+ ],
+ [
+ "==",
+ [
+ "get",
+ "rank"
+ ],
+ 2
+ ]
+ ],
+ "layout": {
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Bold"
+ ],
+ "text-max-width": 6.25,
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 2,
+ 9,
+ 5,
+ 17
+ ]
+ },
+ "paint": {
+ "text-color": "#000",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "label_country_1",
+ "type": "symbol",
+ "source": "openmaptiles",
+ "source-layer": "place",
+ "maxzoom": 9,
+ "filter": [
+ "all",
+ [
+ "==",
+ [
+ "get",
+ "class"
+ ],
+ "country"
+ ],
+ [
+ "==",
+ [
+ "get",
+ "rank"
+ ],
+ 1
+ ]
+ ],
+ "layout": {
+ "text-field": [
+ "case",
+ [
+ "has",
+ "name:nonlatin"
+ ],
+ [
+ "concat",
+ [
+ "get",
+ "name:latin"
+ ],
+ "\n",
+ [
+ "get",
+ "name:nonlatin"
+ ]
+ ],
+ [
+ "coalesce",
+ [
+ "get",
+ "name_en"
+ ],
+ [
+ "get",
+ "name"
+ ]
+ ]
+ ],
+ "text-font": [
+ "Noto Sans Bold"
+ ],
+ "text-max-width": 6.25,
+ "text-size": [
+ "interpolate",
+ [
+ "linear"
+ ],
+ [
+ "zoom"
+ ],
+ 1,
+ 9,
+ 4,
+ 17
+ ]
+ },
+ "paint": {
+ "text-color": "#000",
+ "text-halo-blur": 1,
+ "text-halo-color": "#fff",
+ "text-halo-width": 1
+ }
+ }
+ ],
+ "name": "OKI Caraïbes"
+}
\ No newline at end of file
diff --git a/app/src/lib/server/lieux.ts b/app/src/lib/server/lieux.ts
new file mode 100644
index 0000000..1424ffe
--- /dev/null
+++ b/app/src/lib/server/lieux.ts
@@ -0,0 +1,152 @@
+import rawLieux from './data/lieux.json';
+import type {
+ Categorie,
+ Difficulte,
+ ExplorerLieu,
+ GuessResult,
+ Lieu,
+ Region,
+ RoundPlace
+} from '$lib/types';
+
+const lieux = rawLieux as Lieu[];
+
+const REGIONS: Region[] = ['GUADELOUPE', 'MARTINIQUE', 'GUYANE', 'REUNION'];
+const CATEGORIES: Categorie[] = ['MONUMENT', 'LIEU'];
+
+export const MAX_POINTS = 5000;
+
+/** Rayon (km) de la courbe de score selon la difficulté du lieu. */
+export const RAYONS: Record = {
+ FACILE: 50,
+ MOYEN: 15,
+ DIFFICILE: 5,
+ EXPERT: 1
+};
+
+/** Distance haversine en km. */
+export function haversineKm(a: { lat: number; lon: number }, b: { lat: number; lon: number }): number {
+ const R = 6371;
+ const dLat = ((b.lat - a.lat) * Math.PI) / 180;
+ const dLon = ((b.lon - a.lon) * Math.PI) / 180;
+ const s =
+ Math.sin(dLat / 2) ** 2 +
+ Math.cos((a.lat * Math.PI) / 180) * Math.cos((b.lat * Math.PI) / 180) * Math.sin(dLon / 2) ** 2;
+ return 2 * R * Math.asin(Math.sqrt(s));
+}
+
+function parseRegion(value: string | null): Region | null {
+ return value && REGIONS.includes(value as Region) ? (value as Region) : null;
+}
+
+function parseCategorie(value: string | null): Categorie | null {
+ return value && CATEGORIES.includes(value as Categorie) ? (value as Categorie) : null;
+}
+
+export function getLieuById(id: string): Lieu | undefined {
+ return lieux.find((l) => l.id === id);
+}
+
+/** Sélection aléatoire d'un lieu, avec filtres optionnels. */
+export function getRandomLieu(options: {
+ region?: string | null;
+ categorie?: string | null;
+ exclude?: string | null;
+}): Lieu | null {
+ const region = parseRegion(options.region ?? null);
+ const categorie = parseCategorie(options.categorie ?? null);
+ const excluded = new Set(
+ (options.exclude ?? '')
+ .split(',')
+ .map((s) => s.trim())
+ .filter(Boolean)
+ );
+ let pool = lieux.filter((l) => !excluded.has(l.id));
+ if (region) pool = pool.filter((l) => l.region === region);
+ if (categorie) pool = pool.filter((l) => l.categorie === categorie);
+ // Si tout a été exclu, on réessaie sans les exclusions (le jeu doit continuer).
+ if (pool.length === 0) {
+ pool = lieux;
+ if (region) pool = pool.filter((l) => l.region === region);
+ if (categorie) pool = pool.filter((l) => l.categorie === categorie);
+ }
+ if (pool.length === 0) return null;
+ return pool[Math.floor(Math.random() * pool.length)];
+}
+
+/** Anti-triche : ne garde que les champs publiques pour /api/round. */
+export function sanitizeRound(lieu: Lieu): RoundPlace {
+ return {
+ id: lieu.id,
+ nom: lieu.nom,
+ nom_creole: lieu.nom_creole,
+ categorie: lieu.categorie,
+ difficulte: lieu.difficulte,
+ photo: lieu.photo,
+ indices: lieu.indices
+ };
+}
+
+/**
+ * Scoring recalculé côté serveur :
+ * score = round(5000 × e^(−d/r)), r selon difficulté ;
+ * pénalité −10 % par indice ; bonus temps ≤ +20 % ; total plafonné à 5000.
+ */
+export function scoreGuess(
+ lieu: Lieu,
+ guess: { lat: number; lon: number },
+ hintsUsed: number,
+ timeMs?: number
+): GuessResult {
+ const distanceKm = haversineKm(guess, lieu.coordonnees);
+ const base = Math.round(MAX_POINTS * Math.exp(-distanceKm / RAYONS[lieu.difficulte]));
+ const penalises = base * Math.max(0, 1 - 0.1 * Math.max(0, hintsUsed));
+ let total = penalises;
+ if (typeof timeMs === 'number' && Number.isFinite(timeMs) && timeMs >= 0) {
+ total += penalises * 0.2 * Math.max(0, 1 - timeMs / 60000);
+ }
+ const score = Math.min(MAX_POINTS, Math.round(total));
+ return {
+ distanceKm: Math.round(distanceKm * 10) / 10,
+ score,
+ coordonnees: lieu.coordonnees,
+ commune: lieu.commune,
+ region: lieu.region,
+ nom: lieu.nom,
+ nom_creole: lieu.nom_creole,
+ wikidata_id: lieu.wikidata_id
+ };
+}
+
+/** Réponse « Passer » : la vérité est révélée, score 0, pas de distance. */
+export function skipResult(lieu: Lieu): GuessResult {
+ return {
+ distanceKm: null,
+ score: 0,
+ coordonnees: lieu.coordonnees,
+ commune: lieu.commune,
+ region: lieu.region,
+ nom: lieu.nom,
+ nom_creole: lieu.nom_creole,
+ wikidata_id: lieu.wikidata_id
+ };
+}
+
+/** Catalogue Explorer : coords arrondies à 0.05° (mode sans score, fiches autorisées). */
+export function catalogueExplorer(): ExplorerLieu[] {
+ return lieux.map((l) => ({
+ id: l.id,
+ nom: l.nom,
+ nom_creole: l.nom_creole,
+ region: l.region,
+ commune: l.commune,
+ coordonnees: {
+ lat: Math.round(l.coordonnees.lat / 0.05) * 0.05,
+ lon: Math.round(l.coordonnees.lon / 0.05) * 0.05
+ },
+ categorie: l.categorie,
+ difficulte: l.difficulte,
+ wikidata_id: l.wikidata_id,
+ photo: l.photo
+ }));
+}
diff --git a/app/src/lib/stores/defi.svelte.ts b/app/src/lib/stores/defi.svelte.ts
new file mode 100644
index 0000000..533e456
--- /dev/null
+++ b/app/src/lib/stores/defi.svelte.ts
@@ -0,0 +1,32 @@
+import type { Coordonnees, GuessResult, RoundPlace } from '$lib/types';
+
+export interface MancheDefi {
+ place: RoundPlace;
+ guess: Coordonnees | null;
+ result: GuessResult;
+}
+
+/** État du Défi 5 rounds — client uniquement, sans session serveur. */
+function creerDefiStore() {
+ let manches = $state([]);
+
+ return {
+ get manches() {
+ return manches;
+ },
+ get total() {
+ return manches.reduce((s, m) => s + m.result.score, 0);
+ },
+ get termine() {
+ return manches.length >= 5;
+ },
+ ajouter(manche: MancheDefi) {
+ manches = [...manches, manche];
+ },
+ reinitialiser() {
+ manches = [];
+ }
+ };
+}
+
+export const defi = creerDefiStore();
diff --git a/app/src/lib/styles/oki.css b/app/src/lib/styles/oki.css
new file mode 100644
index 0000000..e09ca29
--- /dev/null
+++ b/app/src/lib/styles/oki.css
@@ -0,0 +1,118 @@
+/* Tokens OKI — doctrine « calm technology » : couleurs ancrées dans le territoire,
+ un seul accent fort (corail) réservé à l'action primaire (Von Restorff). */
+:root {
+ --oki-vert: #0b3d2e; /* vert profond — couleur principale de la marque */
+ --oki-vert-clair: #17624a;
+ --oki-vert-doux: #e3efe7;
+ --oki-sable: #f5efe2; /* fonds */
+ --oki-sable-fonce: #e7dcc4;
+ --oki-bleu: #175e70; /* bleu caraïbe profond */
+ --oki-bleu-doux: #d9ecef;
+ --oki-corail: #e4572e; /* accent — action primaire UNIQUEMENT */
+ --oki-corail-fonce: #c4441f;
+ --oki-encre: #22302a;
+ --oki-encre-doux: #5a6b62;
+ --oki-blanc: #fffdf8;
+ --oki-lacune-fond: #fdf3e3; /* panneau Cas B « Pwen blindé » — teinte distincte */
+ --oki-lacune-bord: #d9a441;
+
+ --oki-radius: 14px;
+ --oki-radius-petit: 9px;
+ --oki-ombre: 0 2px 10px rgba(11, 61, 46, 0.14);
+ --oki-font:
+ system-ui,
+ -apple-system,
+ 'Segoe UI',
+ Roboto,
+ sans-serif;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body {
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: var(--oki-font);
+ color: var(--oki-encre);
+ background: var(--oki-sable);
+ line-height: 1.5;
+}
+
+button {
+ font-family: inherit;
+}
+
+/* — Boutons — */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ border: none;
+ border-radius: var(--oki-radius-petit);
+ padding: 0.85rem 1.6rem;
+ font-size: 1.05rem;
+ font-weight: 650;
+ cursor: pointer;
+ text-decoration: none;
+ transition: background-color 0.15s ease;
+}
+
+.btn:focus-visible,
+a:focus-visible,
+[tabindex]:focus-visible {
+ outline: 3px solid var(--oki-bleu);
+ outline-offset: 2px;
+}
+
+/* Action primaire : le seul élément fortement coloré de l'écran */
+.btn-primaire {
+ background: var(--oki-corail);
+ color: #fff;
+ box-shadow: var(--oki-ombre);
+}
+
+.btn-primaire:hover {
+ background: var(--oki-corail-fonce);
+}
+
+.btn-primaire:disabled {
+ background: var(--oki-sable-fonce);
+ color: var(--oki-encre-doux);
+ cursor: not-allowed;
+ box-shadow: none;
+}
+
+.btn-secondaire {
+ background: var(--oki-vert-doux);
+ color: var(--oki-vert);
+}
+
+.btn-secondaire:hover {
+ background: #d2e5d9;
+}
+
+.btn-tertiaire {
+ background: transparent;
+ color: var(--oki-vert-clair);
+ border: 1.5px solid var(--oki-vert-clair);
+}
+
+.btn-tertiaire:hover {
+ background: var(--oki-vert-doux);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ transition-duration: 0.01ms !important;
+ animation-duration: 0.01ms !important;
+ }
+}
diff --git a/app/src/lib/types.ts b/app/src/lib/types.ts
new file mode 100644
index 0000000..f6c3a7b
--- /dev/null
+++ b/app/src/lib/types.ts
@@ -0,0 +1,82 @@
+export type Region = 'GUADELOUPE' | 'MARTINIQUE' | 'GUYANE' | 'REUNION';
+export type Categorie = 'MONUMENT' | 'LIEU';
+export type Difficulte = 'FACILE' | 'MOYEN' | 'DIFFICILE' | 'EXPERT';
+
+export interface Coordonnees {
+ lat: number;
+ lon: number;
+}
+
+export interface Indice {
+ niveau: number;
+ texte: string;
+}
+
+export interface Photo {
+ url: string;
+ credit: string;
+ alt: string;
+}
+
+/** Lieu complet — serveur uniquement, jamais exposé tel quel au client. */
+export interface Lieu {
+ id: string;
+ nom: string;
+ nom_creole: string;
+ region: Region;
+ commune: string;
+ coordonnees: Coordonnees;
+ categorie: Categorie;
+ difficulte: Difficulte;
+ wikidata_id: string | null;
+ photo: Photo;
+ indices: Indice[];
+}
+
+/** Lieu SANITISÉ envoyé par GET /api/round (aucune coordonnée, commune, région, wikidata_id). */
+export interface RoundPlace {
+ id: string;
+ nom: string;
+ nom_creole: string;
+ categorie: Categorie;
+ difficulte: Difficulte;
+ photo: Photo;
+ indices: Indice[];
+}
+
+/** Réponse de POST /api/guess : la vérité n'est révélée qu'après soumission. */
+export interface GuessResult {
+ distanceKm: number | null;
+ score: number;
+ coordonnees: Coordonnees;
+ commune: string;
+ region: Region;
+ nom: string;
+ nom_creole: string;
+ wikidata_id: string | null;
+}
+
+/** Entrée du catalogue Explorer : coords arrondies à 0.05°, mode sans score. */
+export interface ExplorerLieu {
+ id: string;
+ nom: string;
+ nom_creole: string;
+ region: Region;
+ commune: string;
+ coordonnees: Coordonnees;
+ categorie: Categorie;
+ difficulte: Difficulte;
+ wikidata_id: string | null;
+ photo: Photo;
+}
+
+/** Réponse normalisée de GET /api/wiki/[qid]. */
+export interface WikiSummary {
+ qid: string;
+ title: string;
+ extract: string;
+ url: string;
+ thumbnail: string | null;
+}
+
+export type GameMode = 'monument' | 'lieu' | 'defi';
diff --git a/app/src/routes/+layout.svelte b/app/src/routes/+layout.svelte
new file mode 100644
index 0000000..6a2118f
--- /dev/null
+++ b/app/src/routes/+layout.svelte
@@ -0,0 +1,82 @@
+
+
+
+
+
+ {@render children()}
+
+
+
+
diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte
new file mode 100644
index 0000000..bf4f59f
--- /dev/null
+++ b/app/src/routes/+page.svelte
@@ -0,0 +1,258 @@
+
+
+
+ {t().app.titre} — {t().app.sousTitre}
+
+
+
+
+
+ {t().app.titre}
+ {t().micro.kiteSaYe}
+ {t().app.sousTitre}
+
+
+
+ {t().accueil.choixRegion}
+
+ {#each REGIONS as r (r)}
+
+ {/each}
+
+
+
+
+
+ {t().accueil.choixMode}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/routes/api/guess/+server.ts b/app/src/routes/api/guess/+server.ts
new file mode 100644
index 0000000..99c2ed0
--- /dev/null
+++ b/app/src/routes/api/guess/+server.ts
@@ -0,0 +1,51 @@
+import { json } from '@sveltejs/kit';
+import { getLieuById, scoreGuess, skipResult } from '$lib/server/lieux';
+import type { RequestHandler } from './$types';
+
+interface GuessBody {
+ id?: unknown;
+ lat?: unknown;
+ lon?: unknown;
+ hintsUsed?: unknown;
+ timeMs?: unknown;
+ skip?: unknown;
+}
+
+/**
+ * POST /api/guess { id, lat, lon, hintsUsed, timeMs? }
+ * → score recalculé serveur + vérité du lieu (révélée seulement ici).
+ * `skip: true` révèle la réponse avec un score de 0 (bouton « Passer »).
+ */
+export const POST: RequestHandler = async ({ request }) => {
+ let body: GuessBody;
+ try {
+ body = (await request.json()) as GuessBody;
+ } catch {
+ return json({ erreur: 'Corps JSON invalide' }, { status: 400 });
+ }
+
+ if (typeof body.id !== 'string') {
+ return json({ erreur: 'id manquant' }, { status: 400 });
+ }
+ const lieu = getLieuById(body.id);
+ if (!lieu) {
+ return json({ erreur: 'Lieu inconnu' }, { status: 404 });
+ }
+
+ if (body.skip === true) {
+ return json(skipResult(lieu));
+ }
+
+ const lat = Number(body.lat);
+ const lon = Number(body.lon);
+ if (!Number.isFinite(lat) || !Number.isFinite(lon) || Math.abs(lat) > 90 || Math.abs(lon) > 180) {
+ return json({ erreur: 'Coordonnées invalides' }, { status: 400 });
+ }
+ const hintsUsed = Math.max(0, Math.floor(Number(body.hintsUsed) || 0));
+ const timeMs =
+ typeof body.timeMs === 'number' && Number.isFinite(body.timeMs) && body.timeMs >= 0
+ ? body.timeMs
+ : undefined;
+
+ return json(scoreGuess(lieu, { lat, lon }, hintsUsed, timeMs));
+};
diff --git a/app/src/routes/api/lieux/+server.ts b/app/src/routes/api/lieux/+server.ts
new file mode 100644
index 0000000..f850351
--- /dev/null
+++ b/app/src/routes/api/lieux/+server.ts
@@ -0,0 +1,10 @@
+import { json } from '@sveltejs/kit';
+import { catalogueExplorer } from '$lib/server/lieux';
+import type { RequestHandler } from './$types';
+
+/** GET /api/lieux → catalogue Explorer (coords arrondies à 0.05°, mode sans score). */
+export const GET: RequestHandler = () => {
+ return json(catalogueExplorer(), {
+ headers: { 'Cache-Control': 'public, max-age=300' }
+ });
+};
diff --git a/app/src/routes/api/round/+server.ts b/app/src/routes/api/round/+server.ts
new file mode 100644
index 0000000..9a74a91
--- /dev/null
+++ b/app/src/routes/api/round/+server.ts
@@ -0,0 +1,18 @@
+import { json } from '@sveltejs/kit';
+import { getRandomLieu, sanitizeRound } from '$lib/server/lieux';
+import type { RequestHandler } from './$types';
+
+/** GET /api/round?region=&categorie=&exclude=id1,id2 → lieu aléatoire SANITISÉ. */
+export const GET: RequestHandler = ({ url }) => {
+ const lieu = getRandomLieu({
+ region: url.searchParams.get('region'),
+ categorie: url.searchParams.get('categorie'),
+ exclude: url.searchParams.get('exclude')
+ });
+ if (!lieu) {
+ return json({ erreur: 'Aucun lieu disponible pour ces critères' }, { status: 404 });
+ }
+ return json(sanitizeRound(lieu), {
+ headers: { 'Cache-Control': 'no-store' }
+ });
+};
diff --git a/app/src/routes/api/wiki/[qid]/+server.ts b/app/src/routes/api/wiki/[qid]/+server.ts
new file mode 100644
index 0000000..7bc6173
--- /dev/null
+++ b/app/src/routes/api/wiki/[qid]/+server.ts
@@ -0,0 +1,81 @@
+import { json } from '@sveltejs/kit';
+import type { WikiSummary } from '$lib/types';
+import type { RequestHandler } from './$types';
+
+const USER_AGENT = 'JWE-OKI/2.0 (https://labola.o-k-i.net/ORGANISATION-KA-INTERNATIONALE/JWE; contact OKI)';
+const TTL_MS = 24 * 60 * 60 * 1000; // 24 h
+
+const cache = new Map();
+
+interface WbEntity {
+ labels?: { fr?: { value: string } };
+ sitelinks?: { frwiki?: { title: string } };
+}
+
+interface WbResponse {
+ entities?: Record;
+}
+
+interface RestSummary {
+ title?: string;
+ extract?: string;
+ content_urls?: { desktop?: { page?: string } };
+ thumbnail?: { source?: string };
+}
+
+/** GET /api/wiki/[qid] → extrait Wikipédia FR normalisé (404 si pas de sitelink fr). */
+export const GET: RequestHandler = async ({ params, fetch }) => {
+ const qid = params.qid;
+ if (!/^Q\d+$/.test(qid)) {
+ return json({ erreur: 'Identifiant Wikidata invalide' }, { status: 400 });
+ }
+
+ const cached = cache.get(qid);
+ if (cached && cached.expires > Date.now()) {
+ return json(cached.summary, { headers: { 'Cache-Control': 'public, max-age=3600' } });
+ }
+
+ const headers = { 'User-Agent': USER_AGENT, Accept: 'application/json' };
+
+ // 1. Wikidata : sitelink frwiki (+ libellé fr en fallback)
+ const wbUrl = `https://www.wikidata.org/w/api.php?action=wbgetentities&ids=${encodeURIComponent(qid)}&props=sitelinks%7Clabels&sitefilter=frwiki&languages=fr&format=json`;
+ let entity: WbEntity | undefined;
+ try {
+ const res = await fetch(wbUrl, { headers });
+ if (!res.ok) throw new Error(`wikidata ${res.status}`);
+ entity = ((await res.json()) as WbResponse).entities?.[qid];
+ } catch {
+ return json({ erreur: 'Wikidata injoignable' }, { status: 502 });
+ }
+
+ const titre = entity?.sitelinks?.frwiki?.title;
+ if (!titre) {
+ // Pas de page Wikipédia FR → le front bascule sur l'écran « Pwen blindé ».
+ return json({ erreur: 'Pas de page Wikipédia en français' }, { status: 404 });
+ }
+
+ // 2. Wikipédia FR : résumé de l'article
+ let data: RestSummary;
+ try {
+ const res = await fetch(
+ `https://fr.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(titre)}`,
+ { headers }
+ );
+ if (res.status === 404) return json({ erreur: 'Article introuvable' }, { status: 404 });
+ if (!res.ok) throw new Error(`wikipedia ${res.status}`);
+ data = (await res.json()) as RestSummary;
+ } catch {
+ return json({ erreur: 'Wikipédia injoignable' }, { status: 502 });
+ }
+
+ const summary: WikiSummary = {
+ qid,
+ title: data.title ?? titre,
+ extract: data.extract ?? '',
+ url: data.content_urls?.desktop?.page ?? `https://fr.wikipedia.org/wiki/${encodeURIComponent(titre)}`,
+ thumbnail: data.thumbnail?.source ?? null
+ };
+ cache.set(qid, { expires: Date.now() + TTL_MS, summary });
+
+ return json(summary, { headers: { 'Cache-Control': 'public, max-age=3600' } });
+};
diff --git a/app/src/routes/explorer/+page.svelte b/app/src/routes/explorer/+page.svelte
new file mode 100644
index 0000000..86b0c73
--- /dev/null
+++ b/app/src/routes/explorer/+page.svelte
@@ -0,0 +1,261 @@
+
+
+
+ {t().app.titre} — {t().explorer.titre}
+
+
+
+
+
+ {#each REGIONS as r (r)}
+
+ {/each}
+
+
+
+ {#if erreur}
+
{t().jeu.erreur}
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+
diff --git a/app/src/routes/jeu/[mode]/+page.svelte b/app/src/routes/jeu/[mode]/+page.svelte
new file mode 100644
index 0000000..cce9b98
--- /dev/null
+++ b/app/src/routes/jeu/[mode]/+page.svelte
@@ -0,0 +1,543 @@
+
+
+
+ {t().app.titre} — {t().modes[mode]}
+
+
+
+ {#if bilan}
+
+
{t().defi.bilan}
+
+ {defi.total}
+ {t().defi.scoreTotal}
+
+
+
+
+
+ {#each defi.manches as m, i (m.place.id)}
+ -
+ {i + 1}. {m.result.nom}
+ {t().regions[m.result.region]}
+ {m.result.score} {t().resultat.points}
+
+ {/each}
+
+
+
+ ● {t().defi.taPosition} —
+ ● {t().defi.positionReelle}
+
+
+ {:else if chargement}
+
+ {:else if erreur}
+
+
{t().jeu.erreur}
+
+
+ {:else if place}
+
+
+ {#if result}
+
+
+
+ {:else}
+
+ {/if}
+
+
+
+ void soumettre(false)}
+ />
+ {#if estDefi && !defiTermine}
+ {t().jeu.round} {Math.min(mancheCourante, 5)} {t().jeu.sur} 5
+ {/if}
+ {#if timerActif && !result}
+
+
+
+ {/if}
+
+
+
+ {#if !result}
+
+
+
+
+
+
+
+ {#if indicesMontres.length > 0}
+
+ {/if}
+ {#if !guess}
+
{t().jeu.poserMarqueur}
+ {/if}
+ {/if}
+ {/if}
+
+
+
diff --git a/app/src/routes/jeu/[mode]/+page.ts b/app/src/routes/jeu/[mode]/+page.ts
new file mode 100644
index 0000000..35625a7
--- /dev/null
+++ b/app/src/routes/jeu/[mode]/+page.ts
@@ -0,0 +1,12 @@
+import { error } from '@sveltejs/kit';
+import type { GameMode } from '$lib/types';
+import type { PageLoad } from './$types';
+
+const MODES: GameMode[] = ['monument', 'lieu', 'defi'];
+
+export const load: PageLoad = ({ params }) => {
+ if (!MODES.includes(params.mode as GameMode)) {
+ error(404, 'Mode inconnu');
+ }
+ return { mode: params.mode as GameMode };
+};
diff --git a/app/src/service-worker.ts b/app/src/service-worker.ts
new file mode 100644
index 0000000..49e829b
--- /dev/null
+++ b/app/src/service-worker.ts
@@ -0,0 +1,64 @@
+///
+///
+///
+///
+
+import { build, files, version } from '$service-worker';
+
+const sw = self as unknown as ServiceWorkerGlobalScope;
+
+const APP_SHELL = `jwe-shell-${version}`; // immuable, recréé à chaque build
+const PHOTOS = 'jwe-photos'; // stale-while-revalidate
+
+const ASSETS = [...build, ...files];
+
+sw.addEventListener('install', (event) => {
+ event.waitUntil(caches.open(APP_SHELL).then((cache) => cache.addAll(ASSETS)));
+});
+
+sw.addEventListener('activate', (event) => {
+ event.waitUntil(
+ caches.keys().then((keys) =>
+ Promise.all(
+ keys
+ .filter((key) => key !== APP_SHELL && key !== PHOTOS)
+ .map((key) => caches.delete(key))
+ )
+ )
+ );
+});
+
+function isPhoto(url: URL): boolean {
+ return (
+ url.hostname === 'upload.wikimedia.org' ||
+ (url.hostname === 'commons.wikimedia.org' && url.pathname.startsWith('/wiki/Special:FilePath'))
+ );
+}
+
+sw.addEventListener('fetch', (event) => {
+ const url = new URL(event.request.url);
+ if (event.request.method !== 'GET') return;
+
+ // App shell : cache d'abord
+ if (ASSETS.includes(url.pathname)) {
+ event.respondWith(caches.match(event.request).then((r) => r ?? fetch(event.request)));
+ return;
+ }
+
+ // Photos Wikimedia : stale-while-revalidate
+ if (isPhoto(url)) {
+ event.respondWith(
+ caches.open(PHOTOS).then(async (cache) => {
+ const cached = await cache.match(event.request);
+ const frais = fetch(event.request)
+ .then((res) => {
+ if (res.ok) void cache.put(event.request, res.clone());
+ return res;
+ })
+ .catch(() => cached as Response);
+ return cached ?? frais;
+ })
+ );
+ return;
+ }
+});
diff --git a/app/static/favicon.ico b/app/static/favicon.ico
new file mode 100644
index 0000000..411db8c
Binary files /dev/null and b/app/static/favicon.ico differ
diff --git a/app/static/icon-maskable.svg b/app/static/icon-maskable.svg
new file mode 100644
index 0000000..0e9f600
--- /dev/null
+++ b/app/static/icon-maskable.svg
@@ -0,0 +1,5 @@
+
diff --git a/app/static/icon.svg b/app/static/icon.svg
new file mode 100644
index 0000000..e695431
--- /dev/null
+++ b/app/static/icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/app/static/manifest.webmanifest b/app/static/manifest.webmanifest
new file mode 100644
index 0000000..22c3196
--- /dev/null
+++ b/app/static/manifest.webmanifest
@@ -0,0 +1,25 @@
+{
+ "name": "JWE — Jeu de géolocalisation OKI",
+ "short_name": "JWE",
+ "description": "Devine la commune à partir d'une photo — Guadeloupe, Martinique, Guyane, La Réunion.",
+ "start_url": "/",
+ "scope": "/",
+ "display": "standalone",
+ "background_color": "#f5efe2",
+ "theme_color": "#0b3d2e",
+ "lang": "fr",
+ "icons": [
+ {
+ "src": "/icon.svg",
+ "sizes": "any",
+ "type": "image/svg+xml",
+ "purpose": "any"
+ },
+ {
+ "src": "/icon-maskable.svg",
+ "sizes": "any",
+ "type": "image/svg+xml",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/app/svelte.config.js b/app/svelte.config.js
new file mode 100644
index 0000000..b4b7de8
--- /dev/null
+++ b/app/svelte.config.js
@@ -0,0 +1,12 @@
+import adapter from '@sveltejs/adapter-node';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ preprocess: vitePreprocess(),
+ kit: {
+ adapter: adapter()
+ }
+};
+
+export default config;
diff --git a/app/tsconfig.json b/app/tsconfig.json
new file mode 100644
index 0000000..a8f10c8
--- /dev/null
+++ b/app/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "moduleResolution": "bundler"
+ }
+}
diff --git a/app/vite.config.ts b/app/vite.config.ts
new file mode 100644
index 0000000..bbf8c7d
--- /dev/null
+++ b/app/vite.config.ts
@@ -0,0 +1,6 @@
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ plugins: [sveltekit()]
+});