Rain grid¶
dpc_radar.grid ¶
One radar frame as an array, and what to trust in it.
A :class:RainGrid is a DPC frame read from its GeoTIFF: rain in mm (CUM,
SRT1) or mm/h (SRI), NaN where the radar has no data. Two grids exist and
must not be mixed: SRI and SRT1 are 1 km in a Transverse Mercator projection,
the CUM products 0.01° in EPSG:4326. Every method takes lon/lat in degrees and
converts them to the grid's own projection.
Read a place with :meth:RainGrid.sample: the median of the 3x3 pixels around
it, never one pixel. Check a frame against the one before it with
:meth:RainGrid.check before trusting it: on 9-11 October 2026 and again from
21 October to 3 November the DPC published every CUM24 frame at :30 with a
bell of up to 20,584 mm over Abruzzo, while the :00 frames were right.
BBox
module-attribute
¶
BBox = tuple[float, float, float, float]
west, south, east, north, in degrees (EPSG:4326).
RainGrid
dataclass
¶
One frame: data in mm or mm/h (float32, as published), NaN where there is no data.
from_geotiff
classmethod
¶
from_geotiff(raw: bytes, name: str | Product, time: datetime) -> RainGrid
A frame from the bytes of its GeoTIFF; -9999 becomes NaN.
sample ¶
sample(points: list[tuple[float, float]]) -> list[float | None]
The value at each lon/lat point: the median of the valid 3x3 pixels.
The median and not the pixel, so that one wrong pixel never becomes a
flood on one cell. Outside the frame, or where the 3x3 window holds no
data, the answer is None: not measured, which is not the same as
dry.
peak ¶
peak(bbox: BBox, *, threshold: float) -> tuple[float, int]
The highest value inside bbox, and how many pixels reach threshold.
(0.0, 0) outside the frame or where there is no data: Limen's radar
trigger asks "is it raining hard enough, on enough km², here?".
crop ¶
crop(bbox: BBox) -> RainGrid
The part of the frame inside bbox (plus the pixel on each edge).
check ¶
check(previous: RainGrid, *, max_step: float, min_pixels: int = 5) -> FrameCheck
Whether to trust this frame, given the one before it.
Two accumulations of the same length, one period apart, differ by the
rain that entered the window minus the rain that left it: never more
than one period of rain. Where they differ by more than max_step,
one of the two is wrong, and the sign says which: every corruption
seen so far adds rain.
- no pixel jumps:
consistent, the frame as it is; - fewer than
min_pixelsjump:pixels_masked, those pixels set toNaN; - more jump, and this frame is the higher:
rejected, use the previous frame instead; - more jump, and this frame is the lower:
previous_wrong, this frame as it is (it is the previous one that was corrupted); - different grids:
not_comparable, the frame as it is.
max_step is the caller's choice (Limen uses 150 mm for CUM
frames 30 minutes apart). Measured on the benchmark of known cases, this
rule passes 17 of 18 checks and, on 96 pairs of whole frames, rejects
the 44 corrupted ones and no clean one. It cannot see an artefact that
does not move between frames.
discard_inconsistent ¶
discard_inconsistent(previous: RainGrid, *, max_step: float) -> tuple[RainGrid, int]
This frame without the pixels that moved more than max_step.
Limen's rule from October 2026, kept for comparison: it cannot tell
which of the two frames is wrong, so it masks the pixels in both cases,
and it lets through the edges of a corrupted bell (jumps under
max_step). Prefer :meth:check.
FrameCheck
dataclass
¶
The answer of :meth:RainGrid.check.
grid is the frame to use (with masked pixels for pixels_masked),
or None when the frame is rejected. jumps is how many pixels
moved more than max_step.