You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
osem_dash/app/aggregate.html

166 lines
5.7 KiB
HTML

<!doctype html>
<head>
<meta charset="utf-8" />
<title>openSenseMap data aggregation</title>
<style>
body {
font-family: sans-serif;
display: flex;
flex-direction: column;
align-items: center;
}
details { padding: 10px; }
th, td { padding: 10px; text-align: right; }
tr:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<h1 id="title">loading ...</h1>
<h4 id="subtitle"></h4>
<div>
<button onclick="loadConfig('&phenomenon=Niederschlag&operation=sum')">Niederschlag</button>
<button onclick="loadConfig('&phenomenon=Temperatur&operation=arithmeticMean')">ø Temperatur</button>
<button onclick="loadConfig('&phenomenon=Temperatur&operation=max')">max. Temperatur</button>
<button onclick="loadConfig('&phenomenon=Temperatur&operation=min')">min. Temperatur</button>
<button onclick="loadConfig('&phenomenon=PM2.5&operation=max')">max. Feinstaub</button>
</div>
<div>
<button onclick="loadConfig('&windowSize=30 days', reset=false)">Monat</button>
<button onclick="loadConfig('&windowSize=1 week', reset=false)">Woche</button>
<button onclick="loadConfig('&windowSize=1 day', reset=false)">Tag</button>
<button onclick="loadConfig('&windowSize=1 hour', reset=false)">Stunde</button>
</div>
<details>
<summary>Advanced Usage</summary>
<p>
Shown data can be modified through the URL search query. Supported keys are:
<code>boxId, phenomenon, start, end, windowSize, operation</code>
For supported values check out the <a href="https://docs.opensensemap.org/#api-Statistics-descriptive" target="_blank">openSenseMap API docs</a>.
</p>
<p>
Example:
<code>
?boxId=5b26181b1fef04001b69093c&amp;phenomenon=Temperatur&amp;operation=max&amp;windowSize=14 days&amp;start=2019-10-01T00:00:00.000Z
</code>
</p>
</details>
<br/>
<div id="status"></div>
<table id="datatable"></table>
<script>
const defaultConfig = {
boxId: '5b26181b1fef04001b69093c',
phenomenon: 'Niederschlag',
operation: 'sum',
start: new Date('2019-04-01').toISOString(),
end: new Date().toISOString(),
windowSize: '30 days',
}
const config = Object.assign(defaultConfig, parseQuery())
main(config).catch(console.error)
async function main (config) {
try {
const data = await fetchOsemDataAggregate(config)
const table = document.querySelector('#datatable')
populateTable(table, data, config)
const title = `${data['boxName']} - ${data['phenomenon']}`
document.querySelector('#title').innerText = title
document.title = title
const subtitle = `${config.operation} per ${config.windowSize}`
document.querySelector('#subtitle').innerText = subtitle
} catch (err) {
document.querySelector('#status').innerText = err
}
}
function parseQuery () {
const kvPairs = window.location.search
.slice(1) // remove leading ?
.split('&') // create list of { key: value } objects
.map(kv => {
[k,v] = kv.split('=')
return { [k]: decodeURIComponent(v) }
})
return Object.assign.apply(null, kvPairs) // combine objects
}
async function fetchOsemDataAggregate (config) {
const {
boxId,
phenomenon,
operation,
start,
end,
windowSize,
} = config
const url = `https://api.opensensemap.org/statistics/descriptive?boxid=${boxId}&from-date=${start}&to-date=${end}&phenomenon=${phenomenon}&window=${windowSize}&operation=${operation}&columns=boxName,unit,phenomenon&format=json&download=false`
const res = await fetch(url)
return (await res.json())[0]
}
function populateTable (table, data, config) {
const dateFormat = d => new Date(d).toLocaleDateString('de')
const valFormat = v => Math.round(v * (10 ** 2)) / (10 ** 2)
const addRow = (left, right) => {
const row = document.createElement('tr')
row.innerHTML = `<td>${left}</td><td>${right}</td>`
table.appendChild(row)
}
const dates = Object.keys(data).filter(k => k.startsWith('20')).sort()
for (let i = 0; i < dates.length; i++) {
const dateFrom = dates[i]
const dateTo = dates[i + 1] || new Date()
const val = data[dates[i]]
const left = `${dateFormat(dateFrom)} - ${dateFormat(dateTo)}`
const right = `${valFormat(val)} ${data['unit']}`
addRow(left, right)
}
// add aggregate of all values
let aggregate = null
switch (config.operation) {
case 'sum':
aggregate = dates.map(d => data[d]).reduce((s, v) => s += v, 0)
break
case 'arithmeticMean':
aggregate = dates.map(d => data[d]).reduce((s, v) => s += v, 0) / dates.length
break
case 'max':
aggregate = Math.max.apply(null, dates.map(d => data[d]))
break
case 'min':
aggregate = Math.min.apply(null, dates.map(d => data[d]))
break
}
if (aggregate)
addRow(config.operation, `${valFormat(aggregate)} ${data['unit']}`)
}
// click handler of the UI buttons.
function loadConfig (queryString, reset = true) {
if (reset)
window.location.search = queryString // this assignment reloads the page
else
window.location.search += queryString
}
</script>
</body>
</html>