Research: rain from radar, and compressed ground textures #308
Labels
No labels
agent
agent:ci
agent:done
agent:failed
agent:needs-input
agent:refined
agent:refining
agent:running
agent:shipped
agent:skip
autonomous
autopilot
driven
local
plan
proposal
qa
qa-gap
research
retro
ship
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jeroen/cartopolis#308
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Research night 2026-09-29 (unattended, read-only). Two areas, both checked against live services or measured tonight. Neither is in
docs/direction.md's frontier, the open issues, or an earlier note (docs/notes/has no radar or texture-compression note).1. Rain where it is actually raining: precipitation radar
What. Today the rain is one number for the whole view.
systems/map/weather.rs:151-164fetches met.no's point forecast for the camera and turns the hourlyprecipitation_amountintoWeatherState::precipitation(weather.rs:34,:146). The globe's clouds come from NOAA GFS at 1° resolution (globe_weather.rs:1-12). A radar composite gives the rain as a measured field: showers that start and stop, cross the city, and can be seen as cells from altitude.Why it fits. Value 1: "Measured beats plausible. Prefer a surveyed fact to a convincing invention." Value 2: "Official sources first." A one-number forecast is the plausible version; the KNMI radar is the measured, official one.
Evidence, checked 2026-09-29 ~00:30 UTC:
api.dataplatform.knmi.nl/open-data/v1/datasets/radar_reflectivity_composites/versions/2.0andradar_forecast/2.0). The endpoint is live: 401 without a key. With the shared anonymous key published on developer.dataplatform.knmi.nl, both returned{"error":"Rate Limit Exceeded"}, so the anonymous key is not usable in production. A free registered key would be needed, and it would belong on the server (atlas), never in the client. The files are HDF5 on a ~1 km national grid, which is why the decoding belongs in atlas: HDF5 in Rust means binding the C library, and the Android and wasm builds cannot take that. Licence: KNMI open data is published under CC BY 4.0. I did not re-verify this tonight because the dataset page is rendered in JavaScript.api.rainviewer.com/public/weather-maps.json): keyless and live. It returned 13 past frames at 10-minute steps, and a z7 tile over Groningen came back200 image/png(17 KB). Zoom 7 is the maximum (their docs; z8+ return a 3 KB empty tile, checked), i.e. roughly 600 m per pixel at 512 px. Terms: free with no key, "not intended for high-volume commercial applications", and attribution ("Weather data by RainViewer" plus a link) is required. It is useful as a fallback outside the Netherlands. It is not the primary source (value 2).What players get. Rain falling where it is actually raining right now, a dry street under a clear patch, and a shower visibly crossing the city. From altitude, rain cells over the country line up with the clouds.
Size. Medium, in two pieces. Atlas: fetch every 5 minutes with a registered key, decode the HDF5, and publish a small georeferenced grid (e.g. a
radar/latestPNG with a timestamp), in the same pattern as its other datasets. Client: sample that grid at the camera forprecipitationinstead of the forecast, keeping met.no as the fallback (value 3). A spatial rain overlay seen from altitude is a later step.Risks. A registered key is a person's action (an account at KNMI). Radar returns clutter such as ground echo and sea clutter. Near the edge of coverage the composite degrades, so it must fall back to the forecast there and not to zero.
First ticket. Atlas: publish the latest KNMI radar composite as a georeferenced grid, and let the client take its rain from it. Acceptance:
--dump-stategainsrain_source(radar/forecast), andprecipitationequals the grid sample at the anchor.rain_source == forecastand the value is unchanged from today.2. Compressed ground textures: most of the ground's GPU memory back
What. Every ground tile is uploaded as uncompressed
Rgba8UnormSrgb(systems/map/map_stream.rs:2085, the onlyTextureFormatin the tile path; there is no BC, ETC2 or ASTC anywhere incrates/cartopolis/src). GPUs sample block-compressed formats natively at 1/4 (BC3/BC7) or 1/8 (BC1) of the memory.Why it fits. Value 5: "The frame budget is not negotiable. Every streaming path is bounded per frame, in bytes as well as items." Resident memory is the documented way this client loses a device (
quality.rs'shandset_guardedtable;docs/notes/upload-bursts.md).Evidence.
quality.rs's handset table: over Groningen,tile_px512 → 256 moved resident memory 991 → 567 MB. So the ground textures at 512 px come to about 565 MB (the 424 MB saved is 3/4 of that). BC3 would hold them in about 140 MB, and BC1 in about 70 MB. That is the same memorytile_px 256buys on handsets today, but without halving the ground's resolution.texpresso(pure Rust), 512² map tile:Against the 48–68 ms a tile already costs to rasterise (
docs/notes/tile-raster-cost.md, cited in CLAUDE.md), the fast encoder adds roughly 30–40 % worker time per tile, and a mip chain adds a third on top. It runs on the worker, not the main thread. The upload per frame also shrinks 4–8×, which helps the per-frame upload ceiling directly.What players get. On desktop and the Steam Deck: sharper ground at the same memory, or the same ground with several hundred MB of headroom for more of the city to stay loaded. On phones, the same headroom without the 256 px downgrade, if the ETC2/ASTC part below works out.
Size. Medium. Behind a capability check (
TEXTURE_COMPRESSION_BCon the adapter;quality::Capabilitiesis where that kind of answer lives), encode each mip level on the tile worker, upload the compressed format, and keep RGBA8 as the fallback.Risks.
globe_ocean.rs), and BC1 has only 1-bit alpha, so the globe needs BC3/BC7 while the flat tiles can take BC1.platform::warm) would need to hold compressed chains, which is a win (smaller) but a format change.First ticket. Measure first:
CARTO_TILE_COMPRESS=bc1|bc3on the flat clipmap tiles, desktop only, gated on the adapter feature. Acceptance: at Groningen 150 m and 1700 m,--dump-state'sgpu_resident_kbdrops by at least 50 % against the same run without it, withsettled == trueand settle time reported beside it (a machine-checkable before/after, value 6). The decision to make it the default comes after the numbers.Recommendation
Area 2 first. It is a measured performance win on hardware already in the loop (the Deck slot, desktop). It needs no account, key or new service, and its first ticket is a pure before/after number. Area 1 is the more visible one, but it is blocked on a person registering a KNMI key, and it is two pieces of work across atlas and the client. Worth doing once that key exists.