I wanted to create a mobile-friendly weather service for Finland which has the simplest possible user interface (in Finnish).
Sataako.fi fetches weather radar images from Finnish Meteorological Institute's Open Data APIs and shows them as a MapLibre GL JS image layer after some post-processing. The vector map tiles are served from OpenFreeMap using OpenStreetMap data. The movement of rain clouds is shown as a short animation covering the last 80 minutes.
If you want to embed the map into another website such as a radiator or a home automation system, you can fix the coordinates and zoom level and automatically hide the side panel using query parameters.
Available query parameters:
lng&lat: Set the coordinate the map centers on (WGS84 longitude/latitude).zoom: Set the default zoom levelcollapsed: Set the collapsed status of the side panel. Set astrueto hide the side bar by default.
Example: https://www.sataako.fi?lng=24.94&lat=60.17&zoom=7&collapsed=true
The node.js application runs in Hetzner Cloud with a single vCpu instance. It's deployed there with ansible.
It's responsible for
- maintaing an up-to-date list of radar frames available from FMI
- delivering those frames as post-processed WebP images
To be able to serve a decent amount of concurrent users without exceeding the FMI API request rate, the following steps are done:
- requests to FMI API go through a task queue which limits concurrency
- radar frame list is cached internally for one minute before a re-fetch from FMI
- radar frame images are cached locally for about one hour
FMI provides a WMS compliant HTTP API for fetching a composite radar image covering all of Finland. To be able to use those images on top of the map the following steps are taken:
- request a PNG image in EPSG:3067 projection which is native for FMI's radar images
- decode PNG into raw 32bit pixel data
- change the solid white background (0xffffff) and gray outside-range area (0xf7f7f7) to fully transparent
- detect the boundary between the outside-range area and the radar coverage, and draw a blue edge line
- encode as WebP maintaining transparency
MapLibre GL JS reprojects those images on the fly from EPSG:3067 to the map's Web Mercator projection using precomputed corner coordinates.
- Lauri Kangassalo (lakka) - Lightning implementation
