Skip to content

Weather radar #144

Description

@jdemaeyer

With v2.0.2, a new /radar endpoint landed in Bright Sky. 🎉

This endpoint contains

  • rainfall radar data
  • on a 1200 km (height) x 1100 km (width) grid
  • with each pixel corresponding to 1 km²
  • in 5-minute intervals
  • including a forecast for the next two hours.

This data comes from the DWD's RV radar composite product.

Please feel very welcome to add feedback and suggestions to this issue!

Quickstart

This request will get you radar data near Münster, reaching 200 km to the East/West/North/South, as a two-dimensional grid of integers:

https://api.brightsky.dev/radar?lat=52&lon=7.6&format=plain

API structure

  • The new endpoint is available at https://api.brightsky.dev/radar

  • The response looks roughly like:

    {
      "radar": [
        {
          "timestamp": "2023-05-09T11:05:00+00:00",
          "source": "RADOLAN::RV::2023-05-09T11:05:00+00:00",
          "precipitation_5": "... base64-encoded bytestring or array of integers, see below ..."
        },
        ...
      ],
      "geometry": {
        "type": "Geometry",
        "coordinates": [
          [1.4633,55.86209],
          [3.56699,45.69643],
          [16.58087,45.68461],
          [18.73162,55.84544],
        ]
      }
    }
    
  • Allowed parameters (all of them optional):

    • format: determines how the precipitation data is encoded into the precipitation_5 field (default: compressed):
      • compressed: base64-encoded, zlib-compressed bytestring of 2-byte integers
      • bytes: base64-encoded bytestring of 2-byte integers
      • plain: Nested array of integers
    • bbox: bounding box top, left, bottom, right in pixels, edges are inclusive (default: full 1200x1100 grid)
    • lat / lon / distance: alternative way to set a bounding box, where lat / lon will lay in the center pixel of the returned radar data, which will reach distance meters to each side of this pixel (see example request below). distance can be omitted and defaults to 200000 (i.e. 200 km). The exact x-y-position of the supplied lat/lon will be returned as latlon_position
    • date: ISO8601 timestamp of first record (default: 1 hour before latest measurement)
    • last_date: ISO8601 timestamp of last record (default: 2 hours after date)
    • tz: timezone to be used for timestamps (default: UTC)

Parsing examples

The radar data is quite big (naively unpacking the default 25-frames response into Python integer arrays will eat roughly 125 MB of memory), so use bbox whenever you can.

compressed format

With Python using numpy:

import base64
import zlib

import numpy as np
import requests


resp = requests.get('https://api.brightsky.dev/radar')
raw = resp.json()['radar'][0]['precipitation_5']
raw_bytes = zlib.decompress(base64.b64decode(raw))

data = np.frombuffer(
    raw_bytes,
    dtype='i2',
).reshape(
    # Adjust this to the height/width of your bbox
    (1200, 1100),
)

With Python using the standard library's array:

import array

# [... load raw_bytes as above ...]

data = array.array('H')
data.frombytes(raw_bytes)
data = [
  # Adjust `1200` and `1100` to the height/width of your bbox
  data[row*1100:(row+1)*1100]
  for row in range(1200)
]

Simple plot using matplotlib:

import matplotlib.pyplot as plt

# [... load data as above ...]

plt.imshow(data, vmax=50)
plt.show()

bytes format

Same as for compressed, but add ?format=bytes to the URL and remove the call to zlib.decompress, using just raw_bytes = base64.b64decode(raw) instead.

plain format

This is obviously a lot simpler than the compressed format. It is, however, also a lot slower. Nonetheless, if you have a small-ish bbox the performance difference becomes manageable, so just using the plain format and not having to deal with unpacking logic can be a good option in this case.

With Python:

import requests


resp = requests.get('https://api.brightsky.dev/radar?format=plain')
data = resp.json()['radar'][0]['precipitation_5']

Content

  • The grid is a polar stereographic projection of Germany and the regions bordering it. This is different from the mercator projection used for most consumer-facing maps like OpenStreetMap or Google Maps, and overlaying the radar data onto such a map without conversion will be inaccurate!
  • The DWD data does not cover the whole grid! Many areas near the edges will always be 0.
  • Values represent 0.01 mm / 5 min. I.e., if a pixel has a value of 45, then 0.45 mm of precipitation fell in the corresponding square kilometer in the past five minutes.
  • The four corners of the grid are as follows:
    • Northwest: Latitude 55.86208711, Longitude 1.463301510
    • Northeast: Latitude 55.84543856, Longitude 18.73161645
    • Southeast: Latitude 45.68460578, Longitude 16.58086935
    • Southwest: Latitude 45.69642538, Longitude 3.566994635
  • The latitude/longitude for the center of any grid cell (pixel) can be found in this file.

You can find details and more information in the DWD's RV product info (German only). Below is an example visualization of the rainfall radar data taken from this document, using the correct projection and showing the radar coverage:

image

Notes and Questions for Feedback

  • This is an experimental feature and the interface is very much subject to change. Please regularly check this issue if you're using it.
  • Use a bbox and the default compressed format if possible -- this'll get you the fastest response times by far and reduce load on the server. If you have a small-ish bounding box (e.g. 250 x 250 pixels), using the plain format should be fine.
  • I'm not sure how useful the bytes format is. The idea is to save the hassle of parsing a giant JSON array on the client side, but maybe that's actually not too bad?
  • The polar-stereographic projection that this data uses makes a lot of sense from a data perspective (as each pixel represents the same area) but is rarely used in maps. If you're looking for tiles you can overlay on a map, take a look at the dwd:RV-Produkt layer on the DWD's open GeoServer.
  • The conversion between pixel x/y coordinates and lat/lon coordinates is quite unwieldy. Many users will probably just want to know "what's the rainfall like close to my location", so maybe alternative parameters for setting the bounding box by supplying the lat/lon of its center and a maximum distance could be a simple interface for this?

Resources

Metadata

Metadata

Assignees

No one assigned

    Labels

    experimentalFeatures that are currently available but not finalized

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions