Lägg till en levande pollenkarta på din webbplats eller app

•10 min läsning
Kartplattor med pollenprognos över Europa och Afrika, lutade i 3D, var och en märkt med sin z/x/y-adress

Atmospores pollenprognos finns nu som kartplattor (tiles). Med ungefär 150 rader kod, inklusive en liten server, kan du lägga ett globalt pollenlager dag för dag på din egen karta: på en webbsida, en instrumentpanel eller i en mobilapp.

Guiden förutsätter ingen erfarenhet av kartor. Den förklarar vad kartplattor är och hur åtkomsten fungerar, och bygger sedan en fungerande karta steg för steg med kod du kan kopiera. Prova den levande versionen först: växla mellan gräs, träd och ogräs, välj en dag och klicka var som helst på land.

MåttligHögMycket hög
Live: den aktuella prognosen, ritad från samma plattor som din app kommer att använda. Klicka på en cell för att se värdet.

Det här får du

Alla landområden på jorden

Ett prognosrutnät som täcker alla kontinenter, i celler på 0,25° (cirka 28 km).

25 arter plus summor

Björk, ek, ambrosia, timotej och 21 till, plus sammanlagda summor för träd, gräs och ogräs. Varje platta innehåller alla.

Dina färger, din karta

Plattorna innehåller siffror, inte bilder. Du väljer färger, genomskinlighet och vilka arter som visas, på vilken bakgrundskarta du vill.

7 dagar framåt, uppdateras dagligen

I dag och de kommande sex dagarna, beräknade på nytt varje dag från den senaste väderprognosen.

Kartplattor på två minuter

En webbkarta är inte en enda stor bild. Den är en mosaik av små rutor som kallas plattor. Helt utzoomad visar en enda platta hela världen. Varje steg du zoomar in delar varje platta i fyra, så att kartan kan visa fler detaljer men ändå bara laddar det som syns på skärmen.

0/0/0
Zoom 0 · 1 platta
1/0/0
1/1/0
1/0/1
1/1/1
Zoom 1 · 4 plattor
2/0/0
2/1/0
2/2/0
2/3/0
2/0/1
2/1/1
2/2/1
2/3/1
2/0/2
2/1/2
2/2/2
2/3/2
2/0/3
2/1/3
2/2/3
2/3/3
Zoom 2 · 16 plattor
Samma pollenkarta på zoomnivå 0, 1 och 2. Varje platta har en adress: zoom / kolumn / rad.

En plattas adress är tre tal: zoomnivån z, kolumnen x (räknad från vänster) och raden y (räknad uppifrån). En platt-URL är en mall med platshållare för talen:

https://pollenapi.com/tiles/v1/…/2026-10-25/{z}/{x}/{y}.mvt?…

Du räknar aldrig ut talen själv. Du ger mallen till ett kartbibliotek som MapLibre, och det fyller i talen för plattorna på skärmen, laddar ner dem och ritar dem. När du panorerar eller zoomar hämtar det de nya.

Våra plattor är vektorplattor (.mvt-filerna). I stället för en färdig bild innehåller varje platta prognosrutnätets celler som former, med pollenvärdena som siffror. Därför kan du färglägga kartan på nytt, byta art eller läsa det exakta värdet under muspekaren, helt i webbläsaren och utan att ladda ner något nytt.

Så fungerar åtkomsten

Kartplattor ingår i betalda planer. Din API-nyckel måste hållas hemlig, så den hamnar aldrig på webbsidan. I stället ber din server om plattlänkarna, och webbläsaren använder dem:

1. Din server

anropar GET /v1/map-tiles med din API-nyckel. Det är ett API-anrop.

2. Din webbsida

får ett litet JSON-svar (TileJSON) från din server, med en platt-URL för varje prognosdag.

3. Atmospores CDN

skickar plattorna direkt till webbläsaren. Nedladdning av plattor är gratis och räknas inte mot din kvot.

Platt-URL:erna är signerade: de fungerar på alla webbplatser i ungefär 6 timmar (fältet expires anger exakt när) och är kopplade till ditt konto, så det finns inget annat att hålla hemligt.

Bygg den: en pollenkarta i tre steg

Du behöver Node.js 18 eller nyare (eller Python 3), en Atmospore API-nyckel på en betald plan och en textredigerare. Skapa en ny mapp för två filer: en liten server och en webbsida.

Vill du hellre klona det? Det kompletta, testade exemplet finns på GitHub: github.com/atmospore/examples

Steg 1: en liten backend

Servern har ett jobb: hämta plattlänkarna med din nyckel och lämna dem vidare till sidan. Den minns svaret i en timme, så även en välbesökt sida gör högst 24 API-anrop per dygn. Välj språk:

server.mjs
// server.mjs: run with  ATMOSPORE_API_KEY=your-key node server.mjs
import http from "node:http";
import { readFile } from "node:fs/promises";

let cached = null;
let fetchedAt = 0;

// Ask Atmospore for fresh tile links at most once an hour.
// Each call counts as one API request; the tiles themselves are free.
async function pollenTiles() {
  if (cached && Date.now() - fetchedAt < 60 * 60 * 1000) return cached;
  const res = await fetch("https://pollenapi.com/v1/map-tiles", {
    headers: { "x-api-key": process.env.ATMOSPORE_API_KEY },
  });
  if (!res.ok) throw new Error(`Atmospore answered ${res.status}`);
  cached = await res.json();
  fetchedAt = Date.now();
  return cached;
}

http.createServer(async (req, res) => {
  try {
    if (req.url === "/pollen-tiles") {
      res.setHeader("Content-Type", "application/json");
      res.end(JSON.stringify(await pollenTiles()));
    } else {
      res.setHeader("Content-Type", "text/html");
      res.end(await readFile("index.html"));
    }
  } catch (err) {
    res.statusCode = 502;
    res.end(String(err));
  }
}).listen(3000, () => console.log("Open http://localhost:3000"));

Har du redan en backend lägger du hellre till /pollen-tiles-routen där. Inget annat i guiden ändras.

Steg 2: kartsidan

Spara det här som index.html i samma mapp. Den laddar MapLibre GL JS, ett gratis kartbibliotek med öppen källkod, och en gratis bakgrundskarta från OpenFreeMap, så du behöver inga andra konton.

index.html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Pollen map</title>
  <link rel="stylesheet" href="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css">
  <script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>
  <style>
    html, body, #map { margin: 0; height: 100%; }
    #controls { position: absolute; top: 10px; left: 10px; z-index: 1;
                background: white; padding: 8px; border-radius: 8px; font: 14px sans-serif; }
  </style>
</head>
<body>
  <div id="controls">
    <select id="species">
      <option value="grass_tot">Grass</option>
      <option value="tree_tot">Trees</option>
      <option value="weed_tot">Weeds</option>
      <option value="birch">Birch</option>
    </select>
    <select id="day"></select>
  </div>
  <div id="map"></div>

  <script>
    // Risk levels in grains/m³: moderate, high and very high start at these values.
    const THRESHOLDS = {
      tree: [15, 90, 1500],
      grass: [5, 20, 200],
      weed: [10, 50, 500],
    };
    const CATEGORY = { grass_tot: "grass", tree_tot: "tree", weed_tot: "weed", birch: "tree" };

    // Colour each cell by the risk level of one species.
    function fillColor(species) {
      const [moderate, high, veryHigh] = THRESHOLDS[CATEGORY[species]];
      return [
        "step", ["coalesce", ["get", species], 0], // missing value = 0
        "rgba(0, 0, 0, 0)",   // low: transparent
        moderate, "#fde047",  // moderate: yellow
        high, "#f97316",      // high: orange
        veryHigh, "#dc2626",  // very high: red
      ];
    }

    const map = new maplibregl.Map({
      container: "map",
      style: "https://tiles.openfreemap.org/styles/positron", // free basemap, no key
      center: [10, 50],
      zoom: 3,
    });

    map.on("load", async () => {
      const pollen = await (await fetch("/pollen-tiles")).json();

      map.addSource("pollen", {
        type: "vector",
        tiles: pollen.tiles,       // today's {z}/{x}/{y} URL
        minzoom: pollen.minzoom,
        maxzoom: pollen.maxzoom,   // MapLibre zooms in past 5 on its own
        attribution: pollen.attribution,
      });

      // Draw the pollen under the place names so labels stay readable.
      const firstLabel = map.getStyle().layers.find((l) => l.type === "symbol");
      map.addLayer({
        id: "pollen",
        type: "fill",
        source: "pollen",
        "source-layer": "pollen",
        paint: {
          "fill-color": fillColor("grass_tot"),
          "fill-opacity": 0.6,
          "fill-antialias": false, // no hairlines between neighbouring cells
        },
      }, firstLabel?.id);

      // Pick a species: only the colours change, no new download.
      const speciesSelect = document.getElementById("species");
      speciesSelect.onchange = () =>
        map.setPaintProperty("pollen", "fill-color", fillColor(speciesSelect.value));

      // Pick a day: swap in that day's tile URL from steps[].
      const daySelect = document.getElementById("day");
      for (const s of pollen.steps) daySelect.add(new Option(s.step, s.step));
      daySelect.onchange = () => {
        const step = pollen.steps.find((s) => s.step === daySelect.value);
        map.getSource("pollen").setTiles(step.tiles);
      };

      // Click a cell to see the number behind the colour.
      map.on("click", "pollen", (e) => {
        const value = e.features[0].properties[speciesSelect.value] ?? 0;
        new maplibregl.Popup().setLngLat(e.lngLat)
          .setHTML(`${value.toFixed(0)} grains/m³`).addTo(map);
      });
    });
  </script>
</body>
</html>

Steg 3: kör den

Starta servern med din API-nyckel:

terminal
ATMOSPORE_API_KEY=your-key node server.mjs

På Windows (PowerShell): $env:ATMOSPORE_API_KEY="your-key"; node server.mjs

Öppna http://localhost:3000 i webbläsaren. Du ska se Europa med dagens prognos för gräspollen. Använd menyerna för att byta art och dag, och klicka på en färgad cell för att se värdet.

Det färdiga exemplet: dagens risk för gräspollen över Europa på en ljus bakgrundskarta
Resultatet: dagens risk för gräspollen, ritad under ortnamnen.

Så fungerar koden

Källan och lagret

map.addSource talar om var datan kommer ifrån: platt-URL-mallen från din server, och zoomintervallet 0–5 som det finns plattor för. Zoomar du förbi 5 fortsätter MapLibre att förstora plattorna från zoom 5, så du kan fortfarande zooma ända ner till gatunivå. map.addLayer talar om hur det ska ritas: "source-layer": "pollen" är namnet på datan i varje platta, och typen "fill" målar varje prognoscell som en färgad ruta. Lagret läggs in före det första etikettlagret, så att ortnamnen ligger kvar läsbara överst.

Färg efter risknivå

Varje cell har ett värde i pollenkorn per kubikmeter luft för varje art. Step-uttrycket i fillColor gör om talet till en färg, med samma risknivåer som vårt API och vår webbplats:

PollenMåttligHögMycket hög
Träd≥ 15≥ 90≥ 1500
Gräs≥ 5≥ 20≥ 200
Ogräs≥ 10≥ 50≥ 500

Värden i korn/m³. Celler under måttlig nivå lämnas genomskinliga.

Har en cell inget pollen av en art utelämnas värdet helt, vilket håller plattorna små. Därför börjar uttrycket med coalesce(…, 0): ett saknat värde räknas som noll. Ändra färgerna, lägg till en blek färg för låga nivåer eller använd egna gränser; allt finns i den här enda funktionen.

Byta art och dag

Varje platta innehåller redan alla arter, så att byta art är bara att byta färgregel med setPaintProperty, och inget laddas ner. Varje prognosdag har sin egen platt-URL i steps-listan som din server skickar vidare; setTiles byter in den och MapLibre hämtar den dagens plattor. Inget av det kräver ett nytt anrop till Atmospores API.

Läsa ett värde

map.on("click", "pollen", …) ger dig cellen under muspekaren, med alla värden i features[0].properties. Samma idé fungerar för verktygstips, eller för en sidopanel som listar alla arter på platsen någon klickade på.

Hålla kartan uppdaterad

Vi publicerar en ny prognos varje dag, och plattlänkarna slutar gälla efter ungefär 6 timmar. Backenden ovan sköter båda: den hämtar nya länkar varje timme, och inom samma timme ger Atmospore identiska URL:er, så webbläsare behåller plattorna de har cachat.

Har folk din sida öppen i flera timmar kan du be backenden om länkarna igen då och då (en gång i timmen räcker gott) och skicka den nya URL:en till setTiles. Varje anrop till /v1/map-tiles räknas som ett API-anrop; nedladdning av plattor räknas aldrig.

Med ett annat kartbibliotek

  • Mapbox GL JS: Koden är densamma: byt maplibregl mot mapboxgl och ange din Mapbox-åtkomsttoken. Våra plattor följer standarden Mapbox Vector Tile.
  • Leaflet: Leaflet ritar bara bildplattor på egen hand. Lägg till tillägget Leaflet.VectorGrid, ge det platt-URL-mallen och stila "pollen"-lagret med en funktion som returnerar en färg för varje cell.
  • OpenLayers: Använd ett VectorTile-lager med en VectorTile-källa, MVT-formatet och platt-URL-mallen, plus en stilfunktion för färgerna.
  • iOS och Android: MapLibre Native och Mapbox Maps SDK:erna läser samma TileJSON och samma stilregler, så färguttrycket från den här guiden fungerar där också.

Bra att veta

Hur detaljerad är datan?

Prognosrutnätet är 0,25° (cirka 28 km vid ekvatorn, smalare mot polerna). Det finns plattor för zoom 0–5; zoomar du längre förstoras plattorna från zoom 5, så på stadsnivå ser du tydliga rutor. Det är prognosens verkliga upplösning.

Varför ser cellerna större ut när jag zoomar ut?

På zoom 0–2 slås närliggande celler ihop till celler på 2°, 1° och 0,5° för att hålla världsvyn lätt. Varje sammanslagen cell får det högsta värdet inuti, så pollentopparna försvinner aldrig när du zoomar ut.

Vilka värden finns i varje platta?

Ett värde per art, med samma namn som i resten av API:et (birch, oak, timothy, ambrosia och så vidare), plus summorna tree_tot, grass_tot och weed_tot. Fältet vector_layers i svaret från /v1/map-tiles listar alla, och API-dokumentationen har den fullständiga referensen.

Varför saknas vissa värden?

En art utan pollen i en cell utelämnas från cellen för att hålla plattorna små, så räkna ett saknat värde som 0. Öppet hav har inga celler, och plattor som bara skulle vara hav finns inte: servern svarar 404, och MapLibre ritar helt enkelt ingenting där.

Vad räknas mot min kvot?

Bara anrop till /v1/map-tiles, ett API-anrop vardera. Nedladdning av plattor är gratis och obegränsad. Med cachningen på en timme från den här guiden blir det högst 24 anrop per dygn, oavsett hur många som använder din karta.

Kan jag använda plattorna på min egen domän och i appar?

Ja, plattorna kan laddas från alla webbplatser och appar. Håll API-nyckeln på din server och visa källhänvisningen: "© Atmospore" följer med i TileJSON, och MapLibre visar den automatiskt.

Sätt pollen på din karta

Kartplattor ingår i alla betalda planer. Skaffa en nyckel, följ de tre stegen ovan, så har du en levande pollenkarta igång på några minuter.