add aggregate.html
This commit is contained in:
parent
b12aeb139b
commit
46e59006bd
2 changed files with 146 additions and 6 deletions
145
app/aggregate.html
Normal file
145
app/aggregate.html
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
<!doctype html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>openSenseMap Dataview</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans; }
|
||||||
|
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>
|
||||||
|
<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, parseHash())
|
||||||
|
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 parseHash () {
|
||||||
|
const kvPairs = window.location.search
|
||||||
|
// remove leading ?
|
||||||
|
.slice(1)
|
||||||
|
// create objects from kvpairs
|
||||||
|
.split('&')
|
||||||
|
.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']}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadConfig (queryString, reset = true) {
|
||||||
|
if (reset)
|
||||||
|
window.location.search = queryString
|
||||||
|
else
|
||||||
|
window.location.search += queryString
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -13,11 +13,6 @@ var App = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function () {
|
render: function () {
|
||||||
|
|
||||||
const monthStart = new Date('2019-01-01').toISOString();
|
|
||||||
const monthEnd = new Date().toISOString();
|
|
||||||
const monthRainUrl = `https://api.opensensemap.org/statistics/descriptive?boxid=${config.senseBox.id}&from-date=${monthStart}&to-date=${monthEnd}&phenomenon=Niederschlag&window=30d&operation=sum&columns=boxName,unit&format=json&download=false`
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<header className='site-header' role='banner'>
|
<header className='site-header' role='banner'>
|
||||||
|
@ -27,7 +22,7 @@ var App = React.createClass({
|
||||||
<span>
|
<span>
|
||||||
<a target='_blank' href={'https://opensensemap.org/explore/' + config.senseBox.id}>🗺️ Karte</a>
|
<a target='_blank' href={'https://opensensemap.org/explore/' + config.senseBox.id}>🗺️ Karte</a>
|
||||||
<br/>
|
<br/>
|
||||||
<a target='_blank' href={monthRainUrl}>Niederschlag / Monat</a>
|
<a href='./aggregate.html'>Daten-Aggregation</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue