Urban Observatory API Example¶
This notebook demonstrates how to use the uo_pyfetch library to fetch data from the Urban Observatory Sensor API.
In [11]:
from datetime import datetime, timedelta
import uo_pyfetch
from IPython.display import HTML
Define bounding box for Newcastle area¶
In [2]:
bbox = [-1.652756, 54.973377, -1.620483, 54.983721]
Fetch available sensor variables¶
In [3]:
variables = uo_pyfetch.get_variables()
print("Available variables:")
for v in variables["Variables"][:5]:
print(f" - {v['Name']} ({v.get('Units', 'no units')})")
Available variables: - allpollu (%) - Ammonia (?) - Average Speed (KmPH) - Battery (V) - Brood (Celsius)
Fetch available sensor themes¶
In [4]:
themes = uo_pyfetch.get_themes()
print("\nAvailable themes:")
for t in themes["Themes"]:
print(f" - {t['Name']}")
Available themes: - Sewage - Sensor Metrics - Electrical - Seismic - Water Quality - Traffic - Air Quality - Water Level - Bee Hive - People - Building - Environment - Weather - Noise - Vehicles
Fetch sensors within bounding box¶
In [13]:
print("\nFetching sensors in bbox...")
sensors_df = uo_pyfetch.get_sensors(limit=50, bbox=bbox)
HTML(sensors_df.head().to_html())
Fetching sensors in bbox...
Out[13]:
| Sensor_Name | Location_WKT | Sensor_Centroid_Longitude | Ground_Height_Above_Sea_Level | Sensor_Centroid_Latitude | Sensor_Height_Above_Ground | Broker_Name | Raw_ID | |
|---|---|---|---|---|---|---|---|---|
| 0 | PER_NEWCASTLE_STJAMES | POINT(-1.62099 54.97408) | -1.62099 | 50.750000 | 54.97408 | 2.0 | Newcastle Council RSS feed | 137 |
| 1 | PER_NEWCASTLE_STJAMES_METRO | POINT(-1.62099 54.97408) | -1.62099 | 50.750000 | 54.97408 | 2.0 | Newcastle Council RSS feed | 220 |
| 2 | PER_BUILDING_OAT_NUBS_TREND_MOM | POINT(-1.62332 54.97415) | -1.62332 | 56.509998 | 54.97415 | 2.0 | Building External Sensor | 615 |
| 3 | PER_BUILDING_OAT_SPORTS_HALL_TREND_MOM | POINT(-1.62469 54.98274) | -1.62469 | 66.709999 | 54.98274 | 2.0 | Building External Sensor | 630 |
| 4 | PER_BUILDING_OAT_RICH_RD_BLR_HSE_TREND_MOM | POINT(-1.62419 54.98162) | -1.62419 | 64.959999 | 54.98162 | 2.0 | Building External Sensor | 634 |
Fetch recent sensor data (last 2 hours)¶
In [12]:
print("\nFetching sensor data (last 2 hours)...")
sensor_data_df = uo_pyfetch.get_sensor_data(
last_n_hours=2,
variables=["NO2", "PM10"],
bbox=bbox,
limit=1000
)
HTML(sensor_data_df.to_html(index=False))
Fetching sensor data (last 2 hours)...
Out[12]:
| Sensor_Name | Variable | Value | Timestamp | Flagged | Location_WKT | Sensor_Centroid_Longitude | Ground_Height_Above_Sea_Level | Sensor_Centroid_Latitude | Sensor_Height_Above_Ground | Broker_Name | Raw_ID |
|---|---|---|---|---|---|---|---|---|---|---|---|
| PER_AIRMON_MESH306245 | NO2 | 23.21424 | 2025-06-05 10:30:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | PM10 | 6.95000 | 2025-06-05 10:30:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | NO2 | 22.25544 | 2025-06-05 10:45:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | PM10 | 7.54000 | 2025-06-05 10:45:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | NO2 | 24.17304 | 2025-06-05 11:00:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | PM10 | 7.35000 | 2025-06-05 11:00:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | NO2 | 26.37264 | 2025-06-05 11:15:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | PM10 | 7.20000 | 2025-06-05 11:15:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | NO2 | 23.90984 | 2025-06-05 11:30:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | PM10 | 6.92000 | 2025-06-05 11:30:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | NO2 | 23.77824 | 2025-06-05 11:45:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| PER_AIRMON_MESH306245 | PM10 | 6.02000 | 2025-06-05 11:45:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
Fetch full history for a specific sensor if data is available¶
In [24]:
if not sensor_data_df.empty:
sensor_name = sensor_data_df.iloc[0]["Sensor_Name"]
print(f"\nFetching full history for sensor: {sensor_name}")
start = datetime.now() - timedelta(days=1)
end = datetime.now()
sensor_specific_df = uo_pyfetch.get_sensor_data_by_name(
sensor_name,
start=start,
end=end,
variables=["NO2"]
)
else:
print("\nNo sensors found in bbox.")
HTML(sensor_specific_df.head().to_html())
Fetching full history for sensor: PER_AIRMON_MESH306245
Out[24]:
| Sensor_Name | Variable | Value | Timestamp | Flagged | Location_WKT | Sensor_Centroid_Longitude | Ground_Height_Above_Sea_Level | Sensor_Centroid_Latitude | Sensor_Height_Above_Ground | Broker_Name | Raw_ID | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | PER_AIRMON_MESH306245 | NO2 | 25.31984 | 2025-06-04 13:30:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| 1 | PER_AIRMON_MESH306245 | NO2 | 26.91784 | 2025-06-04 13:45:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| 2 | PER_AIRMON_MESH306245 | NO2 | 24.51144 | 2025-06-04 14:00:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| 3 | PER_AIRMON_MESH306245 | NO2 | 27.57584 | 2025-06-04 14:15:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
| 4 | PER_AIRMON_MESH306245 | NO2 | 27.50064 | 2025-06-04 14:30:00 | False | POINT(-1.648605 54.977836) | -1.648605 | 111.389999 | 54.977836 | 2.0 | aq_mesh_api | 79521 |
In [ ]: