Forecasting snow and ice as contours, not icons
Forecasting snow and ice as contours, not icons
Most weather apps tell you it's going to snow with a small snowflake icon and a number of inches. That's fine when you only care about your own address. It's almost useless when you care about a route, a region, or a point on a map that isn't where you are.
The meteorology community has always visualized precipitation as isopleths: contour lines and fills that show how a quantity varies across space. Snow totals, rainfall, wind speed, pressure, all of it. The problem is that isopleth maps have traditionally lived on TV weather graphics and government products, not in consumer apps.
DewLogic renders them live, anywhere on Earth, with a user-adjustable time window.
What you see
Open the map, tap "Snowfall Forecast" in the Winter section, drag the hour slider to whatever window you care about (anything from 6 to 72 hours). Within a second or two the map fills in with colored contours showing expected snow accumulation over that window.
levels (cm): [0.5, 1, 2.5, 5, 10, 20, 40]
Areas below 0.5 cm are fully transparent so the contours only appear where meaningful snow is expected. Same interaction for the Ice Forecast toggle, with a purple / magenta scale and smaller thresholds (0.1 to 15 mm), because a tenth of a millimeter of glaze on a road is already a hazard.
The pipeline
This is not a tile-server product. We're generating the isopleths client-side from primary hourly forecast data, which is the only way to make the time window user-configurable.
Open-Meteo hourly (free, no key, global 72-hour data)
-> HourlyForecast { snowfallCm, iceMm }
-> MapDataProvider tile cache
-> getSnowfallForecastPoints(hours:)
-> IsoplethEngine (Rust IDW contour generator)
-> MapLibre GeoJSON fill layer
For each tile we've already cached for the Virtual Station, we already have 72 hours of hourly forecast data in memory. Summing snowfall across a user-selected window is a Map lookup plus an addition, per-tile. We cache those sums keyed by tile ID and hour window so re-rendering the same window on the same tiles is nearly free.
The contour generation itself runs in Rust through flutter_rust_bridge. IDW interpolation across the tile's forecast points, then marching-squares contouring at the preset levels, then back to Dart as GeoJSON. MapLibre renders the GeoJSON as a fill layer on top of the basemap.
Ice: derived from WMO codes
Ice is the subtle one. Open-Meteo doesn't give us a direct "ice accumulation" field, but it does give us hourly weather_code and precipitation mm. WMO codes 56 and 57 are freezing drizzle. 66 and 67 are freezing rain. When those codes are active, we treat the hour's precipitation as ice accumulation at a 1:1 liquid-to-ice ratio (a deliberately conservative assumption).
It is not a frontier-lab ice model. It's a fast, global, good-enough approximation, and it visualizes better than staring at a list of WMO codes in a forecast.
Why not just use the NOAA WMS tile?
NOAA has a perfectly good ice accumulation WMS layer (ndfd.conus.iceaccum). We support it as an overlay. But it has three limitations that drove us to build our own:
1. CONUS only. Doesn't exist outside the US. 2. Fixed time window. NOAA decides the aggregation, not you. 3. Server-side colors. The legend has no gradient control because the tiles arrive pre-colored.
Our isopleth version works globally, respects whatever hour window you choose, and uses a color ramp consistent with our snow visualization. We enforce mutual exclusion with the WMS layer so you don't end up with two ice overlays stacked on top of each other.
The slider behavior is intentional
When you drag the hour slider, the label updates live but the contours do not. Only when you let go (on onChangeEnd) do we trigger a recomputation.
This is a small UX decision with a noticeable effect. Recomputing contours on every drag event is possible but visually noisy. Deferring to release feels more deliberate and keeps the map stable while you're still choosing a value.
If you want to nerd out on the IDW parameters, the marching-squares implementation, or why we landed on these specific accumulation levels, ask.

Replies