airq 1.3.0

CLI air quality checker: 14-signal comfort, event detection, pollution fronts, source attribution, sensor/model merge
Documentation

airq

Crates.io airq-core License: MIT

Check air quality from your terminal. Any city in the world, no API key needed. Detects pollution fronts, generates visual reports with maps and heatmaps.

Hamburg Air Quality Report

Pollution front analysis for Hamburg — 522 sensors, cross-correlation tracking, heatmap overlay. Generated with airq report --city hamburg --radius 150 --pdf

Merges two data sources with dynamic weighting by divergence:

  • Sensor.Community — citizen science sensors (15,000+ real sensors worldwide) — primary, ground truth
  • Open-Meteo — CAMS atmospheric model (PM2.5, PM10, CO, NO2, O3, SO2, UV) — fallback

When sources diverge (e.g. model says 130, sensors say 7), sensors win. Model weight drops to ~0% at high divergence.

Install

brew install fortunto2/tap/airq     # Homebrew (macOS & Linux)
cargo install airq                  # crates.io (any platform)

Or download prebuilt binaries from GitHub Releases.

Quick start

airq --city gazipasa
Resolved city: Gazipaşa, Türkiye
Sources: Open-Meteo (model) + Sensor.Community (1 sensors, 5km median)
--------------------------------------------------
PM2.5  9.6 avg  (12.7 model, 6.5 sensors) μg/m³
PM10   14.7 avg  (16.2 model, 13.1 sensors) μg/m³
CO    159.0 μg/m³
NO2   1.9 μg/m³
O3    73.0 μg/m³
SO2   1.1 μg/m³
UV Index: 0 ☀️ (Low)
Humidity: 59% | Pressure: 1009 hPa (normal)
Wind:   5.2 km/h ↙ NE (gusts 8)
Comfort: 90/100 🟢 Excellent
--------------------------------------------------
🟢 US AQI: 40 | EU AQI: 29 — Good

Commands

Any city in the world

airq --city tokyo
airq --city "new york"
airq --city berlin
airq --city анталья

By coordinates

airq --lat 55.75 --lon 37.62

Comfort index

airq comfort --city berlin
Berlin, Germany — Comfort Index: 67/100 — Good

  Air Quality   80/100  ████████░░  AQI 38 (Good)
  Temperature   21/100  ██░░░░░░░░  2°C
  Wind          80/100  ████████░░  17 km/h SW
  UV           100/100  ██████████  0.6 (Low)
  Pressure      84/100  ████████░░  1005 hPa
  Humidity      75/100  ███████░░░  74%

Extended data (pollen, earthquakes, geomagnetic)

airq --city gazipasa --full

Shows additional data only when significant:

  • Pollen levels (grass, birch, alder, ragweed)
  • Nearby earthquakes (M3+, 200km, 7 days)
  • Geomagnetic Kp index (when unsettled/storm)

History (sparkline)

airq history --city istanbul --days 5
Istanbul, Türkiye — last 5 days
2026-03-09: ██░░░ 10.9 µg/m³ (AQI 44 🟢)
2026-03-10: ███░░ 20.7 µg/m³ (AQI 57 🟡)
2026-03-11: ███░░ 21.5 µg/m³ (AQI 68 🟡)
2026-03-12: ███░░ 17.3 µg/m³ (AQI 68 🟡)
2026-03-13: ███░░ 20.3 µg/m³ (AQI 64 🟡)

Top cities by AQI

airq top --country turkey
airq top --country russia --count 10
airq top --country usa --json
# City              AQI  PM2.5
1 Delhi             210  🟣 58.9
2 Mumbai            87   🟡 29.3
3 Bangalore         62   🟡 17.5
4 Chennai           54   🟡 13.7
5 Kolkata           43   🟢 10.2

Any country in the world — 10,000+ cities built-in. Use airq top --country x --list to see all.

Compare providers

airq compare --city berlin                       # area median (all sensors in 5km)
airq compare --city gazipasa --sensor-id 77955   # specific sensor
Berlin, Germany — Provider Comparison
Sensor.Community: 143 sensors, 5km radius
┌──────────┬───────────┬─────────────────┬─────────┐
│ Metric   │ Open-Meteo│     Area Median │ Average │
├──────────┼───────────┼─────────────────┼─────────┤
│ PM2.5    │       4.2 │             6.8 │     5.5 │
│ PM10     │       5.3 │            11.1 │     8.2 │
│ US AQI   │        31 │       28 (calc) │      29 │
└──────────┴───────────┴─────────────────┴─────────┘

Single provider

airq --city berlin --provider open-meteo          # model only
airq --city berlin --provider sensor-community --sensor-id 72203  # sensor only

JSON output

All commands support --json:

airq --city berlin --json
airq history --city tokyo --days 7 --json
airq top --country usa --json
airq compare --city berlin --json

Pollution front detection

Detect pollution fronts moving between cities using cross-correlation analysis:

airq front --city gazipasa --radius 150 --days 3
Analyzing pollution fronts around Gazipaşa, Türkiye...
Nearby cities (150km radius):
  Alanya (42km NW), Manavgat (97km NW), Karaman (129km NE)...

Current wind: 5.2 km/h ↙ NE

Spike detection (last 72h):
  🟡 Alanya: 16.8 µg/m³ (+2.6) 2026-03-15 20:00
  🟢 Manavgat: 12.0 µg/m³ (+2.0) 2026-03-15 20:00

Pollution fronts detected:
  ↘ Manavgat → Gazipaşa | 5 km/h SE | lag 20h | corr 87%
  ⚠ Manavgat → Gazipaşa front detected (lag 20h, 5 km/h)

Uses Z-score spike detection, cross-correlation with time-lag, and haversine geometry to track pollution movement.

HTML report with map

Generate a visual report with Leaflet.js map showing cities, front arrows, and analysis tables:

airq report --city hamburg --radius 150 --days 3         # HTML only
airq report --city hamburg --radius 150 --days 3 --pdf   # HTML + PDF

Opens in any browser. For PDF export (--pdf) you need one of:

  • Google Chrome (recommended) — already installed on most systems, used in headless mode
  • wkhtmltopdf — lightweight alternative:
    brew install wkhtmltopdf        # macOS
    sudo apt install wkhtmltopdf    # Ubuntu/Debian
    

Pollution source attribution (blame)

Identify which factories, power plants, or highways contribute to local air pollution:

airq blame --city hamburg --radius 20 --days 7

Auto-discovers factories, power plants, and highways from OpenStreetMap (Overpass API). CPF = probability that high PM2.5 occurs when wind blows from that source direction. Custom sources in config:

[[sources]]
name = "My Local Factory"
lat = 55.82
lon = 37.73
source_type = "factory"

The report command includes blame data too — source markers on the map and CPF table in the PDF.

Find nearby sensors

airq nearby --city gazipasa --radius 10

Configuration

Set up a default city and favorites list so you don't have to type --city every time:

airq init --city tokyo          # set default city
airq                            # now just works — uses tokyo

Config file: ~/.config/airq/config.toml

default_city = "tokyo"
cities = ["tokyo", "berlin", "istanbul", "new york"]

With a favorites list, check all cities at once:

airq --all
# City              AQI  PM2.5
1 Istanbul          98   🟡 34.6
2 New York          72   🟡 22.0
3 Berlin            31   🟢 4.2
4 Tokyo             35   🟢 8.3

How it works

By default (--provider all), airq fetches both sources in parallel and averages PM2.5/PM10. Each value shows the breakdown:

PM2.5  4.9 avg  (4.2 model, 5.7 sensors) μg/m³
  • model = Open-Meteo CAMS atmospheric forecast (global, ~11km grid) — can be inaccurate for some regions
  • sensors = median of all Sensor.Community sensors within 5km — real measurements, ground truth

When both available, dynamic merge weights sensors higher. If model diverges >5x from sensors, model is ignored. If no sensors nearby, falls back to model only.

AQI scale

AQI Category Color
0–50 Good 🟢
51–100 Moderate 🟡
101–150 Unhealthy for Sensitive Groups 🟠
151–200 Unhealthy 🔴
201–300 Very Unhealthy 🟣
301–500 Hazardous 🟤

US AQI from Open-Meteo API. EU AQI also shown. For sensor-only data, AQI calculated using EPA formula.

Front detection methodology

The front and report commands use cross-correlation analysis to detect pollution movement:

  1. Spike detection — Z-score on hourly PM2.5 differences. Flags sudden changes (z > 2σ)
  2. Cross-correlation — compares time-series between city/sensor pairs with lags -24h to +24h. Peak correlation reveals transit time
  3. Speed & direction — haversine distance / time lag = front speed. Bearing calculated from coordinates
  4. Dual-source — when both Open-Meteo (model) and Sensor.Community (ground sensors) are available, correlations are weighted and merged. Agreement boosts confidence
  5. Sensor clustering — nearby sensors (~5km) are grouped into zones for spatial analysis. Reports show individual sensor locations with PM2.5 heatmap overlay

See examples/ for sample reports.

Data sources

All free, no API keys needed:

Architecture

Cargo workspace with shared core:

airq-core/          Pure calculations, no IO — WASM-ready
├── lib.rs          AQI (EPA), 14 sigmoid normalizers, ComfortScore, fronts, CPF, WASM bindings
├── matrix.rs       SignalMatrix: macro-driven time-series, ML vector (44-dim), bincode storage
├── event.rs        Event detection: EWMA + concordance + directional (dual PM2.5+PM10)
└── merge.rs        Model+sensor dynamic weighting by divergence

airq/               CLI + async network fetching

Use airq-core in your own project:

airq-core = "1.2"                                          # CLI (default)
airq-core = { version = "1.2", default-features = false }  # minimal / iOS / WASM

101 tests. 14 environmental signals with sigmoid/gaussian normalization.

License

MIT