What it is
ATS Forecast Anywhere turns the whole planet into a weather query surface: click any point on Earth and an animated gold pin drops, a cyan pulse lands, and the right-rail glass panel refills with a complete 7-day Open-Meteo forecast — a "now-ish" hero with WMO condition icons, a clickable 7-day strip, a 48-hour canvas chart (temperature line, precipitation-probability bars, day/night shading, a NOW marker), and a details grid of UV, sunrise and sunset, gusts, and humidity. A type-ahead geocoder, a °F/°C toggle, a 12-city "Surprise me" pod, and session-memory recent-location chips round out the tool.
Everything is keyless: Open-Meteo's forecast and geocoding APIs are free with open CORS, and the basemap is Esri's anonymous streets-night vector style — the one app in this set on a night-city ground rather than dark gray. It boots on Detroit with the panel open, deliberately avoiding geolocation permission friction, and runs with an empty .env.
The engineering story is about correctness in the small: timezone-safe time math for any point on Earth, a single-fetch unit system, and a full WMO weather-code vocabulary.
The experience
The app boots on Detroit, pin already dropped, panel already open, header clock ticking in local Detroit time. From there the planet is one click away:
Like every app in the ATS branded line, the interface follows the house standard: React 19 and Tailwind 4 drive a dark glass shell, every control is a Lucide-iconed glass pod designed for this app, and there is no Esri widget chrome anywhere on screen. The map engine is Esri; the experience is ATS — designed, not assembled.
- Click anywhere. An animated pin drop and landing pulse, then the panel refills; raw clicks are labeled by coordinates (42.33°N 83.05°W) since Open-Meteo has no keyless reverse geocoder — searched and Surprise picks carry real names.
- Type-ahead search. The Open-Meteo geocoder behind a 250 ms debounce with aborted in-flight requests and full keyboard navigation (↑ ↓ Enter Esc).
- Instant units. The °F/°C toggle converts client-side from a single imperial fetch — every number flips instantly, with no refetch and no extra API load.
- Local-time correct everywhere. All forecast times are the location's own timezone, never the browser's; the header clock ticks live from the API's UTC offset. Polar cities render honestly — Longyearbyen in July shows zero night shading.
- The 48-hour chart. A DPR-aware canvas with a temperature line, precipitation bars, day/night shading built from actual sunrise/sunset spans, hover inspection, and a NOW marker; clicking a day card refocuses the window.
- Surprise me. Twelve curated world cities — Reykjavík to Ushuaia to Timbuktu to Longyearbyen — plus recent-location chips (max 5, session memory).
The data — where it comes from
Both services are live, keyless, and CORS-open (verified with Origin-header checks on build day); the basemap is the only Esri dependency.
| Source | What it provides | Refresh | Cost | Attribution |
|---|---|---|---|---|
| Open-Meteo Forecast API (api.open-meteo.com) | 7-day / 168-hour model forecast: temperature, precipitation, wind, humidity, UV, weather codes, sunrise/sunset, with timezone=auto metadata | Live; one fetch per selected place | Free (keyless) | Open-Meteo.com, CC BY 4.0 |
| Open-Meteo Geocoding API (geocoding-api.open-meteo.com) | Type-ahead place search: name, country, admin region, population, coordinates, timezone | Live; debounced per keystroke | Free (keyless) | Open-Meteo.com, CC BY 4.0 |
| Esri streets-night vector basemap | Anonymous legacy v4 night basemap | Esri-hosted | Free (anonymous) | Esri |
How it was assembled
The application is a fully static single-page app — no backend, no database, no server-side rendering. The stack is the shared ATS Labs branded-app baseline:
Module map — the codebase is small and deliberate; each file owns one concern:
Key engineering decisions
- The full WMO 4677 code table, not a highlights reel. src/lib/wmo.ts maps every weather-interpretation code Open-Meteo documents — clear through fog and rime fog, three drizzle and freezing-drizzle grades, rain, freezing rain, snow, snow grains, three shower grades, snow showers, thunderstorms, and thunder with hail — each to a label and a Lucide icon with day/night variants, with a generic-cloud fallback for anything undocumented.
- UTC-as-local time handling. Open-Meteo returns naive local ISO strings ("2026-07-11T06:06", no offset). Parsing those with new Date(...) would silently shift every sunrise and sunset by the viewer's timezone — so every naive string is parsed with Date.UTC into a consistent "UTC-as-local" space, and "now at the location" is computed from Date.now() + utc_offset_seconds × 1000 read back through getUTC* accessors. The dashboard is correct from Detroit to Longyearbyen.
- One fetch, two unit systems. The app fetches once in imperial units and derives °C, km/h, millimeters, and meters at render time — the unit toggle is instant, costs no API call, and can never show mixed-fetch inconsistencies. (Elevation arrives in meters regardless of unit parameters; the converter knows.)
- The geocoder's absent-results quirk. When nothing matches, Open-Meteo omits the results key entirely rather than returning an empty array — the client reads data.results ?? [], and treats aborted in-flight requests as routine rather than as errors.
- Antimeridian sanity. Map clicks on a wrapped world return unwrapped longitudes (e.g. −190° after panning past the date line); coordinates are normalized back into [−180, 180] before the API call, which would otherwise reject them.