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

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.
Det här får du
Ett prognosrutnät som täcker alla kontinenter, i celler på 0,25° (cirka 28 km).
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.
Plattorna innehåller siffror, inte bilder. Du väljer färger, genomskinlighet och vilka arter som visas, på vilken bakgrundskarta du vill.
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.
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:
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:
anropar GET /v1/map-tiles med din API-nyckel. Det är ett API-anrop.
får ett litet JSON-svar (TileJSON) från din server, med en platt-URL för varje prognosdag.
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: 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.
<!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:
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.

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:
| Pollen | Måttlig | Hög | Mycket 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
maplibreglmotmapboxgloch 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.