Commit 81428664 authored by alain's avatar alain 🐙

settings for calibrated data / download feature

parent ea366bf0
export const appSettings = {
language: "nl",
panelBreakpoint: 800
panelBreakpoint: 700
}
\ No newline at end of file
import luchtmeetnet from "./sources/luchtmeetnet"
import teom from "./sources/teom"
import holu from "./sources/holu"
//import holu from "./sources/holu"
import holu from "./sources/holu-cal"
//import holu from "./sources/holu-rivm"
//import sentinel from "./sources/sentinel"
......@@ -27,6 +28,7 @@ export const dataGroups = [
id: "μg/m3",
label: " μg/m3",
range: [0, 150],
ticks: [0, 25, 50, 75, 100, 125, 150],
legend: {
0: { label: "Goed", color: colors.green },
35: { label: "Matig", color: colors.yellow },
......@@ -55,6 +57,7 @@ export const dataGroups = [
id: "μg/m3",
label: " μg/m3",
range: [0, 250],
ticks: [0, 50, 100, 150, 200, 250],
legend: {
0: { label: "Goed", color: colors.green },
55: { label: "Matig", color: colors.yellow },
......@@ -83,6 +86,7 @@ export const dataGroups = [
id: "μg/m3",
label: " μg/m3",
range: [0, 250],
ticks: [0, 50, 100, 150, 200, 250],
legend: {
0: { label: "Goed", color: colors.green },
55: { label: "Matig", color: colors.yellow },
......@@ -92,7 +96,7 @@ export const dataGroups = [
}
}
],
tooltip: "<p>Deze monitor meet de totale hoeveelheid stof. De Engelse benaming voor deze metingen is Total Suspended Particles ‘TSP”. Tientallen jaren geleden bestonden er normen voor deze metingen. Omdat een gedeelte van dit stof te groot is om in te ademen is deze wetgeving later omgezet in PM10 normen. Van deze TSP metingen is bekend dat het een relatie kan leggen met klachten over zichtbare stofoverlast.</p>",
tooltip: "<p>Deze monitor meet de totale hoeveelheid stof. De Engelse benaming voor deze metingen is Total Suspended Particles ‘TSP”. Tientallen jaren geleden bestonden er normen voor deze metingen. Omdat een gedeelte van dit stof te groot is om in te ademen is deze wetgeving later omgezet in PM10 normen. Van deze TSP metingen is bekend dat het een relatie kan leggen met klachten over zichtbare stofoverlast.</p>",
more: "",
type: "StationLayer",
sources: [
......@@ -108,6 +112,7 @@ export const dataGroups = [
id: "μg/m3",
label: " μg/m3",
range: [0, 250],
ticks: [0, 50, 100, 150, 200, 250],
legend: {
0: { label: "Goed", color: colors.green },
70: { label: "Matig", color: colors.yellow },
......@@ -134,6 +139,7 @@ export const dataGroups = [
id: "μg/m3",
label: " μg/m3",
range: [0, 300],
ticks: [0, 50, 100, 150, 200, 250, 300],
legend: {
0: { label: "Goed", color: colors.green },
100: { label: "Matig", color: colors.yellow },
......
......@@ -81,7 +81,7 @@ export const mapItemSettings = {
sizeMax: 200,
elevationMaxMin: 120,
elevationMaxMax: 2000,
colorOffline: "#888"
colorOffline: "#333"
}
export const lightSettings = {
......
import { addMissingDataPoints, downsampleData } from "../../data-on-a-map-app/src/util/data"
export default {
name: "holu",
sides: 24,
angle: 0,
factor: 0.5,
daysToFetch: 90,
dataStart: "2019-06-25T00:00:00.000Z",
dataStreams: {
pm25: {
raw: {
minmax: { key: "pm25_raw_minmax", type: "area", active: false, color: false },
mean: { key: "pm25_raw_mean", type: "line", active: false, color: false }
},
cal: {
minmax: { key: "pm25_cal_minmax", type: "area", active: true, color: true },
mean: { key: "pm25_cal_mean", type: "line", active: true, color: true }
}
},
pm10: {
raw: {
minmax: { key: "pm10_raw_minmax", type: "area", active: false, color: false },
mean: { key: "pm10_raw_mean", type: "line", active: false, color: false }
},
cal: {
minmax: { key: "pm10_cal_minmax", type: "area", active: true, color: true },
mean: { key: "pm10_cal_mean", type: "line", active: true, color: true }
}
}
},
all: async function(parameter) {
try {
const allResponse = await fetch(`https://data.waag.org/api/getStations`)
const all = await allResponse.json()
// const latestRawResponse = await fetch(`https://data.waag.org/api/getAllSensors`)
// let latestRaw = await latestRawResponse.json()
const latestResponse = await fetch(`https://data.waag.org/api/holu/holukit/calibrated/recent`)
let latest = await latestResponse.json()
let offlineSensors = []
let unregisteredSensors = []
//let places = {}
const data = all.map(sensor => {
const sensorData = latest.find(o => +o.id === +sensor.id)
const sensorDataReturn = {
id: sensor.id.toString(),
name: "HOLU-sensor " + sensor.id.toString() + ": " + sensor.name,
source: "holu",
coordinates: [sensor.longitude, sensor.latitude],
mean: null,
timestamp: null,
dataAge: null
}
if(sensorData) {
const dataAgeInHours = ((new Date() - new Date(sensorData.time)) / 3600000) - 1
if(dataAgeInHours >= 48 || dataAgeInHours < 0) {
sensorDataReturn.name = sensorDataReturn.name + " (offline)"
} else if(dataAgeInHours < 8) {
sensorDataReturn.mean = sensorData[`${parameter}_mean`]
} else {
sensorDataReturn.name = sensorDataReturn.name + " (no recent data)"
}
sensorDataReturn.timestamp = sensorData.time
sensorDataReturn.dataAge = dataAgeInHours
} else {
sensorDataReturn.name = sensorDataReturn.name + " (offline)"
offlineSensors.push(sensor.id)
}
return sensorDataReturn
})
latest.forEach(sensor => {
if(!all.find(o => +o.id === +sensor.id)) {
unregisteredSensors.push(sensor.id)
}
})
console.log(`Offline Sensors (${offlineSensors.length}):`, offlineSensors.sort((a, b) => a - b).join(', '))
console.log(`Unregistered Sensors (${unregisteredSensors.length}):`, unregisteredSensors.sort((a, b) => a - b).join(', '))
// Object.keys(places).forEach(function(place) {
// places[place].amountOfSensors = places[place].allAverages.length
// places[place].averageOfAverages = places[place].allAverages.reduce((a, b) => a + b, 0) / places[place].allAverages.length
// places[place].averageOfPeaks = places[place].allPeaks.reduce((a, b) => a + b, 0) / places[place].allPeaks.length
// places[place].highestAverage = Math.max(...places[place].allAverages)
// places[place].highestPeak = Math.max(...places[place].allPeaks)
// })
// console.log(places)
return data
} catch (error) {
console.log(error)
}
},
stationMeta: function(station, parameter) {
const stationMeta = {
name: station.name,
description: (station.type === "extended" ? (
'<span>Dit is een extended HOLU-meetstation. Dit prototype meet PM2.5 en PM10 en ook NO2 en O3. Als onderdeel van de pilot wordt er onderzoek gedaan naar de kwaliteit van de data afkomstig van deze meetstations.<br /><a class="more" href="https://hollandseluchten.waag.org/holu-sensorkit/">Lees meer over de HOLU kit</a></span>'
) : (
'<span>Dit is een basic HOLU-meetstation. Dit prototype meet PM2.5 en PM10. Als onderdeel van de pilot wordt er onderzoek gedaan naar de kwaliteit van de data afkomstig van deze meetstations.<br /><a class="more" href="https://hollandseluchten.waag.org/holu-sensorkit/">Lees meer over de HOLU kit</a></span>'
)
)
}
return stationMeta
},
stationData: async function(station, parameter, start, end, granularity) {
try {
var prefix
if (parameter === "pm25") prefix = parameter
if (parameter === "pm10") prefix = parameter
if (parameter === "temperature") prefix = "t_out"
if (parameter === "humidity") prefix = "h_out"
const responseRaw = await fetch(`https://data.waag.org/api/getHourlyValuesSensor?sensor_id=${station.id}&start=${start}&end=${end}`)
const jsonRaw = await responseRaw.json()
let dataRaw = jsonRaw.map(m => {
return {
timestamp: new Date(m.time).getTime(),
mean: m[`${prefix}_mean`],
minmax: [m[`${prefix}_min`], m[`${prefix}_max`]]
}
})
const responseCal = await fetch(`https://data.waag.org/api/holu/holukit/calibrated/hourly?sensor_id=${station.id}&start=${start}&end=${end}`)
const jsonCal = await responseCal.json()
let dataCal = jsonCal.map(m => {
return {
timestamp: new Date(m.time).getTime(),
mean: m[`${prefix}_mean`],
fac: m[`${prefix}_fac`],
}
})
const dataStart = (dataRaw[0].timestamp < dataCal[0].timestamp ? dataRaw[0].timestamp : dataCal[0].timestamp)
const dataEnd = new Date(end).getTime()
dataRaw = addMissingDataPoints(dataRaw, dataStart, dataEnd, granularity)
dataCal = addMissingDataPoints(dataCal, dataStart, dataEnd, granularity)
let data = []
for (let i = 0; i < dataRaw.length; i++) {
const r = dataRaw[i]
const c = dataCal[i]
const o = {
timestamp: r.timestamp
}
o[`${prefix}_raw_mean`] = r.mean
o[`${prefix}_raw_minmax`] = r.minmax
o[`${prefix}_cal_mean`] = c.mean
if(r.mean && c.mean) {
o[`${prefix}_cal_minmax`] = [r.minmax[0] * c.fac, r.minmax[1] * c.fac]
}
data.push(o)
}
return { status: "success", data }
} catch (error) {
console.log(error)
return { status: "error" }
}
},
download: function(id, parameter, start, end) {
return `https://data.waag.org/api/holu/holukit/calibrated/hourly?sensor_id=${id}&start=${start}&end=${end}&format=csv`
}
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ export default {
factor: 0.5,
dataStart: "2019-06-25T00:00:00.000Z",
all: async function getAllHolu(parameter) {
all: async function(parameter) {
try {
const allResponse = await fetch(`https://data.waag.org/api/getStations`)
......@@ -91,7 +91,7 @@ export default {
}
},
detail: async function getDetailHolu(station, parameter, start, end) {
stationData: async function(station, parameter, start, end) {
try {
const stationMeta = {
name: station.name,
......
import { getLastHourISO, getNowHourISO } from "../../data-on-a-map-app/src/util/time"
import { getLastHourISO, getNowISO, subtractOneHourISO } from "../../data-on-a-map-app/src/util/time"
import { groupArrayOfObjectsBy, renameObjectKey, addMissingDataPoints } from "../../data-on-a-map-app/src/util/data"
const officialStations = {
......@@ -82,11 +82,33 @@ export default {
angle: 0,
factor: 1,
daysToFetch: 60,
dataStreams: {
pm25: {
single: {
mean: { key: "pm25_mean", type: "line", active: true, color: true }
}
},
pm10: {
single: {
mean: { key: "pm10_mean", type: "line", active: true, color: true }
}
},
no2: {
single: {
mean: { key: "no2_mean", type: "line", active: true, color: true }
}
},
o3: {
single: {
mean: { key: "o3_mean", type: "line", active: true, color: true }
}
}
},
all: async function (parameter) {
try {
const lastHourISO = getLastHourISO()
const nowHourISO = getNowHourISO()
const nowHourISO = getNowISO()
const allResponse = await fetch(`https://data.waag.org/api/getOfficialMeasurement?formula=${parameter.toUpperCase()}&start=${lastHourISO}&end=${nowHourISO}&station_id=${officialStations[parameter].join("&station_id=")}`)
const all = await allResponse.json()
......@@ -98,9 +120,15 @@ export default {
renameObjectKey(station, "value", "mean")
renameObjectKey(station, "station_number", "id")
// we subtract one hour because luchtmeetnet api gives hour avarages with 'to' time as timestamp (instead of 'from' time as all other apis)
station.timestamp = subtractOneHourISO(station.timestamp_measured)
delete station.timestamp_measured
station.source = "luchtmeetnet"
station.name = "Officieel station: " + station.name
station.coordinates = station.coordinates.reverse()
station.dataAge = (new Date() - new Date(station.timestamp)) / 3600000
return station
})
......@@ -128,18 +156,20 @@ export default {
const groupedData = groupArrayOfObjectsBy(responseData, "formula")
const conformedData = groupedData[0].map(x => {
return {
timestamp: new Date(x.timestamp_measured).getTime(),
value: x.value
const o = {
timestamp: new Date(x.timestamp_measured).getTime()
}
o[`${parameter}_mean`] = x.value
return o
})
const data = addMissingDataPoints(conformedData, granularity)
return data
return { status: "success", data }
} catch (error) {
console.log(error)
return "error"
return { status: "error" }
}
}
}
\ No newline at end of file
......@@ -6,6 +6,23 @@ export default {
angle: 0,
factor: 0.85,
daysToFetch: 90,
dataStreams: {
pm25: {
single: {
mean: { key: "pm25_mean", type: "line", active: true, color: true }
}
},
pm10: {
single: {
mean: { key: "pm10_mean", type: "line", active: true, color: true }
}
},
ps: {
single: {
mean: { key: "ps_mean", type: "line", active: true, color: true }
}
}
},
all: async function(parameter) {
try {
......@@ -42,12 +59,14 @@ export default {
type: sensor.sensor_type,
coordinates: sensor.coordinates,
mean: null,
max: null
timestamp: null,
dataAge: null
}
if(sensorData) {
sensorDataReturn.mean = sensorData[`${parameter}_mean`]
sensorDataReturn.max = sensorData[`${parameter}_max`]
sensorDataReturn.timestamp = sensorData.time
sensorDataReturn.dataAge = (new Date() - new Date(sensorData.time)) / 3600000
} else {
sensorDataReturn.name = sensorDataReturn.name + " (geen data)"
}
......@@ -81,18 +100,18 @@ export default {
const responseData = await response.json()
const conformedData = responseData.map(m => {
return {
timestamp: new Date(m.time).getTime(),
value: m[`${parameter}_mean`],
}
m.timestamp = new Date(m.time).getTime()
delete m.time
return m
})
const data = addMissingDataPoints(conformedData, granularity)
return data
return { status: "success", data }
} catch (error) {
console.log(error)
return "error"
}
console.log(error)
return { status: "error" } }
}
}
\ No newline at end of file
//body: "<p>Deze kaart visualiseert data afkomstig van de HOLU-sensorkits én die van officiële meetpunten van <a href='https://www.luchtmeetnet.nl/' target='_blank' rel='noopener noreferrer'>luchtmeetnet.nl</a>. Hollandse&nbsp;Luchten is een experimenteel project. In dit project wordt er onderzoek gedaan naar de kwaliteit van de data afkomstig van de HOLU-sensorkits en naar de beste manier waarop je deze data kunt inzetten.<br />Op dit moment worden de ruwe meetdata van de HOLU-sensorkits getoond. Binnenkort verschijnen ook de gekalibreerde sensorwaarden op de website. Deze waarden zijn o.a. gecorrigeerd voor het effect van hoge luchtvochtigheid. Alle data zijn indicatief en hebben geen juridische betekenis.</p><p><a class='more' href='https://hollandseluchten.waag.org/veelgestelde-vragen/over-luchtkwaliteit/'>meer weten</a></p>"
export const texts = {
about: {
title: "over deze kaart",
body: "<p>Deze kaart toont data van HOLU-sensorkits én die van officiële meetpunten van <a href='https://www.luchtmeetnet.nl/' target='_blank' rel='noopener noreferrer'>luchtmeetnet.nl</a>. Hollandse&nbsp;Luchten is een experimenteel project waarin er onderzoek wordt gedaan naar de kwaliteit van sensorkit-data en hoe je deze het beste kunt inzetten.<br />Op dit moment worden nog <strong>ruwe meetdata</strong> van de HOLU-sensorkits getoond. Deze waarden zijn dus nog niet gecorrigeerd voor o.a. het effect van hoge luchtvochtigheid.<br /><em>Alle data zijn indicatief en hebben geen juridische betekenis.</em></p><p><a class='more' href='https://hollandseluchten.waag.org/veelgestelde-vragen/over-luchtkwaliteit/'>meer weten</a></p>"
body: "<p>Deze kaart toont data van HOLU-sensorkits én die van officiële meetpunten van <a href='https://www.luchtmeetnet.nl/' target='_blank' rel='noopener noreferrer'>luchtmeetnet.nl</a>. Hollandse&nbsp;Luchten is een experimenteel project waarin er onderzoek wordt gedaan naar de kwaliteit van sensorkit-data en hoe je deze het beste kunt inzetten.<br /><em>Alle data zijn indicatief en hebben geen juridische betekenis.</em></p><p><a class='more' href='https://hollandseluchten.waag.org/veelgestelde-vragen/over-luchtkwaliteit/'>meer weten</a></p>"
},
pilotLocations: "Pilot-locaties",
loading: "Bezig met laden...",
loadingError: "Laden mislukt...",
loadingRetry: "Probeer opnieuw",
nodata: "Geen data...",
lastMean: "laatste uurgemiddelde",
nodata: "geen data...",
lastMean: "uurgemiddelde",
lastPeak: "piekwaarde laatste uur",
lastHour: "afgelopen uur",
hoursAgo: "uur geleden",
hour: "uur",
hourShort: "u",
mean: "gemiddelde",
peak: "piekwaarde",
dataStreams: {
raw: "ruwe data",
cal: "gekalibreerde data",
mean: "gemiddelde",
minmax: "min-max waarde",
},
downloadData: "download data",
startDate: "startdatum",
endDate: "einddatum",
chartHeaderAddition: "(uurwaarden)",
timeSelectionHint: "↖ tijdselectie",
downloadCsv: "download csv",
downloadInfo: "<small><a class='more' href='https://hollandseluchten.waag.org/data-downloaden/' target='_blank' rel='noopener noreferrer'>meer info over data downloaden en de API</a></small>",
}
data-on-a-map-app @ bd8510c1
Subproject commit bf8eb3bb403f3e61b41f2c5891b449c7dbe06e71
Subproject commit bd8510c15dbdd324f262ba97439c0d86b7c6fcfd
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment