Initial commit

This commit is contained in:
Schuwi
2026-03-03 23:26:05 +01:00
commit 6c9a20bf59
9 changed files with 2784 additions and 0 deletions

97
README.md Normal file
View File

@@ -0,0 +1,97 @@
# cloud_cover
Downloads total cloud cover (CLCT) predictions from the [DWD open-data
server](https://opendata.dwd.de/weather/nwp/icon-d2/grib/) and renders
one PNG map per forecast hour, centred on the Brandenburg / Berlin area.
This is a Rust port of `../cloud_cover_prediction.py`. The output format
differs: instead of using PyNGL's contour renderer the Rust version does a
direct point-by-point orthographic projection of the ICON-D2 grid onto a
bitmap, which avoids every system-library dependency (no CDO, no PyNIO, no
PyNGL, no libssl, no libbz2 — the binary is fully self-contained).
## Usage
```
cloud_cover <ISO_TIMESTAMP> <HOURS>
```
| Argument | Description |
|---|---|
| `ISO_TIMESTAMP` | Model run time in ISO 8601, e.g. `2026-03-03T18:00:00Z` |
| `HOURS` | Number of forecast steps to render (048) |
The timestamp must correspond to an ICON-D2 model run, which is issued
every 3 hours starting at 00 UTC (00, 03, 06, 09, 12, 15, 18, 21).
### Example
```sh
cargo run --release -- "2026-03-03T18:00:00Z" 12
```
Output PNGs are written to `cache/<date>_<hour>UTC/clct_000001.png`
`clct_NNNNNN.png`. Downloaded GRIB2 files are cached in the same directory
so that re-running the same timestamp skips the network requests.
## How it works
1. **Download**`reqwest` fetches the bzip2-compressed GRIB2 files from
the DWD server. Two coordinate files (`clat`, `clon`) give the latitude
and longitude of every point on the ICON-D2 icosahedral grid (~542 000
points for Germany). One `clct` file per forecast step contains the
total cloud cover percentage at each point.
2. **Decompress**`bzip2` decompresses the files in-memory.
3. **Parse** — A minimal built-in GRIB2 decoder extracts the data values.
Only simple packing (data representation template 0) is supported, which
is what DWD uses. Grid points that are flagged as absent by the section-6
bitmap are set to `NaN` and skipped during rendering.
4. **Project & render** — Each grid point is projected onto the image plane
using an orthographic projection centred on Falkensee (52.56 °N,
13.08 °E). Points are painted as 2 × 2 pixel squares, colour-coded by
cloud cover percentage. City markers and labels are drawn on top, and a
colour-scale legend is shown on the right. `plotters` writes the final
PNG.
## Cloud cover colour scale
| Cloud cover | Colour |
|---|---|
| < 1 % | Land background (clear) |
| 12 % | Very light blue |
| 25 % | Light blue |
| 510 % | Medium blue |
| 1020 % | Blue |
| 2050 % | Dark blue |
| 50100 % | Very dark blue |
## Marked locations
Falkensee · Pausin · Nauen · Hennigsdorf · Ketzin/Brückenkopf · Potsdam ·
Berlin (Mitte)
## Dependencies
All dependencies are pure Rust or vendored C code 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 with built-in bitmap font |
| `clap` | CLI argument parsing |
| `chrono` | Date/time handling |
| `anyhow` | Error propagation |
## Building
```sh
cd cloud_cover_rs
cargo build --release
```
The compiled binary ends up at `target/release/cloud_cover`.