Introduction

The data from this WebAPI is meant to be used to create weather maps in a web application. They can be freely used for other purposes as long as you follow the terms of service, but the documentation here will center around web maps.

The data in our tiles need to be processed before it can be presented on a map. You need to make some decisions on how to approach this:

First, are you going to make heatmap style images or are you going to have animations, like wind particle streamlines?

The last option requires a lot more work and in a web application you will need to do it using WebGL in your frontend code. A blog from a developer at MapBox describes how to do this in general, but it will have to be adjusted to our data.

The rest of this documentation will focus on the simpler use-case of static heatmap visualisations.

There are two main tasks in processing our data for presentation on a web map:

  • Parse the metadata of datasets to get access to the tileset for the timestep(s) you are interested in.
  • Setup a map layer in your map library to and style this layer using the data in our tileset.

Before proceeding please look through our glossary for definition of the terms used.

Also, read our description of the datasets

Parse metadata

No matter how you visualise the data, you need some logic to continuously update your client with the latest version of the dataset. You most likely also need some logic to visualise the different timesteps each version of the dataset has.

In order to do that, you must regularly (varies by the update frequency of the dataset) check /api/<dataset>/available.json. Then parse the metadata in that response, like:

{
  "bounds": [
    -7.981,
    53.152,
    40.611,
    74
  ],
  "minzoom": 0,
  "maxzoom": 6,
  "scheme": "xyz",
  "name": "precipitation-nowcast",
  "times": [
    {
      "time": "2025-02-03T11:00:00Z",
      "tiles": {
        "png": "https://beta.yr-maps.met.no/api/precipitation-nowcast/be59a29b-949a-469e-9fdd-b1e1fb3d66e4/202502031100/tiles/{z}/{x}/{y}.png",
        "webp": "https://beta.yr-maps.met.no/api/precipitation-nowcast/be59a29b-949a-469e-9fdd-b1e1fb3d66e4/202502031100/tiles/{z}/{x}/{y}.webp"
      }
    },
    {
      "time": "2025-02-03T11:05:00Z",
      "tiles": {
        "png": "https://beta.yr-maps.met.no/api/precipitation-nowcast/be59a29b-949a-469e-9fdd-b1e1fb3d66e4/202502031105/tiles/{z}/{x}/{y}.png",
        "webp": "https://beta.yr-maps.met.no/api/precipitation-nowcast/be59a29b-949a-469e-9fdd-b1e1fb3d66e4/202502031105/tiles/{z}/{x}/{y}.webp"
      }
    },
    [..]
}

You would then use the relevant tileset, e.g https://beta.yr-maps.met.no/api/precipitation-nowcast/be59a29b-949a-469e-9fdd-b1e1fb3d66e4/202502031100/tiles/{z}/{x}/{y}.webp to configure a layer in your mapping library.

NOTE: Use the webp tiles whenever possible. It will be faster to access, and less data to download.

Setup a map layer

Configure your map client with a map layer based on our data.

There are two main strategies for doing this:

  • Backend code.
  • Frontend code using WebGL.

Some of the concepts mentioned here are further clarified in our glossary.

We will be using the react version of Maplibre, but there are many other alternative mapping libraries which might suite your needs better.

Backend

This can be done in multiple ways, but we will document an approach where we use a proxy web service, so you can configure your map client with a raster tile layer by sending the requests to the proxy service.

The idea is to build a proxy that receives the tiling requests from your map client. The proxy then forwards the request to our service to get the data.

Your proxy will then:

  1. Open up the returned tile image.
  2. Loop through each pixel.
  3. Create a new image with correctly styled pixel values.
  4. Return the styled tile image back to the map client.

See our example code for an illustration of how this can be done.

NOTE: Please add caching to your proxy service, to reduce the amount of traffic to our service.

In your frontend code, you would then add a layer to the maplibre client using the url to your proxy, e.g:

<Source
    id="unique_id"
    type="raster"
    tiles={["https://example.com/tiling-proxy/api/precipitation-nowcast/be59a29b-949a-469e-9fdd-b1e1fb3d66e4/202502031100/tiles/{z}/{x}/{y}.webp"]}
    tileSize={256}
    scheme="xyz"
>
    <Layer
        type="raster"
        paint={{ "raster-opacity": 0.6 }}
    />
</Source>

Frontend

Before proceeding you need to know the basics of WebGL. WebGL2 will of course also work just as well, but we will refer to examples in WebGL1.

This approach uses WebGL to set colors for each pixel, based on the values in the tiles, and relies on using the custom layer interface to MapLibre.

  1. Create input texture from relevant tiles.
  2. Write WebGL code to set colors.
  3. Add your layer to the MapLibre client.

NRK has provided some example code, based on how things are done on yr.no to help you along, and we will refer to that.

Create input texture from relevant tiles

Use helper functions from MapLibre to figure out the webmercator bounds of your current map canvas, and which tiles are needed to cover that canvas.

Create a new image from the selected tiles. See an example of how to do this in Typescript.

This new image will then be used as input texture to your WebGL code.

Write WebGL code to set colors

Developers at NRK have provided in-depth examples of how to write WebGL code using our data. You will find documentation for temperature and wind. Please read this documentation before proceeding.

Then:

  • Create your own class that implements the Maplibre custom layer interface.
  • Setup the configuration and data to your shaders, including adding the input texture data from the previous step.
  • Write your WebGL code, based on your chosen color schemes.

Add your layer to the MapLibre client

Add you custom layer to the map. For example using a react component:

import { useEffect } from 'react';
import { useMap } from 'react-map-gl/maplibre';
import ExampleCustomLayer from '../lib/ExampleCustomLayer';

export default function ExampleCustomLayerComponent() {
    const { current: current } = useMap();

    useEffect(() => {
        if (!current) {
            return;
        }

        current.on('load', () => {
            let currentMap = current.getMap()
            let customLayer = new ExampleCustomLayer("example", currentMap)
            current.getMap().addLayer(customLayer);

            return () => current.getMap().remove();
        });
    }, [current]);

    return (
        <></>
    )
}