Why Parking Forecasts Include Confidence and Freshness
Why parking forecasts separate current and future values and include confidence and freshness
A forecast such as “12 spaces left in 30 minutes” can be read like an observation. Adding parking forecasts started with distinguishing current and future values on screen.
Source data may update late, parking turnover differs by time of day, and events, weather, or controls can suddenly change the situation. Hangangjari presents forecasts as values for before-arrival judgment and includes confidence and freshness.
I designed baselines, confidence, reason codes, and backtests as one set so the mobile app could read forecasts quickly and the system could evaluate their accuracy later.
Forecasts Prepared Before Requests
Hangangjari has two kinds of forecasts.
| Forecast | Unit | Purpose |
|---|---|---|
| Parking forecast | Parking-lot level | Compare risk of parking failure around arrival time |
| Park congestion forecast | Park level | Show congestion and confidence for outing decisions |
For both forecasts, workers calculate results ahead of time and the API reads the latest prepared value.
Calculating during a request adds computation time to user wait time. Preparing results in workers makes API latency and forecast-generation failures independently observable.
Parking Forecasts: Current and Future Values
A parking forecast uses the current remaining count, recent change, and historical movement for similar time slots to calculate risk a few minutes ahead.
The parking forecast derives direction of change from current values and historical changes. When evidence is weak, it lowers confidence and state to limit how the forecast should be read.
flowchart TB Snapshots["Parking status snapshots"] --> Buckets["5-minute changes"] Buckets --> Features["Forecast features<br/>recent change · time baseline"] Features --> Worker["Forecast worker"] Worker --> Run["Forecast run<br/>version · generated time"] Worker --> Result["Forecast result<br/>horizon · range · risk · confidence"] Result --> Cache["Redis forecast cache"] Result --> API["Forecast API"] Cache --> API API --> Client["iOS forecast screen"]
The response contains forecast values and metadata the mobile UI uses to choose a presentation.
generated_at: when it was calculated.model_version: which logic created it.horizon_minutes: how far ahead it looks.risk_level: a user-readable risk level.failure_probability: an app-level probability value from internal calculation.confidence: whether the evidence is strong enough.reason_codes: clues explaining why risk is considered high.
The app uses these values to distinguish “risky,” “not enough evidence,” and “unavailable” and to explain how to read the forecast number.
Distinguishing a Missing Row from a Quiet Park
Park congestion sources differ in the current values, forecast values, and update times they provide. The resolver must preserve those differences when answering whether a park is likely to be crowded.
Hangangjari uses current park context and official forecast values together. If a forecast row exists near the target time, it takes priority. If not, current context is used as fallback, but confidence must be lowered.
flowchart LR
Context["Current park context"] --> Resolver["Forecast resolver"]
Official["Official congestion forecast"] --> Resolver
Resolver --> Fresh{"Forecast for target time?"}
Fresh -->|Yes| Forecast["Use forecast value<br/>normal confidence"]
Fresh -->|No| Fallback["Use current value fallback<br/>low confidence"]
Forecast --> Response["Park forecast response"]
Fallback --> Response
A missing forecast row is classified as unavailable, not as a quiet park.
Screen policy decides whether to leave the screen empty, substitute the current value, or show a low-confidence value. In every case, the response preserves the missing forecast-row state.
Keeping the First Screen in Sync
Forecasts appear on the dedicated screen and in per-park summaries on the home screen. After the forecast worker creates forecasts, it invalidates related caches and warms frequently read home summaries again.
sequenceDiagram autonumber participant Worker as Forecast worker participant PG as Postgres participant Redis as Redis participant API as First-screen API participant App as iOS app Worker->>PG: Store forecast run and results Worker->>Redis: Clear forecast cache Worker->>Redis: Prewarm first-screen cache App->>API: Request first-screen summary API->>Redis: Read prepared first-screen value Redis-->>API: Return cached response API-->>App: Return decision-ready first-screen summary
Workers finish DB aggregation ahead of time, so the API reads prepared values when users open the app.
The invalidation and warmup steps also define the incident path. A changed forecast run points to its related caches; an old home summary points separately to the forecast worker and cache warmup.
Scoring Forecasts After Release
After release, accumulated data is used to grade forecasts continuously.
Hangangjari has forecast backtest jobs and stores labels and metrics. Changes to calculation logic compare a verifiable baseline with metrics from the previous version.
Each run retains its model version, label, and metrics so improvements remain inspectable after calculation logic changes.
When grading, I looked at:
- Where errors grow by time slot.
- Whether specific parks or parking lots are repeatedly underpredicted.
- Whether events, weather, or controls should lower confidence.
- How source-data incidents affect forecasts.
- Whether p10/p50/p90 ranges explain real variability.
Fields Kept in the Forecast Response
Forecast responses retain rebuildable inputs, calculation times, evidence, and distinct screen states for missing rows and low congestion.
The mobile app reads prepared values, while model versions and backtest metrics explain the forecast’s evidence and limits.
Hangangjari forecast responses carry confidence, model version, and freshness with future values. Those fields let the app distinguish a forecast from a current observation.
Comments
No comments yet. Be the first to leave one.
Pending review