DEV Community

Sam Arora
Sam Arora

Posted on

Notes from building a global extreme weather alerts map

I spent some time building a live map for extreme weather alerts.

The idea was simple: I wanted one map where I could see active warnings from different regions without bouncing between a bunch of official sites.

Here is what it looks like right now:

Weather-map dashboard showing active global weather alerts

Live version:

https://www.latlng.work/dash/extreme-weather

This is not meant to replace official warning pages. It is more of a developer-friendly viewer and a testbed for dealing with messy real-world geospatial data.

The annoying part was not the map

The first version of the map was easy. Add MapLibre, point it at a GeoJSON source, style polygons by severity, done.

The annoying part was everything before that.

Weather alert feeds do not all behave the same way. For this map I had to deal with a mix of sources like:

  • National Weather Service alerts in the US
  • Environment Canada alerts
  • MeteoAlarm in Europe
  • Bureau of Meteorology alerts in Australia
  • Japan Meteorological Agency alerts

Each source has its own idea of what an alert looks like. Some give you good geometry. Some are mostly area descriptions. Some fields are named clearly, some are not. Severity is also not as universal as you would hope.

So the useful work was normalizing the feeds into one shape that the frontend could understand.

The shape I ended up using

I tried to keep the normalized object boring:

type WeatherAlert = {
  id: string;
  source: "nws" | "environment-canada" | "meteoalarm" | "bom" | "jma";
  event: string;
  headline: string;
  severity: "extreme" | "severe" | "moderate" | "minor" | "unknown";
  effectiveAt?: string;
  expiresAt?: string;
  areaDescription?: string;
  geometry: GeoJSON.Geometry;
};
Enter fullscreen mode Exit fullscreen mode

Once everything looks like that, the frontend does not need to know where the alert came from. It can filter, color, and inspect alerts using the same fields.

That is the part I would spend more time on next time. It is tempting to start by polishing the map, but the data contract matters more.

MapLibre layer

The map layer itself is pretty standard:

map.addSource("weather-alerts", {
  type: "geojson",
  data: "/api/weather-alerts.geojson"
});

map.addLayer({
  id: "weather-alert-fill",
  type: "fill",
  source: "weather-alerts",
  paint: {
    "fill-color": [
      "match",
      ["get", "severity"],
      "extreme", "#7f1d1d",
      "severe", "#dc2626",
      "moderate", "#f59e0b",
      "minor", "#facc15",
      "#64748b"
    ],
    "fill-opacity": 0.36
  }
});
Enter fullscreen mode Exit fullscreen mode

There is nothing clever here, which is good. If the server returns clean GeoJSON, the map should be boring.

A few things that were easy to underestimate

Overlapping alerts get noisy fast. A global map looks fine at low zoom, but local areas can become a pile of polygons and markers.

Expired alerts are tricky. Some feeds lag, and it is easy to show stale warnings if you only trust the source update time.

Search matters more than expected. A weather map without quick place search feels awkward, even if the data layer is good.

Source labels matter too. When people click an alert, they want to know where it came from, not just what the normalized severity says.

Current version

Live map:

https://www.latlng.work/dash/extreme-weather

Short resource page:

https://www.latlng.work/resources/extreme-weather-alerts-map

Still a work in progress, but it has already been useful for testing map UI against messy live data instead of a clean demo dataset.

Top comments (0)