diff --git a/client/index.html b/client/index.html
index 0b11d9b..cea67c5 100644
--- a/client/index.html
+++ b/client/index.html
@@ -2,6 +2,8 @@
Weather Station
+
+
@@ -23,9 +25,6 @@
-
-
-
diff --git a/client/package-lock.json b/client/package-lock.json
index b23333e..1a856f3 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@ztimson/css-utils": "^1.3.5",
"@ztimson/utils": "^0.29.5",
+ "ol-mapbox-style": "^13.4.1",
"vue": "^3.5.38"
},
"devDependencies": {
@@ -589,6 +590,40 @@
"dev": true,
"license": "MIT"
},
+ "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/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/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/@napi-rs/wasm-runtime": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
@@ -950,7 +985,6 @@
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.3.tgz",
"integrity": "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==",
- "dev": true,
"license": "MIT"
},
"node_modules/@polka/url": {
@@ -1274,7 +1308,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/rbush/-/rbush-4.0.0.tgz",
"integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": {
@@ -1558,7 +1591,6 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@zarrita/storage/-/storage-0.2.0.tgz",
"integrity": "sha512-855ZXqtnds7spnT8vNvD+MXa3QExP1m2GqShe8yt7uZXHnQLgJHgkpVwFjE1B0KDDRO0ki09hmk6OboTaIfPsQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"reference-spec-reader": "^0.2.0",
@@ -1868,7 +1900,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
- "dev": true,
"license": "ISC"
},
"node_modules/electron-to-chromium": {
@@ -1940,7 +1971,6 @@
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
"integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
- "dev": true,
"license": "MIT"
},
"node_modules/fsevents": {
@@ -1972,7 +2002,6 @@
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/geotiff/-/geotiff-3.0.5.tgz",
"integrity": "sha512-OWcL9S9+yDZ6iAlXMt32T1iwUApJM8UiD47xbm6ZP1h33d10fqkPs14EG/ttT5EnefpZSx3G15iDFC5FxUNUwA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@petamoriken/float16": "^3.9.3",
@@ -2141,6 +2170,12 @@
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
}
},
+ "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/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -2165,7 +2200,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz",
"integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==",
- "dev": true,
"license": "Apache-2.0"
},
"node_modules/lightningcss": {
@@ -2461,6 +2495,12 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
+ "node_modules/mapbox-to-css-font": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mapbox-to-css-font/-/mapbox-to-css-font-3.2.0.tgz",
+ "integrity": "sha512-kvsEfzvLik34BiFj+S19bv5d70l9qSdkUzrq99dvZ9d5POaLyB4vJMQmq3BoJ5D6lFG1GYnMM7o7cm5Jh8YEEg==",
+ "license": "BSD-2-Clause"
+ },
"node_modules/memorystream": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
@@ -2470,6 +2510,15 @@
"node": ">= 0.10.0"
}
},
+ "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/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
@@ -2572,7 +2621,6 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/numcodecs/-/numcodecs-0.3.2.tgz",
"integrity": "sha512-6YSPnmZgg0P87jnNhi3s+FVLOcIn3y+1CTIgUulA3IdASzK9fJM87sUFkpyA+be9GibGRaST2wCgkD+6U+fWKw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"fflate": "^0.8.0"
@@ -2603,7 +2651,6 @@
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/ol/-/ol-10.9.0.tgz",
"integrity": "sha512-svbbgVQUmEHaKpLQ8kRySojs59Brvgl2zYIrqG9eQNXGfsbi55rQasZIDpwpQzDL6OlzrUb0H4hQaiX9wDoGmA==",
- "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@types/rbush": "4.0.0",
@@ -2618,6 +2665,22 @@
"url": "https://opencollective.com/openlayers"
}
},
+ "node_modules/ol-mapbox-style": {
+ "version": "13.4.1",
+ "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-13.4.1.tgz",
+ "integrity": "sha512-da4FKZUXoXMzK5ADeRHAXzd3bTeiKg/Y9LBOTB6qNTP62MQm93y6ISrIXzOS9atfGgQMEHJ2pCgP272IMeZCXA==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@maplibre/maplibre-gl-style-spec": "^24.4.1",
+ "mapbox-to-css-font": "^3.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ahocevar"
+ },
+ "peerDependencies": {
+ "ol": "*"
+ }
+ },
"node_modules/open": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz",
@@ -2643,14 +2706,12 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
- "dev": true,
"license": "(MIT AND Zlib)"
},
"node_modules/parse-headers": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz",
"integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==",
- "dev": true,
"license": "MIT"
},
"node_modules/path-browserify": {
@@ -2681,7 +2742,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz",
"integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
- "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"resolve-protobuf-schema": "^2.1.0"
@@ -2776,14 +2836,12 @@
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz",
"integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/quick-lru": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz",
"integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -2796,14 +2854,12 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
- "dev": true,
"license": "ISC"
},
"node_modules/rbush": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/rbush/-/rbush-4.0.1.tgz",
"integrity": "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"quickselect": "^3.0.0"
@@ -2842,14 +2898,12 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/reference-spec-reader/-/reference-spec-reader-0.2.0.tgz",
"integrity": "sha512-q0mfCi5yZSSHXpCyxjgQeaORq3tvDsxDyzaadA/5+AbAUwRyRuuTh0aRQuE/vAOt/qzzxidJ5iDeu1cLHaNBlQ==",
- "dev": true,
"license": "MIT"
},
"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==",
- "dev": true,
"license": "MIT",
"dependencies": {
"protocol-buffers-schema": "^3.3.1"
@@ -3417,6 +3471,12 @@
"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",
@@ -3476,7 +3536,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unzipit/-/unzipit-2.0.0.tgz",
"integrity": "sha512-DVeVIWUZCAQPNzm5sB0hpsG1GygTTdBnzNtYYEpInkttx5evkyqRgZi6rTczoySqp8hO5jHVKzrH0f23X8FZLg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -3779,7 +3838,6 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz",
"integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==",
- "dev": true,
"license": "Apache-2.0"
},
"node_modules/which": {
@@ -3819,7 +3877,6 @@
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.10.2.tgz",
"integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==",
- "dev": true,
"license": "CC0-1.0"
},
"node_modules/yallist": {
@@ -3833,7 +3890,6 @@
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/zarrita/-/zarrita-0.7.3.tgz",
"integrity": "sha512-wChTQ1Ox75INoQCzKAfLWAfB70JJ4KjdW8Sz5x4ZWrFB4Dw+YZdnxHTL0xSdsrB9EmKSeK7fS1Y+I2ibhfGbkw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@zarrita/storage": "^0.2.0",
@@ -3844,7 +3900,6 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.2.0.tgz",
"integrity": "sha512-oyPnDa1X5c13+Y7mA/FDMNJrn4S8UNBe0KCqtDmor40Re7ALrPN6npFwyYVRRh+PqozZQdeg23QtbcamZnG5rA==",
- "dev": true,
"license": "MIT AND BSD-3-Clause"
}
}
diff --git a/client/package.json b/client/package.json
index 78c9c79..8290178 100644
--- a/client/package.json
+++ b/client/package.json
@@ -10,6 +10,7 @@
"dependencies": {
"@ztimson/css-utils": "^1.3.5",
"@ztimson/utils": "^0.29.5",
+ "ol-mapbox-style": "^13.4.1",
"vue": "^3.5.38"
},
"devDependencies": {
@@ -24,9 +25,9 @@
"typescript": "~6.0.0",
"vite": "^8.0.16",
"vite-plugin-vue-devtools": "^8.1.2",
+ "vue": "^3.5.38",
"vue-chartjs": "^5.3.3",
- "vue-tsc": "^3.3.5",
- "vue": "^3.5.38"
+ "vue-tsc": "^3.3.5"
},
"engines": {
"node": "^22.18.0 || >=24.12.0"
diff --git a/client/public/dark-theme.json b/client/public/dark-theme.json
new file mode 100644
index 0000000..14b60f8
--- /dev/null
+++ b/client/public/dark-theme.json
@@ -0,0 +1,370 @@
+{
+ "version": 8,
+ "name": "Smooth Dark",
+ "sources": {
+ "osm": {
+ "type": "vector",
+ "tiles": ["https://tiles.zakscode.com/data/osm/{z}/{x}/{y}.pbf"],
+ "maxzoom": 14
+ }
+ },
+ "glyphs": "http://localhost:8080/fonts/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": { "background-color": "#2b2b2e" }
+ },
+ {
+ "id": "water",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "water",
+ "paint": { "fill-color": "#1e2832" }
+ },
+ {
+ "id": "landcover-grass",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landcover",
+ "filter": ["==", "class", "grass"],
+ "paint": { "fill-color": "#2a3028", "fill-opacity": 0.6 }
+ },
+ {
+ "id": "landcover-wood",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landcover",
+ "filter": ["==", "class", "wood"],
+ "paint": { "fill-color": "#262e24", "fill-opacity": 0.5 }
+ },
+ {
+ "id": "landuse-park",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "park"],
+ "paint": { "fill-color": "#283028", "fill-opacity": 0.7 }
+ },
+ {
+ "id": "landuse-cemetery",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "cemetery"],
+ "paint": { "fill-color": "#2c2e2a", "fill-opacity": 0.7 }
+ },
+ {
+ "id": "landuse-hospital",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "hospital"],
+ "paint": { "fill-color": "#322828", "fill-opacity": 0.6 }
+ },
+ {
+ "id": "landuse-school",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "school"],
+ "paint": { "fill-color": "#302c28", "fill-opacity": 0.6 }
+ },
+ {
+ "id": "building",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "building",
+ "minzoom": 13,
+ "paint": {
+ "fill-color": "#333336",
+ "fill-opacity": { "stops": [[13, 0.3], [16, 0.6]] }
+ }
+ },
+ {
+ "id": "boundary-country",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "boundary",
+ "filter": ["==", "admin_level", 2],
+ "paint": {
+ "line-color": "#4a4448",
+ "line-width": { "stops": [[2, 0.5], [6, 1.5], [10, 2.5]] }
+ }
+ },
+ {
+ "id": "boundary-state",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "boundary",
+ "filter": ["==", "admin_level", 4],
+ "minzoom": 4,
+ "paint": {
+ "line-color": "#3e3a3c",
+ "line-width": { "stops": [[4, 0.3], [8, 1]] },
+ "line-dasharray": [3, 2]
+ }
+ },
+ {
+ "id": "waterway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "waterway",
+ "paint": {
+ "line-color": "#1e2832",
+ "line-width": { "stops": [[8, 0.5], [14, 2]] }
+ }
+ },
+ {
+ "id": "tunnel",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "brunnel", "tunnel"], ["in", "class", "motorway", "trunk", "primary", "secondary", "tertiary"]],
+ "paint": {
+ "line-color": "#2e2e30",
+ "line-width": { "stops": [[8, 0.5], [14, 4], [18, 10]] },
+ "line-dasharray": [3, 3]
+ }
+ },
+ {
+ "id": "road-motorway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "motorway"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#4a4240",
+ "line-width": { "stops": [[5, 0.5], [10, 2], [14, 5], [18, 12]] }
+ }
+ },
+ {
+ "id": "road-trunk",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "trunk"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#443e3c",
+ "line-width": { "stops": [[6, 0.4], [10, 1.5], [14, 4], [18, 10]] }
+ }
+ },
+ {
+ "id": "road-primary",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "primary"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#3e3a38",
+ "line-width": { "stops": [[7, 0.3], [10, 1], [14, 3.5], [18, 9]] }
+ }
+ },
+ {
+ "id": "road-secondary",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "secondary"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#383636",
+ "line-width": { "stops": [[8, 0.3], [12, 1], [14, 3], [18, 7]] }
+ }
+ },
+ {
+ "id": "road-tertiary",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "tertiary"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#353334",
+ "line-width": { "stops": [[9, 0.2], [14, 2], [18, 5]] }
+ }
+ },
+ {
+ "id": "road-minor",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["in", "class", "minor", "service"], ["!=", "brunnel", "tunnel"]],
+ "minzoom": 12,
+ "paint": {
+ "line-color": "#333234",
+ "line-width": { "stops": [[12, 0.3], [16, 2], [18, 4]] }
+ }
+ },
+ {
+ "id": "road-path",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["==", "class", "path"],
+ "minzoom": 14,
+ "paint": {
+ "line-color": "#3a3638",
+ "line-width": 1,
+ "line-dasharray": [4, 2]
+ }
+ },
+ {
+ "id": "rail",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["==", "class", "rail"],
+ "minzoom": 10,
+ "paint": {
+ "line-color": "#3c3a3e",
+ "line-width": { "stops": [[10, 0.5], [14, 1.5]] },
+ "line-dasharray": [5, 5]
+ }
+ },
+ {
+ "id": "bridge",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "brunnel", "bridge"], ["in", "class", "motorway", "trunk", "primary", "secondary", "tertiary"]],
+ "paint": {
+ "line-color": "#403c3a",
+ "line-width": { "stops": [[8, 0.5], [14, 4], [18, 10]] }
+ }
+ },
+ {
+ "id": "aero-runway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "aeroway",
+ "filter": ["==", "class", "runway"],
+ "minzoom": 11,
+ "paint": {
+ "line-color": "#3a383c",
+ "line-width": { "stops": [[11, 2], [16, 12]] }
+ }
+ },
+ {
+ "id": "aero-taxiway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "aeroway",
+ "filter": ["==", "class", "taxiway"],
+ "minzoom": 12,
+ "paint": {
+ "line-color": "#3a383c",
+ "line-width": { "stops": [[12, 0.5], [16, 4]] }
+ }
+ },
+ {
+ "id": "label-water",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "water_name",
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[3, 10], [8, 14]] },
+ "text-letter-spacing": 0.1
+ },
+ "paint": {
+ "text-color": "#4a6068",
+ "text-halo-color": "#2b2b2e",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-road",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "transportation_name",
+ "minzoom": 13,
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[13, 10], [16, 13]] },
+ "symbol-placement": "line",
+ "text-rotation-alignment": "map"
+ },
+ "paint": {
+ "text-color": "#6a6268",
+ "text-halo-color": "#2b2b2e",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-place-city",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "city"],
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[4, 11], [8, 16], [12, 20]] },
+ "text-anchor": "center"
+ },
+ "paint": {
+ "text-color": "#a0969a",
+ "text-halo-color": "#2b2b2e",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "label-place-town",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "town"],
+ "minzoom": 7,
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[7, 10], [12, 15]] },
+ "text-anchor": "center"
+ },
+ "paint": {
+ "text-color": "#8a8088",
+ "text-halo-color": "#2b2b2e",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-place-village",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "village"],
+ "minzoom": 10,
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[10, 9], [14, 13]] },
+ "text-anchor": "center"
+ },
+ "paint": {
+ "text-color": "#787078",
+ "text-halo-color": "#2b2b2e",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-country",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "country"],
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[2, 10], [5, 16]] },
+ "text-transform": "uppercase",
+ "text-letter-spacing": 0.15
+ },
+ "paint": {
+ "text-color": "#908690",
+ "text-halo-color": "#2b2b2e",
+ "text-halo-width": 2
+ }
+ }
+ ]
+}
diff --git a/client/public/light-theme.json b/client/public/light-theme.json
new file mode 100644
index 0000000..b39fbee
--- /dev/null
+++ b/client/public/light-theme.json
@@ -0,0 +1,370 @@
+{
+ "version": 8,
+ "name": "Smooth Light",
+ "sources": {
+ "osm": {
+ "type": "vector",
+ "tiles": ["https://tiles.zakscode.com/data/osm/{z}/{x}/{y}.pbf"],
+ "maxzoom": 14
+ }
+ },
+ "glyphs": "http://localhost:8080/fonts/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": { "background-color": "#f2efe9" }
+ },
+ {
+ "id": "water",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "water",
+ "paint": { "fill-color": "#c8d7d5" }
+ },
+ {
+ "id": "landcover-grass",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landcover",
+ "filter": ["==", "class", "grass"],
+ "paint": { "fill-color": "#e0e8d8", "fill-opacity": 0.6 }
+ },
+ {
+ "id": "landcover-wood",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landcover",
+ "filter": ["==", "class", "wood"],
+ "paint": { "fill-color": "#d4dfc4", "fill-opacity": 0.5 }
+ },
+ {
+ "id": "landuse-park",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "park"],
+ "paint": { "fill-color": "#dce8d0", "fill-opacity": 0.7 }
+ },
+ {
+ "id": "landuse-cemetery",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "cemetery"],
+ "paint": { "fill-color": "#e0e4dd", "fill-opacity": 0.7 }
+ },
+ {
+ "id": "landuse-hospital",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "hospital"],
+ "paint": { "fill-color": "#f0e4e4", "fill-opacity": 0.6 }
+ },
+ {
+ "id": "landuse-school",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "landuse",
+ "filter": ["==", "class", "school"],
+ "paint": { "fill-color": "#eae0d8", "fill-opacity": 0.6 }
+ },
+ {
+ "id": "building",
+ "type": "fill",
+ "source": "osm",
+ "source-layer": "building",
+ "minzoom": 13,
+ "paint": {
+ "fill-color": "#dfdbd7",
+ "fill-opacity": { "stops": [[13, 0.3], [16, 0.6]] }
+ }
+ },
+ {
+ "id": "boundary-country",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "boundary",
+ "filter": ["==", "admin_level", 2],
+ "paint": {
+ "line-color": "#c4b8aa",
+ "line-width": { "stops": [[2, 0.5], [6, 1.5], [10, 2.5]] }
+ }
+ },
+ {
+ "id": "boundary-state",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "boundary",
+ "filter": ["==", "admin_level", 4],
+ "minzoom": 4,
+ "paint": {
+ "line-color": "#d0c8bc",
+ "line-width": { "stops": [[4, 0.3], [8, 1]] },
+ "line-dasharray": [3, 2]
+ }
+ },
+ {
+ "id": "waterway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "waterway",
+ "paint": {
+ "line-color": "#c8d7d5",
+ "line-width": { "stops": [[8, 0.5], [14, 2]] }
+ }
+ },
+ {
+ "id": "tunnel",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "brunnel", "tunnel"], ["in", "class", "motorway", "trunk", "primary", "secondary", "tertiary"]],
+ "paint": {
+ "line-color": "#e8e4e0",
+ "line-width": { "stops": [[8, 0.5], [14, 4], [18, 10]] },
+ "line-dasharray": [3, 3]
+ }
+ },
+ {
+ "id": "road-motorway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "motorway"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#e0d0b8",
+ "line-width": { "stops": [[5, 0.5], [10, 2], [14, 5], [18, 12]] }
+ }
+ },
+ {
+ "id": "road-trunk",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "trunk"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#e4d8c4",
+ "line-width": { "stops": [[6, 0.4], [10, 1.5], [14, 4], [18, 10]] }
+ }
+ },
+ {
+ "id": "road-primary",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "primary"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#e8dfd4",
+ "line-width": { "stops": [[7, 0.3], [10, 1], [14, 3.5], [18, 9]] }
+ }
+ },
+ {
+ "id": "road-secondary",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "secondary"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#ece6dc",
+ "line-width": { "stops": [[8, 0.3], [12, 1], [14, 3], [18, 7]] }
+ }
+ },
+ {
+ "id": "road-tertiary",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "class", "tertiary"], ["!=", "brunnel", "tunnel"]],
+ "paint": {
+ "line-color": "#f0ebe4",
+ "line-width": { "stops": [[9, 0.2], [14, 2], [18, 5]] }
+ }
+ },
+ {
+ "id": "road-minor",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["in", "class", "minor", "service"], ["!=", "brunnel", "tunnel"]],
+ "minzoom": 12,
+ "paint": {
+ "line-color": "#f0ebe4",
+ "line-width": { "stops": [[12, 0.3], [16, 2], [18, 4]] }
+ }
+ },
+ {
+ "id": "road-path",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["==", "class", "path"],
+ "minzoom": 14,
+ "paint": {
+ "line-color": "#d8d0c4",
+ "line-width": 1,
+ "line-dasharray": [4, 2]
+ }
+ },
+ {
+ "id": "rail",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["==", "class", "rail"],
+ "minzoom": 10,
+ "paint": {
+ "line-color": "#d0ccc4",
+ "line-width": { "stops": [[10, 0.5], [14, 1.5]] },
+ "line-dasharray": [5, 5]
+ }
+ },
+ {
+ "id": "bridge",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "transportation",
+ "filter": ["all", ["==", "brunnel", "bridge"], ["in", "class", "motorway", "trunk", "primary", "secondary", "tertiary"]],
+ "paint": {
+ "line-color": "#e0d8cc",
+ "line-width": { "stops": [[8, 0.5], [14, 4], [18, 10]] }
+ }
+ },
+ {
+ "id": "aero-runway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "aeroway",
+ "filter": ["==", "class", "runway"],
+ "minzoom": 11,
+ "paint": {
+ "line-color": "#d8d4cc",
+ "line-width": { "stops": [[11, 2], [16, 12]] }
+ }
+ },
+ {
+ "id": "aero-taxiway",
+ "type": "line",
+ "source": "osm",
+ "source-layer": "aeroway",
+ "filter": ["==", "class", "taxiway"],
+ "minzoom": 12,
+ "paint": {
+ "line-color": "#d8d4cc",
+ "line-width": { "stops": [[12, 0.5], [16, 4]] }
+ }
+ },
+ {
+ "id": "label-water",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "water_name",
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[3, 10], [8, 14]] },
+ "text-letter-spacing": 0.1
+ },
+ "paint": {
+ "text-color": "#8aacaa",
+ "text-halo-color": "#f2efe9",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-road",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "transportation_name",
+ "minzoom": 13,
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[13, 10], [16, 13]] },
+ "symbol-placement": "line",
+ "text-rotation-alignment": "map"
+ },
+ "paint": {
+ "text-color": "#9c9284",
+ "text-halo-color": "#f2efe9",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-place-city",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "city"],
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[4, 11], [8, 16], [12, 20]] },
+ "text-anchor": "center"
+ },
+ "paint": {
+ "text-color": "#6b6156",
+ "text-halo-color": "#f2efe9",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "label-place-town",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "town"],
+ "minzoom": 7,
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[7, 10], [12, 15]] },
+ "text-anchor": "center"
+ },
+ "paint": {
+ "text-color": "#7a7068",
+ "text-halo-color": "#f2efe9",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-place-village",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "village"],
+ "minzoom": 10,
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[10, 9], [14, 13]] },
+ "text-anchor": "center"
+ },
+ "paint": {
+ "text-color": "#8a8078",
+ "text-halo-color": "#f2efe9",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "label-country",
+ "type": "symbol",
+ "source": "osm",
+ "source-layer": "place",
+ "filter": ["==", "class", "country"],
+ "layout": {
+ "text-field": "{name}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": { "stops": [[2, 10], [5, 16]] },
+ "text-transform": "uppercase",
+ "text-letter-spacing": 0.15
+ },
+ "paint": {
+ "text-color": "#8a7e72",
+ "text-halo-color": "#f2efe9",
+ "text-halo-width": 2
+ }
+ }
+ ]
+}
diff --git a/client/src/assets/logo.svg b/client/src/assets/logo.svg
deleted file mode 100644
index 7565660..0000000
--- a/client/src/assets/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/client/src/components/CurrentWeather.vue b/client/src/components/CurrentWeather.vue
index b0465f6..c00c31d 100644
--- a/client/src/components/CurrentWeather.vue
+++ b/client/src/components/CurrentWeather.vue
@@ -108,47 +108,75 @@ onMounted(async () => {
Feels Like
-
{{ ~~(data.heat_index) }}°C
+
+ —
+ {{ ~~(data.heat_index) }}°C
+
Humidity
-
{{ Math.round(data.humidity) }}%
+
+ —
+ {{ Math.round(data.humidity) }}%
+
+
+
+
Rain
+
+
+ —
+ Raining
+ {{data.precipitation_chance}}%
+
Cloud Cover
-
{{ Math.round(data.clouds) }}%
-
-
-
Pressure
-
-
{{ data.pressure_hpa?.toFixed(1) }} hPa
+
+ —
+ {{ Math.round(data.clouds) }}%
+
Sea Level
-
{{ data.pressure_slp?.toFixed(1) }} ({{ getPressureTrend(data.pressure_trend) }})
+
+ —
+ {{ data.pressure_slp?.toFixed(1) }} ({{ getPressureTrend(data.pressure_trend) }})
+
VPD KPA
-
{{ data.vapor_pressure_deficit?.toFixed(2) }} kPa
+
+ —
+ {{ data.vapor_pressure_deficit?.toFixed(2) }} kPa
+
Air Quality
-
{{ data.air_quality }} / 500
+
+ —
+ {{ data.air_quality }} / 500
+
Gas Resistance
-
{{ data.air_quality_ohms }} Ω
+
+ —
+ {{ data.air_quality_ohms }} Ω
+
Visibility
-
{{ data.visibility?.toFixed(1) }} km
+
+ —
+ {{ data.visibility?.toFixed(1) }} km
+
diff --git a/client/src/components/ForecastStrip.vue b/client/src/components/ForecastStrip.vue
index b9c8f94..a2109da 100644
--- a/client/src/components/ForecastStrip.vue
+++ b/client/src/components/ForecastStrip.vue
@@ -73,9 +73,9 @@ onMounted(async () => {
↑{{ ~~(d.temperature_max) + '°' || '—' }}
↓{{ ~~(d.temperature_min) + '°' || '—' }}
- 🌡️ {{ ~~(d.humidity) || 0 }}%
- 🍃 {{ ~~(d.wind_gusts as any) || 0 }} Kph ({{dir(d.wind_dir as any)}})
- 🌧️ {{ d.precipitation_chance || 0 }}% ({{ (d.precipitation as any || 0).toFixed(1) }}mm)
+ 🌡️ {{ ~~(d.humidity) || 0 }}%
+ 🍃 {{ ~~(d.wind_gusts as any) || 0 }} Kph ({{dir(d.wind_dir as any)}})
+ 🌧️ {{ d.precipitation_chance || 0 }}% ({{ (d.precipitation as any || 0).toFixed(1) }}mm)
diff --git a/client/src/components/MapView.vue b/client/src/components/MapView.vue
index 088796f..458cbdb 100644
--- a/client/src/components/MapView.vue
+++ b/client/src/components/MapView.vue
@@ -1,11 +1,10 @@