Switch to vector maps (MapLibre GL JS + PMTiles) and add RainViewer radar
Replace Leaflet/raster tiles with MapLibre GL JS: an online OpenFreeMap style and an offline style built from a local PMTiles archive via pmtiles.js and protomaps-themes-base (no glyphs/labels offline by design, see docs/architecture.adoc). Frontend libraries are vendored locally rather than loaded from a CDN so the app keeps working with no internet access. Also add an animated RainViewer precipitation radar overlay (toggle, play/pause, scrub slider), matching the default time window of the weather-radar-card Home Assistant integration.
This commit is contained in:
1 parent
7329497e95
commit
5afb130c45
19 files changed
+1378
-14
No files matched your search
+84
-1
@@ -40,7 +40,16 @@ regular page.
|
||||
local-only, not exposed on the network).
|
||||
|
||||
| `public/`, `views/`
|
||||
| Frontend assets and Sinatra templates. The map view uses Leaflet.
|
||||
| Frontend assets and Sinatra templates. The map view uses MapLibre GL
|
||||
JS, rendering vector tiles.
|
||||
|
||||
| `public/vendor/`
|
||||
| Third-party frontend libraries, vendored (not loaded from a CDN) so
|
||||
the app also works with no internet access. See
|
||||
`docs/third-party-notices.adoc`.
|
||||
|
||||
| `data/`
|
||||
| Local, gitignored data files, e.g. `offline.pmtiles`.
|
||||
|
||||
| `src-tauri/`
|
||||
| The Tauri (Rust) desktop shell.
|
||||
@@ -74,6 +83,80 @@ instead of relying on the system Ruby. That decision — and the
|
||||
resulting sidecar build step — is not implemented yet and should be
|
||||
revisited before any real distribution build.
|
||||
|
||||
== Offline maps
|
||||
|
||||
Map data is vector, not raster: the app ships
|
||||
https://maplibre.org[MapLibre GL JS] (vendored, see below) and the map
|
||||
view (`public/js/map.js`) offers two styles:
|
||||
|
||||
* *Online* — a full hosted vector style,
|
||||
https://tiles.openfreemap.org/styles/liberty[OpenFreeMap "liberty"],
|
||||
requires internet access.
|
||||
* *Offline* — built at runtime from a local
|
||||
https://github.com/protomaps/PMTiles[PMTiles] archive at
|
||||
`data/offline.pmtiles`, served as-is (see below) at
|
||||
`/tiles/offline.pmtiles`, decoded client-side by
|
||||
https://github.com/protomaps/PMTiles[pmtiles.js] registered as a
|
||||
`pmtiles://` protocol handler, and styled with base layers from
|
||||
https://github.com/protomaps/basemaps[protomaps-themes-base].
|
||||
|
||||
`GET /api/offline-tiles` reports whether `data/offline.pmtiles` is
|
||||
present (`{"available": true|false}`); the frontend uses it once, on
|
||||
load, to default to the offline style when local tile data exists —
|
||||
otherwise it defaults to the online style. Either way, the layer
|
||||
toggle in the top-left of the map lets the user switch manually.
|
||||
|
||||
PMTiles is a single, self-indexed file, which is why this needs no
|
||||
tile-decoding logic on the server: `app.rb`'s `/tiles/offline.pmtiles`
|
||||
route (`send_file`, which Sinatra serves with HTTP range-request
|
||||
support) just returns byte ranges of the file; pmtiles.js in the
|
||||
browser reads the archive's own directory and requests only the bytes
|
||||
it needs for the tiles on screen.
|
||||
|
||||
=== Known limitation: no offline text labels
|
||||
|
||||
The offline style only renders `protomaps-themes-base`'s non-label
|
||||
layers (roads, buildings, land use, water — no place names or street
|
||||
labels). Label rendering in MapLibre needs a "glyphs" server serving
|
||||
font PBF ranges, which would mean vendoring a font glyph set locally
|
||||
too; that has not been done, to keep the initial scope manageable.
|
||||
Revisit this if labelled offline maps turn out to matter in practice.
|
||||
|
||||
No PMTiles file ships with the repository — it is expected to be
|
||||
supplied locally (see `docs/development.adoc`) and stays gitignored
|
||||
because such extracts are typically large binary files.
|
||||
|
||||
== Weather radar overlay
|
||||
|
||||
`public/js/radar.js` adds a "Radar" map control that overlays recent
|
||||
precipitation radar from https://www.rainviewer.com[RainViewer]'s free
|
||||
public API (no API key required). Enabling it:
|
||||
|
||||
. Fetches `https://api.rainviewer.com/public/weather-maps.json`,
|
||||
which lists RainViewer's past radar frames (fixed 10-minute
|
||||
cadence, at most 13 covering the last 120 minutes).
|
||||
. Takes the most recent 7 (last ~60 minutes — the same default as
|
||||
the https://github.com/jpettitt/weather-radar-card[Home Assistant
|
||||
weather-radar-card], for a consistent feel with that setup) and
|
||||
adds one MapLibre raster source/layer per frame, all pre-loaded but
|
||||
only the current frame's `raster-opacity` non-zero.
|
||||
. Animates by toggling that opacity across frames on an interval
|
||||
(play/pause + a scrub slider), rather than swapping tiles in and out
|
||||
— avoids reloading tiles on every step.
|
||||
|
||||
Tile URL: `{host}{frame.path}/256/{z}/{x}/{y}/2/1_0.png` — `2` is
|
||||
RainViewer's "Universal Blue" color scheme, `1` enables their
|
||||
smoothing, `0` disables the snow overlay. `host` and each frame's
|
||||
`path` come from the API response.
|
||||
|
||||
Since `map.setStyle()` (switching Online/Offline) tears down all
|
||||
custom sources and layers, the control listens for `style.load` and
|
||||
re-adds the radar layers if the overlay was on.
|
||||
|
||||
This is a live, online-only overlay — it always needs internet access
|
||||
and is unrelated to the offline PMTiles basemap above. Per RainViewer's
|
||||
terms, the API is free for personal/non-commercial use.
|
||||
|
||||
== Networking
|
||||
|
||||
The Sinatra server only binds to `127.0.0.1`. It is not meant to be
|
||||
|
||||
@@ -18,6 +18,38 @@ via the `rerun` gem instead:
|
||||
bundle exec rerun -- bundle exec puma -C config/puma.rb
|
||||
----
|
||||
|
||||
TIP: Browsers apply heuristic caching to static files even without
|
||||
explicit cache headers, so an edit to `public/` (or `views/`) can
|
||||
appear not to have taken effect on the next reload. Force a real
|
||||
reload (e.g. Ctrl/Cmd+Shift+R) if that happens.
|
||||
|
||||
== Offline map data
|
||||
|
||||
To enable the offline style, place a PMTiles file at
|
||||
`data/offline.pmtiles` (see `docs/architecture.adoc` for how it is
|
||||
served and styled). It is not part of the repository — obtain or
|
||||
build one locally:
|
||||
|
||||
* The simplest source is a pre-built extract from
|
||||
https://app.protomaps.com[protomaps.com's extract builder] — pick a
|
||||
region and download it as `.pmtiles`. Its schema is what
|
||||
`protomaps-themes-base` (the styling library used for the offline
|
||||
style) expects.
|
||||
* To convert some other vector tile source (e.g. an `.mbtiles` file)
|
||||
into PMTiles, use the
|
||||
https://github.com/protomaps/go-pmtiles[go-pmtiles] CLI:
|
||||
`pmtiles convert in.mbtiles data/offline.pmtiles`.
|
||||
|
||||
Once the file is in place, `GET /api/offline-tiles` reports
|
||||
`available: true` and the map defaults to the offline style.
|
||||
|
||||
This is picked up automatically for `cargo tauri dev` (it just runs
|
||||
the Sinatra app from the project root). To ship it inside a *packaged*
|
||||
build, add it to `bundle.resources` in `src-tauri/tauri.conf.json`:
|
||||
`"../data/offline.pmtiles": "data/offline.pmtiles"`. Not added by
|
||||
default, since these files can be large and are picked per
|
||||
deployment.
|
||||
|
||||
== Desktop shell (Tauri)
|
||||
|
||||
=== Prerequisites
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
= Third-party notices
|
||||
:toc:
|
||||
:toclevels: 1
|
||||
|
||||
Frontend libraries vendored under `public/vendor/` instead of loaded
|
||||
from a CDN, so the app keeps working with no internet access. All are
|
||||
BSD-3-Clause licensed.
|
||||
|
||||
[cols="1,1,2"]
|
||||
|===
|
||||
| Library | Version | Source
|
||||
|
||||
| MapLibre GL JS
|
||||
| 6.6.0
|
||||
| https://github.com/maplibre/maplibre-gl-js
|
||||
|
||||
| pmtiles (pmtiles.js)
|
||||
| 4.5.0
|
||||
| https://github.com/protomaps/PMTiles
|
||||
|
||||
| protomaps-themes-base
|
||||
| 4.5.0
|
||||
| https://github.com/protomaps/basemaps
|
||||
|===
|
||||
|
||||
The online map style (not vendored, fetched at runtime when the
|
||||
"Online" layer is selected) is
|
||||
https://tiles.openfreemap.org/styles/liberty[OpenFreeMap "liberty"],
|
||||
built from https://www.openstreetmap.org[OpenStreetMap] data.
|
||||
|
||||
To update a vendored library: download its package (e.g.
|
||||
`npm pack <name>@<version>`), replace the corresponding files under
|
||||
`public/vendor/<name>/`, and update the version in this file.
|
||||
Reference in new issue
Block a user