Schuwi a2f5c902a3 Add --false-color rendering mode for agentic interpretation
Maps raw cloud cover to six distinct stepped colour bands (clear green
through overcast red) on a dark navy background, with fine gradation
below 30% where conditions matter for astrophotography and two coarse
bands above. Skips OSM base map and alpha blending entirely.

The triangulation now stores raw cover values; scale_cloud_cover() is
applied post-blur only in the default blending mode, keeping its
behaviour identical.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 21:09:47 +01:00
2026-03-03 23:26:05 +01:00

cloud_cover

Cloud cover forecast maps for astrophotography planning, powered by DWD open data.

Example output — prediction for the Berlin/Brandenburg area

What is this?

The German Weather Service (DWD) publishes free, high-resolution numerical weather predictions through its open-data server. The ICON-D2 model covers Germany and surrounding areas with ~2 km grid spacing and produces forecasts up to 48 hours ahead, updated every 3 hours.

This tool downloads the total cloud cover (CLCT) field from ICON-D2 and renders it as a series of map images — one per forecast hour — overlaid on an OpenStreetMap base layer. The result is a quick visual answer to "will the sky be clear tonight?"

Quick start

cargo build --release
./target/release/cloud_cover "2026-03-07T09:00:00Z" 24

Output appears in cache/2026-03-07_09UTC/ — one PNG per forecast hour plus an animated GIF.

Usage

cloud_cover [OPTIONS] <ISO_TIMESTAMP> <HOURS>
Argument Description
ISO_TIMESTAMP Model run time in ISO 8601, e.g. 2026-03-07T09:00:00Z
HOURS Number of forecast steps to render (048)

The timestamp must match an ICON-D2 model run: every 3 hours starting at 00 UTC (00, 03, 06, 09, 12, 15, 18, 21).

Options

Flag Default Description
--center-lat 52.56 Map centre latitude (°N)
--center-lon 13.08 Map centre longitude (°E)
--zoom 10 OSM tile zoom level (higher = more detail, smaller area)
--no-basemap Skip OSM tiles; use a plain green background
--false-color False-colour mode: distinct stepped colour bands, no base map (see below)

False-colour mode

--false-color skips the OSM base layer and maps cloud cover directly to semantically meaningful colour bands, making go/no-go decisions quick to read at a glance — particularly useful for automated or agentic interpretation:

Cover Colour Meaning
< 1% Bright green Clear sky
15% Light green Near-clear
515% Yellow-green Light cloud
1530% Amber Marginal
3060% Orange Cloudy
> 60% Red Overcast

Areas outside the ICON-D2 data region are shown in dark navy. The fine gradation below 30% reflects where cloud cover actually matters for astrophotography; above 30% only two coarse bands are used.

Examples

# Next 12 hours from the 18 UTC run, default viewport (Falkensee / Berlin)
./target/release/cloud_cover "2026-03-06T18:00:00Z" 12

# Wider area centred on central Germany at zoom 8
./target/release/cloud_cover "2026-03-07T06:00:00Z" 24 \
  --center-lat 51.0 --center-lon 10.5 --zoom 8

# Quick run without map tiles
./target/release/cloud_cover "2026-03-07T09:00:00Z" 6 --no-basemap

# False-colour mode for easy agentic interpretation
./target/release/cloud_cover "2026-03-07T09:00:00Z" 6 --false-color

Output

Each run produces files in cache/<date>_<hour>UTC/:

  • clct_000001.pngclct_NNNNNN.png — one 900 × 600 px map per forecast hour, showing cloud cover blended over the OSM base layer with a colour legend
  • clct_animation.gif — animated loop of all frames (generated when there are multiple steps)
  • clat.grib2, clon.grib2, clct_NNN.grib2 — cached raw forecast data; re-running the same timestamp skips the download

Image titles show the forecast time converted to CET/CEST (Europe/Berlin). The first frame is labelled "Conditions at …", subsequent frames "Prediction at … (+NNh)".

In the default mode, cloud cover is rendered as a continuous blend from the base map (clear sky) toward a light blue-white tone (overcast). With --false-color, a stepped colour scale is used instead (see above), with no base map.

How it works

  1. Download — Fetches bzip2-compressed GRIB2 files from the DWD open-data server: two coordinate grids (clat, clon) describing the ~542,000 points of the ICON-D2 icosahedral grid over Germany, plus one clct file per forecast step. Downloads run in parallel and are cached to disk.

  2. Parse — A built-in minimal GRIB2 decoder extracts the data arrays. Only simple packing (data representation template 0) is implemented, which is the format DWD uses for these fields. Grid points flagged absent by the GRIB2 bitmap are set to NaN.

  3. Interpolate — The irregular grid points are projected into pixel space via an orthographic projection, then connected into a Delaunay triangulation. Each output pixel is interpolated via barycentric coordinates within its enclosing triangle, followed by a NaN-aware Gaussian blur to smooth triangle edges.

  4. Render — The base layer comes from Carto Voyager OSM tiles (fetched and cached separately for the basemap and for labels). Cloud cover is blended on top, then map labels are composited above the clouds. City markers, a title bar, and a colour legend complete the frame.

  5. Animate — All frames are colour-quantised to 256-colour palettes (in parallel) and assembled into a looping GIF.

Dependencies

All dependencies are pure Rust or vendored C compiled into the binary — no system shared libraries are required at runtime beyond libc.

Crate Role
reqwest (rustls-tls) HTTP client with pure-Rust TLS
bzip2 Bzip2 decompression (vendors libbzip2)
plotters + plotters-bitmap PNG rendering
font8x8 Built-in 8×8 bitmap font for map labels
clap CLI argument parsing
chrono + chrono-tz Date/time handling and timezone conversion
anyhow Error propagation
spade Delaunay triangulation for grid interpolation
rayon Parallel downloads, rendering, and GIF quantisation
image PNG decoding (loading frames for GIF assembly)
gif GIF encoding

Building

cargo build --release

The binary is at target/release/cloud_cover.

Future ideas

  • Configurable display timezone (currently hardcoded to Europe/Berlin)
  • Configurable city markers / observation sites
  • Configurable data sources
  • Vector map tile support for increased rendering resolution
  • Separation of cache and output files & automatic cache management
  • Automatic newest prediction fetching
Description
No description provided
Readme 223 KiB
Languages
Rust 100%