Skip to content

aeolus.networks

Functions for working with discrete monitoring networks (AURN, SAQN, Breathe London, etc.).

Functions

Get monitoring site metadata for a network.

Networks are discrete monitoring networks operated by organizations with finite numbers of sites that can be listed completely.

Parameters:

Name Type Description Default
network str

Network name ("AURN", "SAQN", "BREATHE_LONDON", etc.)

required
**filters

Optional network-specific filters

{}

Returns:

Type Description
DataFrame

DataFrame with site metadata including: - site_code: Unique site identifier - site_name: Human-readable site name - latitude: Site latitude - longitude: Site longitude - source_network: Network name

Raises:

Type Description
ValueError

If network is unknown or not a network type

Examples:

>>> # Get all AURN sites
>>> sites = aeolus.networks.get_metadata("AURN")
>>>
>>> # Get Breathe London sites
>>> sites = aeolus.networks.get_metadata("BREATHE_LONDON")
>>>
>>> # Some networks support filters
>>> sites = aeolus.networks.get_metadata("BREATHE_LONDON", borough="Camden")
Source code in src/aeolus/networks/api.py
def get_metadata(network: str, **filters) -> pd.DataFrame:
    """
    Get monitoring site metadata for a network.

    Networks are discrete monitoring networks operated by organizations with
    finite numbers of sites that can be listed completely.

    Args:
        network: Network name ("AURN", "SAQN", "BREATHE_LONDON", etc.)
        **filters: Optional network-specific filters

    Returns:
        DataFrame with site metadata including:
            - site_code: Unique site identifier
            - site_name: Human-readable site name
            - latitude: Site latitude
            - longitude: Site longitude
            - source_network: Network name

    Raises:
        ValueError: If network is unknown or not a network type

    Examples:
        >>> # Get all AURN sites
        >>> sites = aeolus.networks.get_metadata("AURN")
        >>>
        >>> # Get Breathe London sites
        >>> sites = aeolus.networks.get_metadata("BREATHE_LONDON")
        >>>
        >>> # Some networks support filters
        >>> sites = aeolus.networks.get_metadata("BREATHE_LONDON", borough="Camden")
    """
    source_spec = get_source(network)

    if not source_spec:
        raise ValueError(f"Unknown network: {network}")

    # Verify it's a network
    source_type = source_spec.get("type", "network")
    if source_type != "network":
        raise ValueError(
            f"{network} is a {source_type}, not a network.\n"
            f"Use aeolus.portals.find_sites() for portals."
        )

    # Get metadata fetcher
    fetcher = source_spec.get("fetch_metadata")
    if not fetcher:
        raise ValueError(f"Network {network} does not support metadata fetching")

    return fetcher(**filters)

Download air quality data from a network.

Parameters:

Name Type Description Default
network str

Network name ("AURN", "SAQN", "BREATHE_LONDON", etc.)

required
sites list[str]

List of site codes to download

required
start_date datetime

Start of date range (inclusive)

required
end_date datetime

End of date range (inclusive)

required

Returns:

Type Description
DataFrame

DataFrame with standardised schema: - site_code: Site identifier - date_time: Measurement timestamp - measurand: Pollutant measured (e.g., "NO2", "PM2.5") - value: Measured value - units: Units of measurement - source_network: Network name - ratification: Data quality flag - created_at: When record was fetched

Raises:

Type Description
ValueError

If network is unknown or not a network type

Examples:

>>> from datetime import datetime
>>> data = aeolus.networks.download(
...     "AURN",
...     ["MY1", "MY2"],
...     datetime(2024, 1, 1),
...     datetime(2024, 1, 31)
... )
Source code in src/aeolus/networks/api.py
def download(
    network: str,
    sites: list[str],
    start_date: datetime,
    end_date: datetime,
) -> pd.DataFrame:
    """
    Download air quality data from a network.

    Args:
        network: Network name ("AURN", "SAQN", "BREATHE_LONDON", etc.)
        sites: List of site codes to download
        start_date: Start of date range (inclusive)
        end_date: End of date range (inclusive)

    Returns:
        DataFrame with standardised schema:
            - site_code: Site identifier
            - date_time: Measurement timestamp
            - measurand: Pollutant measured (e.g., "NO2", "PM2.5")
            - value: Measured value
            - units: Units of measurement
            - source_network: Network name
            - ratification: Data quality flag
            - created_at: When record was fetched

    Raises:
        ValueError: If network is unknown or not a network type

    Examples:
        >>> from datetime import datetime
        >>> data = aeolus.networks.download(
        ...     "AURN",
        ...     ["MY1", "MY2"],
        ...     datetime(2024, 1, 1),
        ...     datetime(2024, 1, 31)
        ... )
    """
    source_spec = get_source(network)

    if not source_spec:
        raise ValueError(f"Unknown network: {network}")

    # Verify it's a network
    source_type = source_spec.get("type", "network")
    if source_type != "network":
        raise ValueError(
            f"{network} is a {source_type}, not a network.\n"
            f"Use aeolus.portals.download() for portals."
        )

    # Get data fetcher
    fetcher = source_spec.get("fetch_data")
    if not fetcher:
        raise ValueError(f"Network {network} does not support data downloading")

    return fetcher(sites, start_date, end_date)

List all available networks.

Returns:

Type Description
list[str]

List of network names

Examples:

>>> networks = aeolus.networks.list_networks()
>>> print(networks)
['AURN', 'SAQN', 'WAQN', 'NI', 'BREATHE_LONDON', ...]
Source code in src/aeolus/networks/api.py
def list_networks() -> list[str]:
    """
    List all available networks.

    Returns:
        List of network names

    Examples:
        >>> networks = aeolus.networks.list_networks()
        >>> print(networks)
        ['AURN', 'SAQN', 'WAQN', 'NI', 'BREATHE_LONDON', ...]
    """
    from ..registry import SOURCES

    return [name for name, spec in SOURCES.items() if spec.get("type") == "network"]

Usage Examples

Get Network Metadata

import aeolus

# Get all AURN sites
sites = aeolus.networks.get_metadata("AURN")

# View columns
print(sites.columns)
# Index(['site_code', 'site_name', 'latitude', 'longitude', 'site_type', ...])

# Filter to London sites
london = sites[sites['site_name'].str.contains('London')]

Download Network Data

import aeolus
from datetime import datetime

data = aeolus.networks.download(
    network="AURN",
    sites=["MY1", "KC1"],
    start_date=datetime(2024, 1, 1),
    end_date=datetime(2024, 1, 31)
)

List Available Networks

networks = aeolus.networks.list_networks()
print(networks)
# ['AURN', 'SAQN', 'WAQN', 'NI', 'AQE', 'LAQN',
#  'BREATHE_LONDON', 'AIRQO', 'AIRNOW', 'SENSOR_COMMUNITY']

Supported Networks

UK Regulatory Networks (no API key required)

Network Description
AURN UK Automatic Urban and Rural Network
SAQN Scottish Air Quality Network
WAQN Welsh Air Quality Network
NI Northern Ireland Network
AQE Air Quality England
LAQN London Air Quality Network

Other Networks

Network Description API Key
BREATHE_LONDON London low-cost sensors Yes (BL_API_KEY)
AIRQO African cities network Yes (AIRQO_API_KEY)
AIRNOW US EPA real-time data Yes (AIRNOW_API_KEY)
SENSOR_COMMUNITY Global citizen science No