Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to Rwanda REMA data source #1089

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 98 additions & 43 deletions src/adapters/rwanda.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,102 @@
'use strict';
/**
* This code is responsible for implementing all methods related to fetching
* and returning data for the Rwanda REMA data source.
*/

import _ from 'lodash';
import { DateTime } from 'luxon';
import client from '../lib/requests.js';
import log from '../lib/logger.js';
import { promisePostRequest, unifyMeasurementUnits } from '../lib/utils.js';

export const name = 'rwanda';

export async function fetchData (source, cb) {
try {
// Create post requests for all parameters
const params = ['PM25', 'PM10', 'O3', 'NO2', 'SO2', 'CO', 'PB'];
const paramRequests = params.map(p =>
promisePostRequest(source.url, { parameter: p })
// Handle request errors gracefully
.catch(error => { log.warn(error || 'Unable to load data for parameter'); return null; }));
// Run post requests in parallel and wait for all to resolve
let allData = await Promise.all(paramRequests);

allData = allData.map(d => JSON.parse(d)).filter(d => (d));
let measurements = allData.map(data => {
// Create base object
const base = {
location: data.location,
coordinates: data.coordinates,
city: data.city,
attribution: data.attribution,
parameter: data.parameter,
averagingPeriod: data.averagingPeriod
};
// Loop through array of values and dates
const paramMeasurements = data.data.map(d => {
const m = {
date: { local: d.date_local, utc: d.date_utc },
value: Number(d.value),
unit: data.unit
};
unifyMeasurementUnits(m);
return { ...base, ...m };
});
return paramMeasurements;

export const name = 'rwanda-rema';
export const parameters = {
CO: { name: 'co', unit: 'ppm' },
NO2: { name: 'no2', unit: 'ppm' },
O3: { name: 'o3', unit: 'ppm' },
PM10: { name: 'pm10', unit: 'µg/m³' },
PM25: { name: 'pm25', unit: 'µg/m³' },
SO2: { name: 'so2', unit: 'ppm' },
};

/**
* @param {Object} source - The data source, including the URL to fetch from.
* @param {Function} cb - Callback function to handle the response or error.
*/
export function fetchData(source, cb) {
// Potential error if data.features is undefined or not an array
client({ url: source.url })
.then((data) => {

const formattedData = formatData(data.features);

log.debug('First row of formatted:', formattedData.measurements.length && formattedData.measurements[0]);

if (!formattedData) {
throw new Error('Failure to parse data.');
}
cb(null, formattedData);
})
.catch((error) => {
cb(error);
});
cb(null, { name: 'unused', measurements: _.flatten(measurements) });
} catch (e) {
cb(e);
}
}

/**
* @param {Array} features - Array of features from the raw data response.
* @returns {Object} An object containing formatted air quality measurements.
*/
const formatData = function (features) {
let measurements = [];

features.forEach((feature) => {
const { geometry, properties } = feature;
const { coordinates } = geometry;
const longitude = coordinates[0];
const latitude = coordinates[1];

properties.data.forEach((dataItem) => {
Object.entries(dataItem).forEach(([key, value]) => {
try {
if (value !== null && Object.keys(parameters).includes(key)) {
// The dates appear to be in local time, not UTC
const dateString = dataItem.time.replace('Z', '');

const localTime = DateTime.fromISO(dateString, { zone: 'Africa/Kigali' });

const parameter = parameters[key];

measurements.push({
location: properties.title,
city: ' ',
parameter: parameter.name,
value: value,
unit: parameter.unit,
date: {
utc: localTime.toUTC().toISO({ suppressMilliseconds: true }),
local: localTime.toISO({ suppressMilliseconds: true }),
},
coordinates: {
latitude,
longitude,
},
attribution: [
{
name: 'Rwanda Environment Management Authority',
url: 'https://aq.rema.gov.rw/',
},
],
averagingPeriod: {
unit: 'hours',
value: 1,
},
});
}
} catch (error) {
log.error(`Error processing measurement for ${properties.title}: ${error}`);

}
});
});
});

return { measurements: measurements };
};
Loading
Loading