Sensor.Community quickstart — PM over a city¶
Sensor.Community is a global network of low-cost, citizen-operated air sensors. This notebook pulls PM2.5 and PM10 over a sensor-dense area around Stuttgart for one recent day and plots the readings.
The earthlens backend is tabular: download() returns a long-format
pandas.DataFrame. It discovers active sensors in the bbox via the live API, then
fetches each sensor's per-day archive CSV.
Public & keyless, ODbL. No credentials and no extra install — the backend uses core
requests+pandas. Readings are crowdsourced from low-cost sensors (not reference-grade) and licensed under the ODbL;download()emits aLicenseWarning, silenced here after acknowledgement.
Setup¶
%matplotlib inline
import warnings
import matplotlib.pyplot as plt
from earthlens.core import EarthLens
from earthlens.sensor_community import Catalog, LicenseWarning
warnings.simplefilter("ignore", LicenseWarning) # ODbL acknowledged
The pollutant catalog¶
variables names pollutants, mapped to CSV columns + serving sensor types.
sorted(Catalog().pollutants), Catalog().columns_for(["pm25", "pm10"])
Build the request¶
Pick a recent day and a small, sensor-dense bbox — discovery uses the live snapshot, so only sensors currently active in the bbox are found, and one day over a city already returns thousands of readings.
client = EarthLens(
data_source="sensor-community",
variables=["pm25", "pm10"],
start="2026-06-30",
end="2026-06-30",
lat_lim=[48.76, 48.80], # central Stuttgart — a handful of sensors
lon_lim=[9.16, 9.21],
path="out/sensor_community",
)
Download the readings¶
df = client.download(progress_bar=False)
print(df.shape)
df.head()
Plot PM2.5 across the day¶
One line per sensor for the busiest few sensors (a city bbox can hold many).
if not df.empty:
pm25 = df[df["parameter"] == "pm25"]
top = pm25["station_id"].value_counts().head(6).index
fig, ax = plt.subplots(figsize=(10, 4))
for sid in top:
group = pm25[pm25["station_id"] == sid].sort_values("datetime_utc")
ax.plot(
group["datetime_utc"],
group["value"],
marker=".",
ms=3,
lw=0.6,
label=str(sid),
)
ax.set_ylabel("PM2.5 (µg/m³)")
ax.set_xlabel("time (UTC)")
ax.set_title("Sensor.Community PM2.5 — central Stuttgart, 2026-06-30")
ax.legend(title="sensor id", fontsize=8)
fig.autofmt_xdate()
plt.show()