@shootismoke/dataproviders

Helpers to fetch data from air quality data providers

Usage no npm install needed!

<script type="module">
  import shootismokeDataproviders from 'https://cdn.skypack.dev/@shootismoke/dataproviders';
</script>

README

npm (scoped) dependencies Status Maintainability Test Coverage

@shootismoke/dataproviders

A library to fetch air quality data from various providers (AQICN, OpenAQ...) and normalizing data into one common format: the openaq-data-format.

Supported Air Quality Providers

Provider Provider Code Website Notes
AQICN aqicn https://aqicn.org API token needed, get one for free here
WAQI waqi https://waqi.info/ Same institution as AqiCN
OpenAQ v2 openaq https://openaq.org

We plan to support more air quality providers in the future see issue #29.

⚡ Get Started

Install the package:

yarn install @shootismoke/dataproviders

The package exposes a couple of data providers (see list above), and for each data provider, there are two main functions:

  • fetchByGps({ latitude, longitude }, options?) - Fetch air quality data by GPS coordinates.
  • fetchByStation(stationId, options?) - Fetch air quality data by station ID.
// Retrieve the providers by provider code.
import { aqicn, openaq } from '@shootismoke/dataproviders';

async function main() {
    // Fetch the station 1045 on AQICN.
    const data = await aqicn.fetchByStation(1045);
    console.log(data.dominentpol); // `dominentpol` is a field specific to AQICN. This logs "pm25".

    const results = aqicn.normalizeByStation(data); // `results` is an array of normalized OpenAQResults.
    const res = results[0];
    console.log(`${res.parameter}: ${res.value} ${res.unit}`); // Logs "pm25: 34.5 µg/m³".

    // Note how `data.dominentpol` has been converted to `res.parameter`: this is
    // what we call "normalization" in @shootismoke/providers.
}

Usage with fp-ts

The codebase uses io-ts under the hood to perform runtime data validation, the results are functional programming datatypes (Either<E,A>, Task<E,A>...). If you are already using io-ts and fp-ts](https://github.com/gcanti/fp-ts) in your project, it's recommended to import from the /lib/fp subfolder directly.

// Retrieve the providers by provider code, using fp-ts.
import { aqicn, openaq } from '@shootismoke/dataproviders/lib/fp';

aqicn.fetchByGps({ latitude: 45, longitude: 23 }); // Returns a TaskEither<Error, AqicnData>

Here's a real-world example:

import { pipe } from 'fp-ts/lib/pipeable';
import * as TE from 'fp-ts/lib/TaskEither';

pipe(
    // Fetch data from station 'Coyhaique' on the OpenAQ platform.
    openaq.fetchByStation('Coyhaique'),
    // Normalize the data.
    TE.chain((response) => TE.fromEither(normalize(response))),
    // Depending on error/result case, do different stuff
    TE.fold(
        // Do on error:
        (error) => {
            console.error(error);

            // -- snip --
        },
        // Do on success:
        (results) => {
            const res = results[0]; // `results` is an array of normalized OpenAQ objects
            console.log(`${res.value} ${res.unit}`); // Logs "34.5 µg/m³"

            // -- snip --
        }
    )
);

Normalized Data Format

If you use the .normalizeByGps or .normalizeByStation functions, the output will be normalized, i.e. in the same format no matter which provider you use. We follow the openaq-data-format, below are some of its most frequently used fields:

/**
 * The OpenAQ data format. One such object represents one air quality
 * measurement
 */
interface OpenAQResult {
    /**
     * City (or regional approximation) containing location
     */
    city?: string;
    /**
     * Coordinates where the measurement took place. Note that in the
     * openaq-data-format, this field is optional. Using this library, this field
     * will **always** be populated
     */
    coordinates?: {
        latitude: number;
        longitude: number;
    };
    /**
     * Country containing location in two letter ISO format
     */
    country: string;
    /**
     * Time of measurement including both local time and UTC time.
     */
    date: {
        local: string;
        utc: string;
    };
    /**
     * A unique ID representing the location of a measurement (can be a station
     * ID, a place...)
     */
    location: string;
    /**
     * The pollutant id (pm25, pm10, o3...)
     */
    parameter: Pollutant;
    /**
     * The value of the measurement
     */
    value: number;
    /**
     * The unit the value is measured in (µg/m³, ppm)
     */
    unit: Unit;
    // -- snip --
    // There are some other optional fields, see docs for more details.
}

/**
 * The normalized data is an array of OpenAQ results. We ensure there is
 * always at least one element in the Normalized array.
 */
type OpenAQResults = OpenAQResult[];

See openaq-data-format for more information.

Full Documentation

See the API reference documentation.

:raising_hand: Contribute

  1. Fork the repo
  2. Make your changes in your own fork
  3. Make sure yarn lint and yarn test pass
  4. Create a Pull Request on this repo

Tests

Look out for *.spec.ts in the codebase. Run:

yarn test

:newspaper: License

GPL-3.0. See LICENSE file for more information.