radolan

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 26, 2026 License: MIT Imports: 9 Imported by: 0

README

radolan

Go package for parsing DWD RADOLAN and RADVOR-RE weather radar composites and working with the DWD polar stereographic grid.

CI Go Reference

Background

DWD Open Data publishes radar composites in the RADOLAN binary format covering Germany and surrounding areas. This package parses those composites and provides coordinate projection, Z-R conversion, and spatial sampling utilities.

This is a production-hardened fork of jonnyschaefer/radolan (MIT). It has diverged significantly from the original and is not intended to be merged back upstream. Key changes:

  • ErrUnknownUnit tolerance in NewComposites: RV composites (5-min precipitation rate) trigger this error on valid data. The upstream library discards such composites; this fork continues and returns them — the data is usable.
  • Format ≥ 5 projection fix: RADVOR-RE nowcast composites use Format=5 on the DE1200 national grid. The upstream projection returns NaN for this combination. This fork applies the correct WGS84 polar stereographic math.
  • PrecipitationRateAdaptive: Adaptive Z-R conversion that selects between JossWaldvogel70, Aniol80, and MarshallPalmer55 by intensity — closer to DWD operational practice than a single fixed relation.
  • NeighbourhoodSample: Spatial averaging over a grid neighbourhood, returning mean mm/h, max mm/h, and rain coverage fraction — useful for point precipitation estimation without single-pixel noise.
  • ProjectionFunc: Returns a pre-calibrated closure for repeated projection without per-call composite overhead — important for tight rendering loops.
  • Go 1.22+ modernisation: Range-over-int, slices.SortFunc, other idiomatic updates.

Installation

go get github.com/shyrmapp/radolan

Requires Go 1.22 or later.

Usage

Parse a single composite (RADVOR-RE nowcast)
f, err := os.Open("RV2025032615050.bin.gz")
if err != nil { log.Fatal(err) }
defer f.Close()

comp, err := radolan.NewComposite(f)
if err != nil && err != radolan.ErrUnknownUnit {
    log.Fatal(err)
}

// Project a lat/lng to grid coordinates.
x, y := comp.Project(52.52, 13.41) // Berlin
mmh := radolan.PrecipitationRateAdaptive(comp.At(int(x), int(y)))
fmt.Printf("Berlin: %.2f mm/h\n", mmh)
Parse a tar.bz2 archive (RADOLAN RV, 5-min composites)

RV archives contain multiple composites. NewComposites tolerates ErrUnknownUnit and returns all frames sorted by forecast time.

f, err := os.Open("RV_latest.tar.bz2")
if err != nil { log.Fatal(err) }
defer f.Close()

composites, err := radolan.NewComposites(f)
if err != nil { log.Fatal(err) }
fmt.Printf("Loaded %d frames, latest at %s\n",
    len(composites), composites[len(composites)-1].ForecastTime)
Spatial sampling around a point
proj := comp.ProjectionFunc()
x, y := proj(lat, lng)
avgMMH, maxMMH, coverage := comp.NeighbourhoodSample(int(x), int(y), 2)
fmt.Printf("avg %.2f mm/h, max %.2f mm/h, coverage %.0f%%\n",
    avgMMH, maxMMH, coverage*100)
Z-R conversion
// Adaptive (recommended): selects relation by intensity
mmh := radolan.PrecipitationRateAdaptive(dBZ)

// Fixed relation (DWD operational Germany)
mmh = radolan.PrecipitationRate(radolan.Aniol80, dBZ)

// Inverse: mm/h → dBZ
dbz := radolan.Reflectivity(radolan.Aniol80, mmh)

Supported products

Product Grid Description Notes
EX Middle-European Reflectivity
FX National Nowcast reflectivity
FZ National Nowcast reflectivity
PE Local Echo top
PF Local Reflectivity
PG National picture Reflectivity
PR Local Doppler radial velocity
PX Local Reflectivity
PZ Local 3D reflectivity CAPPI
RV DE1200 5-min precipitation rate (mm/h) ErrUnknownUnit expected
RW National Hourly accumulated precipitation
RX National Reflectivity
SF National Daily accumulated precipitation
WN DE1200 Sphere Nowcast reflectivity
WN DE1200 WGS84 Nowcast reflectivity
WX Extended national Reflectivity
YW DE1200 5-min precipitation rate (mm/h)

RADVOR-RE (RV/YW, Format=5, DE1200 national grid) is fully supported including the WGS84 polar stereographic projection.

Coordinate projection

DWD uses a polar stereographic projection with a spherical Earth model for most grids, and a WGS84 ellipsoid model for DE1200 Format≥5 (RADOLAN RV, RADVOR-RE). This package handles both automatically based on the composite header.

// Grid coordinate from lat/lng
x, y := comp.Project(north, east)

// Pre-calibrated closure for tight loops (avoids repeated header lookups)
project := comp.ProjectionFunc()
x, y = project(north, east)

Coordinates are zero-indexed from the top-left corner of the grid. Non-integer values indicate sub-pixel position; use int(x) for direct array access or bilinear interpolation for smoother results.

Data sources

  • RADOLAN RV (5-min observed): https://opendata.dwd.de/weather/radar/composit/rv/
  • RADVOR-RE (5-min nowcast, 2h ahead): https://opendata.dwd.de/weather/radar/radvor/rv/
  • DWD RADOLAN product documentation: DWD RADOLAN/RADVOR System Description

License

MIT. Derived from jonnyschaefer/radolan (also MIT).

Documentation

Overview

Package radolan parses DWD RADOLAN and RADVOR-RE weather radar composites and provides coordinate projection, Z-R conversion, and spatial sampling.

DWD publishes radar composites as open data: https://opendata.dwd.de/weather/radar/

Quick start

f, _ := os.Open("RV_latest.tar.bz2")
composites, _ := radolan.NewComposites(f)
comp := composites[len(composites)-1]
project := comp.ProjectionFunc()
x, y := project(52.52, 13.41) // Berlin
mmh := radolan.PrecipitationRateAdaptive(comp.At(int(x), int(y)))

RADVOR-RE / RV note

RV and RADVOR-RE composites use Format=5 on the DE1200 national grid with a WGS84 polar stereographic projection. They also report an unknown data unit, causing NewComposite and NewComposites to return ErrUnknownUnit. The data is valid — treat ErrUnknownUnit as a warning, not a fatal error.

References

[1] https://www.dwd.de/DE/leistungen/radolan/radolan_info/radolan_radvor_op_komposit_format_pdf.pdf
[2] https://www.dwd.de/DE/leistungen/gds/weiterfuehrende_informationen.zip
[3] https://www.dwd.de/DE/leistungen/radarprodukte/formatbeschreibung_fxdaten.pdf
[4] https://www.dwd.de/DE/leistungen/opendata/help/radar/radar_pg_coordinates_pdf.pdf
[5] https://www.dwd.de/DE/leistungen/radarniederschlag/rn_info/download_niederschlagsbestimmung.pdf
[6] https://www.dwd.de/DE/leistungen/radarprodukte/formatbeschreibung_wndaten.pdf

Index

Constants

View Source
const (
	DE1200Grid grid // resolution: 1100km * 1200km

)

Variables

View Source
var (
	Aniol80 = NewZR(256, 1.42) // DWD operational Germany, described in [5]

	// Doelling98 is the MeteoSwiss operational Z-R relationship.
	Doelling98 = NewZR(316, 1.50)

	// JossWaldvogel70 is a European stratiform/drizzle Z-R relationship (a=300, b=1.50)
	// commonly cited in German meteorological literature. Note: the original Joss &
	// Waldvogel (1970) paper proposed Z=25R^1.71 for drizzle specifically; this variant
	// (a=300, b=1.50) reflects a widely-used regional adaptation.
	JossWaldvogel70 = NewZR(300, 1.50)

	MarshallPalmer55 = NewZR(200, 1.60) // operational use in austria
)

Common Z-R relationships

View Source
var ErrUnknownUnit = newError("NewComposite", "data unit not defined in catalog. data values can be incorrect")

ErrUnknownUnit indicates that the unit of the radar data is not defined in the catalog. The data values can be incorrect due to unit dependent conversions during parsing. Be careful when further processing the composite.

View Source
var NaN = float32(math.NaN())

Functions

func IsNaN

func IsNaN(f float32) (is bool)

func PrecipitationRate

func PrecipitationRate(relation ZR, dBZ float32) (rate float64)

PrecipitationRate returns the estimated precipitation rate in mm/h for the given reflectivity factor and Z-R relationship.

func PrecipitationRateAdaptive

func PrecipitationRateAdaptive(dBZ float32) float64

PrecipitationRateAdaptive returns the estimated precipitation rate (mm/h) using a regime-adaptive Z-R relationship selected by echo intensity:

  • dBZ ≤ 0: sub-noise floor → 0 mm/h
  • dBZ < 20: drizzle/light stratiform → JossWaldvogel70 (a=300, b=1.50)
  • dBZ < 35: operational stratiform → Aniol80 (a=256, b=1.42)
  • dBZ ≥ 35: convective/hail-contaminated → MarshallPalmer55 (a=200, b=1.60)

At convective thresholds (dBZ ≥ 35), Aniol80 is known to overestimate QPE relative to rain-gauge networks in German summer convection due to hail contamination and partial beam blockage (Krämer et al. 2008, HESS). The switch to MarshallPalmer55 applies a more conservative estimate in this regime. JossWaldvogel70 reduces the overestimate Aniol80 produces for drizzle DSDs.

func Reflectivity

func Reflectivity(relation ZR, rate float64) (dBZ float32)

Reflectivity returns the estimated reflectivity factor for the given precipitation rate (mm/h) and Z-R relationship.

Types

type Composite

type Composite struct {
	Product string // composite product label

	CaptureTime  time.Time     // time of source data capture used for forcasting
	ForecastTime time.Time     // data represents conditions predicted for this time
	Interval     time.Duration // time duration until next forecast

	DataUnit Unit

	PlainData [][]float32 // data for parsed plain data element [y][x]
	Px        int         // plain data width
	Py        int         // plain data height

	DataZ [][][]float32 // data for each voxel [z][y][x] (composites use only one z-layer)
	Data  [][]float32   // data for each pixel [y][x] at layer 0 (alias for DataZ[0][x][y])

	Dx int // data width
	Dy int // data height
	Dz int // data layer

	Rx float64 // horizontal resolution in km/px
	Ry float64 // vertical resolution in km/px

	HasProjection bool // coordinate projection available

	Format int // Version Format
	// contains filtered or unexported fields
}

Radolan radar data is provided as single local sweeps or so called composite formats. Each composite is a combined image consisting of mulitiple radar sweeps spread over the composite area. The 2D composite c has a an internal resolution of c.Dx (horizontal) * c.Dy (vertical) records covering a real surface of c.Dx * c.Rx * c.Dy * c.Dy square kilometers. The pixel value at the position (x, y) is represented by c.Data[ y ][ x ] and is stored as raw float value (NaN if the no-data flag is set). Some 3D radar products feature multiple layers in which the voxel at position (x, y, z) is accessible by c.DataZ[ z ][ y ][ x ].

The data value is used differently depending on the product type: (also consult the DataUnit field of the Composite)

Product label            | values represent         | unit
-------------------------+--------------------------+------------------------
 PG, PC, PX*, ...        | cloud reflectivity       | dBZ
 RX, WX, EX, FZ, FX, ... | cloud reflectivity	    | dBZ
 RW, SF,  ...            | aggregated precipitation | mm/interval
 PR*, ...                | doppler radial velocity  | m/s

The cloud reflectivity (in dBZ) can be converted to rainfall rate (in mm/h) via PrecipitationRate().

The cloud reflectivity factor Z is stored in its logarithmic representation dBZ:

dBZ = 10 * log(Z)

Real world geographical coordinates (latitude, longitude) can be projected into the coordinate system of the composite by using the projection method:

// if c.HasProjection
x, y := c.Project(52.51861, 13.40833)	// Berlin (lat, lon)

dbz := c.At(int(x), int(y))					// Raw value is Cloud reflectivity (dBZ)
rat := radolan.PrecipitationRate(radolan.Doelling98, dbz)	// Rainfall rate (mm/h) using Doelling98 as Z-R relationship

fmt.Println("Rainfall in Berlin [mm/h]:", rat)

func NewComposite

func NewComposite(rd io.Reader) (comp *Composite, err error)

NewComposite reads binary data from rd and parses the composite. An error is returned on failure. When ErrUnknownUnit is returned, the data values can be incorrect due to unit dependent conversions during parsing. In this case be careful when further processing the composite.

func NewComposites

func NewComposites(rd io.Reader) ([]*Composite, error)

NewComposites reads .tar.bz2 data from rd and returns the parsed composites sorted by ForecastTime in ascending order. ErrUnknownUnit is treated as a non-fatal warning: the composite is included in the result and the error is not propagated, consistent with the documented semantics of ErrUnknownUnit in NewComposite.

func NewDummy

func NewDummy(product string, format, dx, dy int) (comp *Composite)

NewDummy creates a blank dummy composite with the given product label, format version, and dimensions. It can be used for generic coordinate projection.

func (*Composite) At

func (c *Composite) At(x, y int) float32

At is shorthand for c.Data[y][x] and returns the radar video processor value at the given point. NaN is returned, if no data is available or the requested point is located outside the scanned area.

func (*Composite) AtZ

func (c *Composite) AtZ(x, y, z int) float32

AtZ is shorthand for c.DataZ[z][y][x] and returns the radar video processor value at the given point. NaN is returned, if no data is available or the requested point is located outside the scanned volume.

func (*Composite) NeighbourhoodSample

func (c *Composite) NeighbourhoodSample(cx, cy, radius int) (avgMMH, maxMMH, coverage float64)

NeighbourhoodSample samples a (2*radius+1)×(2*radius+1) pixel neighbourhood centred on (cx, cy) and returns:

  • avgMMH: mean precipitation rate (mm/h) across all in-bounds pixels
  • maxMMH: peak precipitation rate (mm/h) in the neighbourhood
  • coverage: fraction of in-bounds pixels with rate ≥ 0.1 mm/h

Out-of-bounds pixels are excluded from the count. NaN and non-positive dBZ values (no echo) contribute to the count but not to total or max. Uses PrecipitationRateAdaptive for Z-R conversion.

func (*Composite) Project

func (c *Composite) Project(north, east float64) (x, y float64)

Project transforms geographical coordinates (latitude north, longitude east) to the according data indices in the coordinate system of the composite. NaN is returned when no projection is available. Procedures adapted from [1] and [6].

func (*Composite) ProjectionFunc

func (c *Composite) ProjectionFunc() func(north, east float64) (x, y float64)

ProjectionFunc returns a standalone projection function equivalent to c.Project that holds no reference to the composite. Safe to call after the composite is discarded. Returns nil if no projection is available.

type Unit

type Unit int

Unit represents the physical unit of the radar data values in a composite.

const (
	Unit_unknown Unit = iota // unit not defined in catalog; values may be incorrect
	Unit_mm                  // mm per measurement interval (accumulated precipitation)
	Unit_dBZ                 // radar reflectivity factor (logarithmic)
	Unit_km                  // kilometres (e.g. echo top height)
	Unit_mps                 // metres per second (Doppler radial velocity)
)

func (Unit) String

func (u Unit) String() string

String returns a human-readable label for the unit.

type ZR

type ZR struct {
	// contains filtered or unexported fields
}

Z-R relationship

func NewZR

func NewZR(A, B float64) ZR

New Z-R returns a Z-R relationship mathematically expressed as Z = a * R^b

Directories

Path Synopsis
radolan2png is an example program for the radolan package, that converts radolan composite files to .png images.
radolan2png is an example program for the radolan package, that converts radolan composite files to .png images.
vis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL