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.

ForecastUnitPurpose
Parking forecastParking-lot levelCompare risk of parking failure around arrival time
Park congestion forecastPark levelShow 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

Comments

    Image preview