UNPKG

air-monitor

Version:

Utilities for working with air quality monitoring data.

134 lines (116 loc) 4.53 kB
/** * Utility functions for computing current status summaries and generating GeoJSON * FeatureCollections from Monitor metadata. These are used to spatially visualize the * most recent valid air quality measurements and data latencies from each deployed sensor. * * Internal functions: * - `internal_getCurrentStatus()` – Appends current data statistics to the Monitor's metadata table. * - `internal_createGeoJSON()` – Creates a .geojson file with current data statistics for each time series. */ import * as aq from "arquero"; const op = aq.op; /** * Compute the most recent valid timestamp and value for each deviceDeploymentID, * and append them to the metadata table. * * @param {Monitor} monitor - The Monitor instance. * @returns {aq.Table} An enhanced metadata table with last valid datetime and value. */ export function internal_getCurrentStatus(monitor) { const { data, meta } = monitor; const ids = monitor.getIDs(); // Add a zero-based index to non-datetime columns const dataWithIndex = data .select(aq.not("datetime")) .derive({ index: d => op.row_number() - 1 }); // Replace valid values with their row index; else 0 const valueExprs = {}; ids.forEach(id => { valueExprs[id] = `d => op.is_finite(d['${id}']) ? d.index : 0`; }); // Find max index per ID (i.e., most recent valid row) const maxIndexExprs = {}; ids.forEach(id => { maxIndexExprs[id] = `d => op.max(d['${id}'])`; }); const lastValidIndexObj = dataWithIndex.derive(valueExprs).rollup(maxIndexExprs).object(0); const lastValidIndices = Object.values(lastValidIndexObj); const lastValidDatetime = lastValidIndices.map(i => data.array("datetime")[i]); const lastValidValues = ids.map((id, i) => data.get(id, lastValidIndices[i])); const statusTable = aq.table({ lastValidDatetime, lastValidPM_25: lastValidValues, // Generic, though field is named for PM2.5 }); return meta.assign(statusTable); } /** * Convert the current metadata table to a GeoJSON FeatureCollection. * * @param {Monitor} monitor - The Monitor instance. * @returns {Object} A GeoJSON FeatureCollection object. */ export function internal_createGeoJSON(monitor) { const meta = internal_getCurrentStatus(monitor); const features = []; // From: mv4_wrcc_PM2.5_latest.geojson // // { // "type": "FeatureCollection", // "features": [ // { // "type": "Feature", // "geometry": { // "type": "Point", // "coordinates": [-114.0909, 46.5135] // }, // "properties": { // "deviceDeploymentID": "aaef057f3e4d83c4_wrcc.s139", // "locationName": "Smoke USFS R1-39", // "timezone": "America/Denver", // "dataIngestSource": "WRCC", // "dataIngestUnitID": "s139", // "currentStatus_processingTime": "2022-12-12 13:54:13", // "last_validTime": "2022-12-12 12:00:00", // "last_validLocalTimestamp": "2022-12-12 05:00:00 MST", // "last_nowcast": "2.9", // "last_PM2.5": "2", // "last_latency": "1", // "yesterday_PM2.5_avg": "4.7" // } // } // ] // } // Format any DateTime objects to human-readable strings with timezone const formatDT = (dt) => dt?.toFormat?.('yyyy-MM-dd HH:mm:ss ZZZZ') ?? null; for (let i = 0; i < meta.numRows(); i++) { const site = meta.slice(i, i + 1).object(); features.push({ type: "Feature", geometry: { type: "Point", coordinates: [site.longitude, site.latitude], }, properties: { deviceDeploymentID: site.deviceDeploymentID, locationName: site.locationName, last_time: formatDT(site.lastValidDatetime), last_pm25: site.lastValidPM_25?.toString() ?? null, // timezone: site.timezone, // dataIngestSource: site.dataIngestSource, // dataIngestUnitID: site.dataIngestUnitID, // currentStatus_processingTime: formatDT(site.currentStatus_processingTime), // last_validTime: formatDT(site.lastValidDatetime), // last_validLocalTimestamp: formatDT(site.lastValidLocalTimestamp), // last_nowcast: site.lastNowcast?.toString() ?? null, // last_PM2_5: site.lastValidPM_25?.toString() ?? null, // last_latency: site.lastLatency?.toString() ?? null, // yesterday_PM2_5_avg: site.yesterdayAvgPM_25?.toString() ?? null, }, }); } return { type: "FeatureCollection", features, }; }