64 simulated low-cost PM2.5 sensors sit on a top-down city grid (this is a flat companion to the 3D version — same 64-sensor grid, same field, same QA/QC pipeline, viewed from above instead of as a 3D bar chart). Each reports a noisy raw reading built from a smooth underlying pollution field plus a fixed per-sensor calibration bias, Gaussian noise, occasional dropouts and occasional spike outliers:
raw(i,t) = field(x,z,t) + bias(i) + N(0, σ·noise) [+ spike, w.p. p_out]
Each sensor keeps a rolling history of its last accepted readings. Every new sample is tested with a z-score against that sensor's own recent mean/std:
z = (raw − mean_hist) / std_hist
if |z| > threshold → flagged as outlier, reading rejected
A rejected or missing (dropout) reading is gap-filled by averaging the last N accepted samples (the "gap-fill window" slider). Accepted readings are further corrected by the calibration slider, approximating a periodic co-location check against a reference-grade instrument:
clean(i,t) = (accepted ? raw(i,t) : gapFillAvg(i)) − calibrationOffset
Both the naive raw network mean and the QA/QC-cleaned mean are converted to a US EPA-style Air Quality Index using the standard piecewise-linear PM2.5 breakpoint formula, so you can see directly how much outlier rejection and calibration change the reported air quality.
- Dot color — each sensor's cleaned PM2.5 value mapped to its AQI category.
- Dot size — grows with the cleaned AQI, same mapping the 3D version uses for column height.
- Background tint — the true underlying pollution field the sensors are sampling (never rejected/cleaned — it's the ground truth, not sensor output).
- Red ring — that sensor was flagged as an outlier or missing this tick.
- Trigger Pollution Event — injects a real, spatially-correlated pollution spike (not an artifact) that a well-tuned pipeline should track, not reject.
- Drag the canvas to pan, scroll/pinch to zoom.