Weather-risk routing for long-haul freight
An internal routing tool for a logistics client. The hard part was not finding routes -- it was sampling weather at the right time. A checkpoint 400 miles out needs tomorrow's forecast, not today's.
- Date
- 2025
- Role
- Individual contributor
- Stack
- React · TypeScript · Django · PostgreSQL · OpenWeatherMap · Mapbox
- Scale
- Internal client tool
- Status
- Shipped 2025
A logistics client needed a route planning tool for long-haul truck drivers. The driver enters an origin, destination, departure time, and load weight. The tool returns three route options ranked by safety.
The obvious approach is to pull current weather for every point along each route. The problem is that a driver departing now will not reach a checkpoint 400 miles out for another six or seven hours. Weather now at that location is the wrong data. You need the forecast for when the truck will actually arrive there.
Getting this wrong does not produce an obvious error. It produces a route that looks safe at departure and is dangerous six hours later. A tool that uses current weather for distant checkpoints is not a safety tool. It is a false confidence tool.
Three route alternatives, not one. Drivers need to compare options. The recommendation is a suggestion, not a mandate. All three routes had to be shown in full with per-checkpoint risk and ETA.
Risk is not just weather. Load weight modifies the thresholds. A truck carrying more than 30,000 lb has a lower wind tolerance than an empty one. The risk engine had to account for this.
Weather sampling had to be configurable: every 10, 25, or 50 miles depending on the route length and how granular the risk picture needed to be.
A weather heatmap along the full trip corridor with a 0 to 48 hour forecast slider. Drivers want to see the weather picture for the whole trip, not just the checkpoints.
The backend had to do the heavy lifting. Route calculation, weather fetching, risk scoring, and recommendation logic all live server-side. The client renders the result.
The time-aware weather problem is the central one. For a given route, the backend calculates the estimated time of arrival at each checkpoint by dividing the distance from the origin by the route's average speed. That ETA is converted to a UTC timestamp. The weather API call for that checkpoint requests the forecast for that timestamp, not the current conditions.
This means weather sampling is not a batch of parallel calls against current conditions. It is a sequence of forecast lookups, each with a different target time. Two checkpoints at the same distance from each other get forecast data from different time slots if the route speed changes between them.
The risk scoring layer applies after the weather data comes back. Wind speed, rainfall rate, and snowfall rate are each classified independently against the threshold table. The worst classification across all three conditions determines the checkpoint risk level. Load weight is applied after the initial wind classification: if wind is already High and the load exceeds 40,000 lb, the checkpoint upgrades to Severe.
A checkpoint 400 miles out needs tomorrow's forecast, not today's. Using current weather for distant checkpoints produces a safety tool that gives false confidence, not actual risk reduction.
The Django backend exposes a single route analysis endpoint. The client sends origin, destination, departure datetime, load weight, and sampling interval. The backend calls a routing API for three alternatives, walks each route geometry at the requested interval, computes arrival time at each checkpoint, fetches forecast data, applies the risk rules, and returns a structured response with per-route and per-checkpoint data.
Route recommendation follows a strict priority order: fewest checkpoints classified as No Travel first, then fewest Severe checkpoints, then fewest High, then lowest average risk score, then shortest total travel time. The recommended route is the one that wins the first tiebreaker it reaches.
The heatmap on the frontend interpolates risk values between checkpoints and renders them as a colored overlay along the route corridor. The forecast slider adjusts the displayed weather data across a 0 to 48 hour window without triggering a new backend call. The full forecast data is fetched once and the slider scrubs across the cached result.
The live version is available at
weather-aware-truck-routing-nine.vercel.app
.
- Route options generated
- 3 per query
- Weather sampling
- time-aware forecast at each checkpoint
- Risk dimensions
- wind · rain · snow + load weight modifier
- Heatmap forecast window
- 0–48 hours, client-side scrubbing
The forecast fetches are the performance bottleneck. Each checkpoint requires a separate API call, and three routes with checkpoints every 25 miles on a 600-mile trip is a lot of requests. The next version should batch requests against a weather grid rather than fetching per-point. A grid fetch covers the full corridor in a few calls; per-point lookups scale poorly with route length.
The risk scoring logic is correct but the load weight rules are applied as post-hoc modifiers rather than being integrated into the threshold table. A cleaner design would take load weight as a parameter to the risk function and compute the effective thresholds upfront, rather than classifying at base thresholds and then upgrading.
The forecast slider was added to the client after the backend API was finalized. It required caching the full 48-hour forecast at fetch time, which was not part of the original response shape. Designing the response for the slider from the start would have been simpler than retrofitting it.