Spring til indhold
15+ år Øvet ⏱ 1-2 timer

Byg en vejr-app med rigtige data fra DMI

Hent de nyeste målinger fra DMI’s vejrstationer med JavaScript, og vis temperaturen lige nu og en graf over det seneste døgn i din egen app.

Illustration af en sky, en sol og et computerboard

DMI – Danmarks Meteorologiske Institut – har vejrstationer over hele landet, der måler temperatur, vind og regn hvert 10. minut. Målingerne er frie data: alle må bruge dem, og du behøver hverken at oprette dig eller få en nøgle. I dette projekt henter du dem direkte ind i din egen side med JavaScript.

Sådan gør du

  1. Lav en fil, der hedder vejr.html, i en teksteditor, fx VS Code. Kopiér koden nedenfor ind, og gem filen.
  2. Åbn filen i din browser. Efter et øjeblik ser du temperaturen lige nu og en søjle for hver time i det seneste døgn. Hold musen over en søjle for at se tallet.
  3. Skift station i menuen. Hvor er det varmest lige nu?
  4. Se de rå data: åbn de fem nyeste målinger fra Københavns Lufthavn direkte i browseren. Det er JSON – det samme, som din app får.
  5. Tryk F12 (på Mac: Cmd+Option+I) for at åbne udviklerværktøjerne, og kig under Network, mens siden henter data. Her kan du se forespørgslen til DMI og svaret.

Eksempel (HTML og JavaScript)

<!DOCTYPE html>
<html lang="da">
<head>
  <meta charset="utf-8">
  <title>Min vejr-app</title>
  <style>
    body { font-family: sans-serif; max-width: 600px; margin: 40px auto; padding: 0 16px; }
    #nu { font-size: 3rem; margin: 16px 0 0; }
    #graf { display: flex; align-items: flex-end; gap: 2px; height: 150px; margin-top: 24px; }
    .soejle { flex: 1; background: #2F6BFF; border-radius: 3px 3px 0 0; }
    small { color: #666; }
  </style>
</head>
<body>
  <h1>Vejret lige nu</h1>
  <select id="station">
    <option value="06180">København (Kastrup)</option>
    <option value="06070">Aarhus Lufthavn</option>
    <option value="06120">Odense (H.C. Andersen Airport)</option>
    <option value="06030">Aalborg</option>
    <option value="06080">Esbjerg Lufthavn</option>
    <option value="06193">Bornholm (Hammer Odde Fyr)</option>
  </select>
  <p id="nu">Henter …</p>
  <p id="tid"></p>
  <div id="graf"></div>
  <p><small>Temperaturen det seneste døgn – én søjle pr. time. Data: DMI (CC BY 4.0)</small></p>

  <script>
    const API = 'https://opendataapi.dmi.dk/v2/metObs/collections/observation/items';

    // Hent alle temperaturmålinger fra det seneste døgn (DMI måler hvert 10. minut)
    async function hentMaalinger(station) {
      const url = `${API}?stationId=${station}&parameterId=temp_dry&period=latest-day&limit=300`;
      const svar = await fetch(url);
      if (!svar.ok) {
        throw new Error('DMI svarede med fejlkode ' + svar.status);
      }
      const data = await svar.json();
      const maalinger = data.features
        .map(f => ({ tid: new Date(f.properties.observed), grader: f.properties.value }))
        .filter(m => typeof m.grader === 'number')
        .sort((a, b) => a.tid - b.tid);   // ældste først
      if (maalinger.length === 0) {
        throw new Error('ingen målinger fra stationen lige nu');
      }
      return maalinger;
    }

    function komma(tal) {
      return tal.toLocaleString('da-DK', { minimumFractionDigits: 1, maximumFractionDigits: 1 });
    }

    function tegnGraf(maalinger) {
      const timer = maalinger.filter(m => m.tid.getMinutes() === 0);   // kun hele timer
      const graf = document.getElementById('graf');
      graf.innerHTML = '';
      const grader = timer.map(m => m.grader);
      const bund = Math.min(...grader) - 1;
      const top = Math.max(...grader);
      for (const m of timer) {
        const soejle = document.createElement('div');
        soejle.className = 'soejle';
        soejle.style.height = ((m.grader - bund) / (top - bund)) * 100 + '%';
        soejle.title = `Kl. ${m.tid.getHours()}: ${komma(m.grader)} °C`;
        graf.appendChild(soejle);
      }
    }

    async function opdater() {
      const station = document.getElementById('station').value;
      const nu = document.getElementById('nu');
      const tid = document.getElementById('tid');
      nu.textContent = 'Henter …';
      tid.textContent = '';
      try {
        const maalinger = await hentMaalinger(station);
        const seneste = maalinger[maalinger.length - 1];
        nu.textContent = komma(seneste.grader) + ' °C';
        tid.textContent = 'Målt kl. ' + seneste.tid.toLocaleTimeString('da-DK', { hour: '2-digit', minute: '2-digit' });
        tegnGraf(maalinger);
      } catch (fejl) {
        nu.textContent = 'Kunne ikke hente vejret: ' + fejl.message;
      }
    }

    document.getElementById('station').addEventListener('change', opdater);
    opdater();
  </script>
</body>
</html>

Sådan virker koden

  • fetch(url) beder DMI’s server om data. Det tager lidt tid, så funktionen er async, og await venter på svaret uden at fryse siden.
  • svar.json() laver svaret om til JavaScript-objekter. Hver måling ligger i data.features med et tidspunkt (observed) og en værdi (value).
  • map, filter og sort laver listen om, rydder op og sorterer målingerne efter tid.
  • Grafen er bare div-elementer, hvor højden er sat i procent. Bunden er den laveste temperatur minus én grad, og toppen er den højeste.
  • try og catch fanger fejl – fx hvis du ikke har internet – og viser en besked i stedet for at gå i stå.

💡 DMI’s data må bruges frit under licensen CC BY 4.0. Den kræver, at man skriver, hvor data kommer fra – derfor står der “Data: DMI” nederst i appen. Læs mere om de frie data på dmi.dk/frie-data.

Er JavaScript nyt for dig, så start med JavaScript – gør din hjemmeside levende. Vil du hellere måle vejret selv? Byg en vejrstation med Raspberry Pi.

🧠 Udfordring – prøv selv

Tilføj vinden: hent også parameterId=wind_speed for samme station, og vis vindhastigheden i meter pr. sekund under temperaturen. Kan du også farve søjlerne blå, når det er under 0 grader, og orange, når det er over 20?