Alte Version abgeändert auf neue Datebank /-Struktur).
This commit is contained in:
860
routes/apidata.js
Normal file
860
routes/apidata.js
Normal file
@@ -0,0 +1,860 @@
|
||||
"use strict";
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const moment = require('moment');
|
||||
const mathe = require('mathjs');
|
||||
const fs = require('fs');
|
||||
const $ = require('jquery');
|
||||
const util = require('./utilities');
|
||||
|
||||
|
||||
// Mongo wird in app.js geöffnet und verbunden und bleibt immer verbunden !!
|
||||
|
||||
// Get the city for given Sensor
|
||||
async function getCity(db, sensorid) {
|
||||
let pcoll = db.collection("properties");
|
||||
let properties = await pcoll.findOne({_id:sensorid});
|
||||
let addr = "unKnown";
|
||||
try {
|
||||
addr = properties.location[0].address.country + " " +
|
||||
properties.location[0].address.plz + " " +
|
||||
properties.location[0].address.city;
|
||||
}
|
||||
catch(e) {
|
||||
// do nothing, just skip
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
// API to put data into dBase
|
||||
router.post('/putdata/:what', function(req,res) {
|
||||
let db = req.app.get('dbase');
|
||||
let cmd = req.query.cmd;
|
||||
let what = req.params.what;
|
||||
if (what=='problems') {
|
||||
putAPIproblemdata(db, cmd, req.body)
|
||||
.then((erg) => {
|
||||
// console.log(erg);
|
||||
res.send(erg);
|
||||
});
|
||||
} else {
|
||||
res.send ( {error:'wrong call'})
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
//API to read all datas from the database
|
||||
router.get('/getdata', function (req, res) {
|
||||
let db = req.app.get('dbase');
|
||||
let sid=1;
|
||||
if (!((req.query.sensorid == undefined) || (req.query.sensorid == ""))) {
|
||||
sid = parseInt(req.query.sensorid);
|
||||
}
|
||||
let avg = req.query.avg;
|
||||
let span = req.query.span;
|
||||
let dt = req.query.datetime;
|
||||
if(isNaN(sid)) {
|
||||
getAPIdataTown(db, req.query.sensorid, avg, span, dt, res)
|
||||
.then(erg => res.json(erg));
|
||||
} else {
|
||||
getAPIdataSensor(db, sid, avg, span, dt)
|
||||
.then(erg => res.json(erg));
|
||||
}
|
||||
|
||||
// if(req.query.sensorid == "all") {
|
||||
// getAPIalldata(db, dt)
|
||||
// .then(erg => res.json(erg));
|
||||
});
|
||||
|
||||
router.get('/getprops', function (req, res) {
|
||||
let db = req.app.get('dbase');
|
||||
let sid=0;
|
||||
if (!((req.query.sensorid == undefined) || (req.query.sensorid == ""))) {
|
||||
sid = parseInt(req.query.sensorid);
|
||||
}
|
||||
let dt = "1900-01-01T00:00:00";
|
||||
if(!((req.query.since === undefined) || (req.query.since ==""))) {
|
||||
dt = req.query.since;
|
||||
}
|
||||
let name = ""
|
||||
if(!((req.query.sensortyp === undefined) || (req.query.sensortyp ==""))) {
|
||||
name = req.query.sensortyp;
|
||||
}
|
||||
|
||||
getAPIprops(db, sid, name, dt)
|
||||
.then(erg => res.json(erg));
|
||||
});
|
||||
|
||||
|
||||
router.get('/getmapsensors', function (req, res) {
|
||||
let db = req.app.get('dbase'); // db wird in req übergeben (von app.js)
|
||||
let bounds = {};
|
||||
bounds.south = parseFloat(req.query.south);
|
||||
bounds.north = parseFloat(req.query.north);
|
||||
bounds.east = parseFloat(req.query.east);
|
||||
bounds.west = parseFloat(req.query.west);
|
||||
bounds.poly = [];
|
||||
if (req.query.poly != undefined) {
|
||||
bounds.poly = JSON.parse(req.query.poly);
|
||||
}
|
||||
let ptype = parseInt(req.query.ptype);
|
||||
let stype = req.query.stype;
|
||||
let st = req.query.start;
|
||||
|
||||
getApiMapSensors(db, bounds, stype, ptype, st)
|
||||
.then(erg => res.json(erg));
|
||||
});
|
||||
|
||||
router.get('/getcities', (req,res) => {
|
||||
let db = req.app.get('dbase'); // db wird in req übergeben (von app.js)
|
||||
let country = req.query.country;
|
||||
let type = req.query.type;
|
||||
if (country == undefined) {
|
||||
country = 'all';
|
||||
}
|
||||
if (type == undefined) {
|
||||
type = 'PM';
|
||||
}
|
||||
getApiCities(db,country.toUpperCase(),type.toUpperCase())
|
||||
.then(erg => res.json(erg));
|
||||
});
|
||||
|
||||
// Get address from coordinates using OpenStreetMap Nominatim API
|
||||
router.get('/getaddress', function (req, res) {
|
||||
let db = req.app.get('dbase');
|
||||
let sid = 0;
|
||||
if (!((req.query.sensorid == undefined) || (req.query.sensorid == ""))) {
|
||||
sid = parseInt(req.query.sensorid);
|
||||
}
|
||||
|
||||
if (sid === 0) {
|
||||
res.json({address: null, err: "No sensorid provided"});
|
||||
return;
|
||||
}
|
||||
|
||||
util.getAddress(db, sid)
|
||||
.then(erg => res.json(erg))
|
||||
.catch(err => {
|
||||
console.log("getaddress error:", err);
|
||||
res.json({address: null, err: err.message});
|
||||
});
|
||||
});
|
||||
|
||||
// ***********************************************************
|
||||
// putAPIproblemdata - Daten in der DB speichern
|
||||
//
|
||||
// Parameter:
|
||||
// db: Mongo-Database
|
||||
// cmd: 'start', 'end', 'data'
|
||||
// data: JSON string to put into db
|
||||
//
|
||||
// return:
|
||||
// error, if not correctly saved, else null
|
||||
// ***********************************************************
|
||||
/*
|
||||
async function putAPIproblemdata(db, cmd, data) {
|
||||
// console.log("putAPIproblemdata"," Länge: ", data.length);
|
||||
let collection = db.collection('problemsensors');
|
||||
if(cmd == 'end') {
|
||||
return {error: 'done'};
|
||||
}
|
||||
if(cmd == 'data') {
|
||||
let inserted;
|
||||
let upd = [];
|
||||
for (let i=0; i< data.length; i++){
|
||||
let one = { updateOne: { "filter" : { "_id": data[i]._id}, "update": { $set: data[i]}, "upsert": true } };
|
||||
upd.push(one);
|
||||
}
|
||||
try {
|
||||
inserted = await collection.bulkWrite(upd)
|
||||
// console.log("Modifiziert:", inserted.modifiedCount)
|
||||
}
|
||||
catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
return {error: "OK"}
|
||||
}
|
||||
return { error: 'wrong command'};
|
||||
}
|
||||
*/
|
||||
// ***********************************************************
|
||||
// getAPIprobSensors - Get data for problematic sensors
|
||||
//
|
||||
// Parameter:
|
||||
// db: Mongo-Database
|
||||
//
|
||||
// return:
|
||||
// JSON Dokument mit den angefragten Werten
|
||||
// ***********************************************************
|
||||
/*
|
||||
async function getAPIprobSensors(db,pnr,only,withTxt) {
|
||||
let coll = db.collection('problemsensors');
|
||||
let query = {_id: {$gt: 0}};
|
||||
let proj = {};
|
||||
let count = 0;
|
||||
if(withTxt == undefined) {
|
||||
withTxt = true;
|
||||
}
|
||||
if (pnr != 0) {
|
||||
query = { $and: [ {problemNr: pnr}, {_id: {$gt: 0}} ]} ;
|
||||
}
|
||||
if(only) {
|
||||
proj = {_id: 1};
|
||||
}
|
||||
let docs = await coll.find(query,proj).toArray();
|
||||
if(docs != null) {
|
||||
count = docs.length;
|
||||
}
|
||||
let texte = {};
|
||||
if(withTxt) {
|
||||
let tt = await coll.findOne({_id: 0});
|
||||
if (tt == null) {
|
||||
texte.texte = [];
|
||||
}
|
||||
}
|
||||
let ret;
|
||||
if (only) {
|
||||
ret = {count: count, problemNr: pnr, values: docs, texte: texte.texte};
|
||||
} else {
|
||||
ret = {count: count, values: docs, texte: texte.texte};
|
||||
}
|
||||
if(!withTxt) {
|
||||
delete ret.texte;
|
||||
}
|
||||
return ret
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// ***********************************************************
|
||||
// getAPIdataNew - Get data direct via API for one sensor
|
||||
//
|
||||
// Parameter:
|
||||
// db: Mongo-Database
|
||||
// sid: sensor ID
|
||||
// mavg: time over that to build the average [minutes]
|
||||
// dauer: duration for the data [hours]
|
||||
// start: starting point of 'dauer'
|
||||
// end: end of 'dauer'
|
||||
//
|
||||
// return:
|
||||
// JSON Dokument mit den angefragten Werten
|
||||
// ***********************************************************
|
||||
/*
|
||||
async function getAPIdataNew(db,sid,mavg,dauer,start,end, gstart) {
|
||||
let st = moment(start).startOf('day'); // clone start/end ..
|
||||
let en = moment(end).startOf('day'); // .. and set to start of day
|
||||
|
||||
let retur = {sid: sid, avg: mavg, span: dauer, start: gstart};
|
||||
let collection = db.collection('values');
|
||||
let ergArr = [];
|
||||
let values;
|
||||
for (; st <= en; st.add(1, 'd')) {
|
||||
let id = sid + '_' + st.format('YYYYMMDD');
|
||||
try {
|
||||
values = await collection.findOne({
|
||||
_id: id
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if(values && (values.values.length != 0)) {
|
||||
ergArr.push(...values.values);
|
||||
}
|
||||
}
|
||||
if (ergArr.length == 0) {
|
||||
retur.count = 0;
|
||||
retur['values'] = [];
|
||||
} else {
|
||||
// Bereich einschränken
|
||||
let v = [];
|
||||
let fnd = ergArr.findIndex(x => x.datetime >= start);
|
||||
if (fnd != -1) {
|
||||
v = ergArr.slice(fnd);
|
||||
ergArr = v;
|
||||
}
|
||||
fnd = ergArr.findIndex(x => x.dateTime > end);
|
||||
if (fnd != -1) {
|
||||
v = ergArr.slice(-fnd);
|
||||
erg.Arr = v;
|
||||
}
|
||||
if ((mavg === undefined) || (mavg == 1)) {
|
||||
retur.count = ergArr.length;
|
||||
retur['values'] = ergArr;
|
||||
}
|
||||
// Mittelwert berechnen
|
||||
let x = util.calcMovingAverage(db, ergArr, mavg, 0, 0, true);
|
||||
fnd = x.findIndex(u => u.dt >= gstart);
|
||||
if((fnd == -1) && (dauer == 0)) {
|
||||
let y = x.slice(-1);
|
||||
x = y;
|
||||
} else {
|
||||
if (fnd != -1) {
|
||||
let y = x.slice(fnd);
|
||||
x = y;
|
||||
}
|
||||
}
|
||||
retur.count = x.length;
|
||||
retur.values = x;
|
||||
}
|
||||
return retur;
|
||||
}
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// getAPITN - Get data direct via API for all sensors in a town
|
||||
//
|
||||
// Parameter:
|
||||
// dbase: Mongo-Database
|
||||
// sensors: array of sensors
|
||||
// mavg: time over that to build the average [minutes]
|
||||
// dauer: duration for the data [hours]
|
||||
// start: starting point of 'dauer'
|
||||
// end: end of 'dauer'
|
||||
// town: name of town
|
||||
//
|
||||
// return:
|
||||
// JSON document with data for ALL sensors in town
|
||||
//
|
||||
// ***** Neue DB-Struktur - Versuch
|
||||
//
|
||||
// ********************************************************************
|
||||
async function getAPITN (dbase,sensors,mavg,dauer,start,end,gstart,town) {
|
||||
// Fetch for all this sensors
|
||||
let los = moment(); // debug, to time it
|
||||
let erg = {sid:town, avg: mavg, span: dauer, start: gstart, count: 0, sensordata: []}; // prepare object
|
||||
let val;
|
||||
for(let j=0; j<sensors.length; j++) { // loop thru array of sensors
|
||||
try {
|
||||
val = await getAPIdata(dbase,sensors[j],mavg,dauer,start,end,gstart); // get data for obe sensor
|
||||
if(val.count != 0) { // if there is data
|
||||
delete val.avg; // delete unnecessary elements
|
||||
delete val.span;
|
||||
delete val.start;
|
||||
erg.sensordata.push(val); // and push data to result array
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
console.log("Zeit in getAPIdataTown:",(moment()-los)/1000,'[sec]'); // time it
|
||||
console.log('Daten für',erg.sensordata.length,' Sensoren gelesen');
|
||||
erg.count = erg.sensordata.length; // save count
|
||||
return erg; // and return all data
|
||||
}
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
// getAPIdataTown - Get data direct via API for all sensors in a town
|
||||
//
|
||||
// Call:
|
||||
// http://feinstaub.rexfue.de/api/getdata/?sensorid=stuttgart&avg=5&span=12
|
||||
//
|
||||
// mit:
|
||||
// sensorid: Name der Stadt
|
||||
// avg: Mittelwert-Bildung über xxx Minuten
|
||||
// span: Zeitraum für die Mittelwertbildung in Stunden
|
||||
// dt: Startzeitpunkt
|
||||
//
|
||||
// Parameter:
|
||||
// db: Mongo-Database
|
||||
// town: name of town
|
||||
// avg: time over that to build the average [minutes]
|
||||
// span: duration for the data [hours]
|
||||
// dt: starting point of 'span'
|
||||
// res: http-object to send result
|
||||
//
|
||||
// return:
|
||||
// nothing; JSON document will be sent back
|
||||
//
|
||||
//
|
||||
// For every town, there has to be an JSON-file with the
|
||||
// sensornumbers of ervery sensor living in that town.
|
||||
//
|
||||
// ***** Neue DB-Struktur - Versuch
|
||||
//
|
||||
// ********************************************************************
|
||||
async function getAPIdataTown(db, town, avg, span, dt, res) {
|
||||
// get sensors for the town as array of ids
|
||||
let p = parseParams(avg, span, dt);
|
||||
// get sensor numbers from town-sensor-file
|
||||
let sensors = [];
|
||||
let tw = town.toLowerCase();
|
||||
let data = fs.readFileSync(tw+'.txt');
|
||||
sensors = JSON.parse(data);
|
||||
|
||||
return getAPITN (db,sensors,p.mavg,p.dauer,p.start,p.end,p.gstart, town);
|
||||
}
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
// getAPIdataSenssor - Get data direct via API for all sensors in a town
|
||||
//
|
||||
// Call:
|
||||
// http://feinstaub.rexfue.de/api/getdata/?sensorid=140&avg=5&span=12&datetime=2018-08-ß02T20:12:00
|
||||
//
|
||||
// mit:
|
||||
// sensorid: ID des gewümschten Sensors
|
||||
// avg: Mittelwert-Bildung über xxx Minuten
|
||||
// span: Zeitraum für die Mittelwertbildung in Stunden
|
||||
// datetime: Startzeitpunkt
|
||||
//
|
||||
// Parameter:
|
||||
// db: Datenbank
|
||||
// sid: ID of sensor
|
||||
// avg: time over that to build the average [minutes]
|
||||
// span: duration for the data [hours]
|
||||
// dt: starting point of 'span'
|
||||
//
|
||||
// return:
|
||||
// nothing; JSON document will be sent back
|
||||
//
|
||||
//
|
||||
// ***** Neue DB-Struktur - Versuch
|
||||
//
|
||||
// ********************************************************************
|
||||
async function getAPIdataSensor(db, sid, avg, span, dt) {
|
||||
let p = parseParams(avg, span, dt);
|
||||
return getAPIdata(db,sid,p.mavg,p.dauer,p.start,p.end,p.gstart)
|
||||
}
|
||||
|
||||
|
||||
// *********************************************
|
||||
// Get data direct via API for one sensor
|
||||
//
|
||||
// Call:
|
||||
// http://feinstaub.rexfue.de/api?sid=1234&avg=5&span=24
|
||||
//
|
||||
// mit:
|
||||
// sid: Sensornummer
|
||||
// avg: Mittelwert-Bildung über xxx Minuten
|
||||
// span: Zeitraum für die Mittelwertbildung in Stunden
|
||||
//
|
||||
// return:
|
||||
// JSON Dokument mit den angefragten Werten
|
||||
// *********************************************
|
||||
async function getAPIdata(db, sid, mavg, dauer, start, end, gstart) {
|
||||
let values = [];
|
||||
let retur = {sid: sid, avg: mavg, span: dauer, start: gstart};
|
||||
|
||||
// First, determine sensor type from properties
|
||||
let pcoll = db.collection("properties");
|
||||
let props = await pcoll.findOne({_id: sid});
|
||||
if (!props) {
|
||||
retur.count = 0;
|
||||
retur['values'] = [];
|
||||
retur.error = 'Sensor not found';
|
||||
return retur;
|
||||
}
|
||||
|
||||
// Determine collection based on type
|
||||
let collectionName;
|
||||
if (props.type === 'radioactivity') {
|
||||
collectionName = 'radioactivity_sensors';
|
||||
} else {
|
||||
// Assume THP for any other type
|
||||
collectionName = 'thp_sensors';
|
||||
}
|
||||
|
||||
let collection = db.collection(collectionName);
|
||||
try {
|
||||
values = await collection.find(
|
||||
{
|
||||
sensorid: sid,
|
||||
datetime: {
|
||||
$gte: new Date(start),
|
||||
$lt: new Date(end)
|
||||
}
|
||||
},
|
||||
{
|
||||
projection: {_id: 0},
|
||||
sort: {datetime: 1}
|
||||
}
|
||||
).toArray()
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if(values.length == 0) {
|
||||
retur.count = 0;
|
||||
retur['values'] = [];
|
||||
} else {
|
||||
if((mavg===undefined) || (mavg == 1)) {
|
||||
retur.count = values.length;
|
||||
retur['values'] = values;
|
||||
}
|
||||
let x = await util.calcMovingAverage(db, sid, values, mavg, true);
|
||||
let fnd = x.findIndex(u => u.dt >= gstart);
|
||||
if((fnd == -1) && (dauer == 0)) {
|
||||
let y = x.slice(-1);
|
||||
x = y;
|
||||
} else {
|
||||
if (fnd != -1) {
|
||||
let y = x.slice(fnd);
|
||||
x = y;
|
||||
}
|
||||
}
|
||||
retur.count = x.length;
|
||||
retur.values = x;
|
||||
}
|
||||
return retur;
|
||||
}
|
||||
|
||||
/* ===============================================================
|
||||
// PM (FEINSTAUB) FUNCTIONS REMOVED - Not needed in new DB
|
||||
// ===============================================================
|
||||
|
||||
// *********************************************
|
||||
// Get data direct via API for ALL sensor - WAS PM-SPECIFIC
|
||||
//
|
||||
// Call:
|
||||
// http://feinstaub.rexfue.de/api?sid=all&datetime="2018-06-02T12:00Z"
|
||||
//
|
||||
// mit:
|
||||
// dt: Zeitpunkt, für den die Daten geholt werden
|
||||
// Es werden Daten <= dem Zeitpunkt geholt
|
||||
//
|
||||
// return:
|
||||
// JSON Dokument mit den angefragten Werten
|
||||
// *********************************************
|
||||
async function getAPIalldata(db,dt) {
|
||||
// REMOVED - Was PM-specific, not applicable to Radiation/THP sensors
|
||||
return { error: 'Function removed - was PM-specific' };
|
||||
}
|
||||
|
||||
function isPM(name) {
|
||||
// REMOVED - No longer needed, only Radiation and THP sensors
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// *********************************************
|
||||
// Get properties for all sensors
|
||||
//
|
||||
// Call:
|
||||
// http://feinstaub.rexfue.de/api/getprops?sensorid=1234&since=2810-03-23&sensortyp=SDS011
|
||||
//
|
||||
// mit:
|
||||
// sid: Sensornummer (all -> alle Sensoren)
|
||||
// since: seit dem Datum (incl)
|
||||
// sensortyp: Type des Sensors (z.B. SDS011 oder PM(für alle Feinstaub-Sensoren))
|
||||
//
|
||||
// params:
|
||||
// db Datenbank
|
||||
// sid Sensor-Nummer oder all
|
||||
// typ Sensor-Typ
|
||||
// dt Datum, ab wann gesucht werden soll
|
||||
//
|
||||
// return:
|
||||
// JSON Dokument mit den angefragten werten
|
||||
// *********************************************
|
||||
async function getAPIprops(db,sid,typ,dt) {
|
||||
let properties = [];
|
||||
let erg = [];
|
||||
let entry = {};
|
||||
let pcoll = db.collection("properties");
|
||||
let query = {};
|
||||
if(sid == 0) {
|
||||
if(typ == "") {
|
||||
query = {}; // Get all sensors
|
||||
} else if (typ == 'radioactivity') {
|
||||
query = {type: 'radioactivity'};
|
||||
} else if (typ == 'THP') {
|
||||
query = {type: {$ne: 'radioactivity'}}; // Anything not radioactivity is THP
|
||||
} else {
|
||||
// For specific sensor type names, check in name array
|
||||
query = {'name.name': typ};
|
||||
}
|
||||
} else {
|
||||
query = { _id:sid };
|
||||
}
|
||||
properties = await pcoll.find(query).sort({_id: 1}).toArray();
|
||||
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
let loclast = (properties[i].location.length)-1;
|
||||
|
||||
// Get current name from array
|
||||
let currentName = properties[i].name;
|
||||
if (Array.isArray(properties[i].name)) {
|
||||
currentName = properties[i].name[properties[i].name.length - 1].name;
|
||||
}
|
||||
|
||||
// Get last_seen from values.timestamp if available
|
||||
let lastSeen = properties[i].last_seen || (properties[i].values && properties[i].values.timestamp);
|
||||
if (!lastSeen) {
|
||||
lastSeen = moment("1900-01-01T00:00Z");
|
||||
} else if (lastSeen.$date) {
|
||||
lastSeen = moment(lastSeen.$date);
|
||||
} else {
|
||||
lastSeen = moment(lastSeen);
|
||||
}
|
||||
|
||||
// Build result object
|
||||
let result = {
|
||||
sid: properties[i]._id,
|
||||
typ: currentName,
|
||||
lat: properties[i].location[loclast].loc.coordinates[1],
|
||||
lon: properties[i].location[loclast].loc.coordinates[0],
|
||||
alt: properties[i].location[loclast].altitude,
|
||||
lastSeen: lastSeen.format(),
|
||||
country: properties[i].location[loclast].country
|
||||
};
|
||||
|
||||
// Add since date if available
|
||||
if (properties[i].location[loclast].since) {
|
||||
if (properties[i].location[loclast].since.$date) {
|
||||
result.since = moment(properties[i].location[loclast].since.$date).format();
|
||||
} else {
|
||||
result.since = moment(properties[i].location[loclast].since).format();
|
||||
}
|
||||
}
|
||||
|
||||
erg.push(result);
|
||||
}
|
||||
entry.sensortyp = typ =="" ? "all" : typ;
|
||||
entry.count = erg.length;
|
||||
entry.since = dt;
|
||||
entry.values = erg;
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
// *******************************************************************
|
||||
// parseParams - parse the given paramaters
|
||||
//
|
||||
// params:
|
||||
// avg: averegae time in min
|
||||
// span: data range in hours
|
||||
// dt: start date of data range
|
||||
//
|
||||
// return:
|
||||
// object with:
|
||||
// mavg: average time in min (default 1min, max; 1440min))
|
||||
// dauer: data range in hoiurs (default 24h, max: 720h)
|
||||
// start: start date/time to calculate average
|
||||
// end: end of data range
|
||||
// gstart: start on datarange (without avg-time)
|
||||
//
|
||||
// **********************************************************************
|
||||
function parseParams(avg, span, dt) {
|
||||
let params = {};
|
||||
params.mavg = 1; // default average
|
||||
if (avg !== undefined) { // if acg defined ..
|
||||
params.mavg = parseInt(avg); // .. use it
|
||||
}
|
||||
if (params.mavg > 1440) { params.mavg = 1440;} // avgmax = 1 day
|
||||
params.dauer = 24; // span default 1 day
|
||||
if(span !== undefined) { // if defined ..
|
||||
params.dauer = parseInt(span); // .. use it
|
||||
}
|
||||
if (params.dauer > 720) { params.dauer = 720;} // spanmax = 30 days
|
||||
params.start = moment(); // default start -> now
|
||||
params.end = moment(); // define end
|
||||
if(dt != undefined) { // if defined ..
|
||||
params.start = moment(dt); // .. use it ..
|
||||
params.end = moment(dt).add(params.dauer,'h'); // .. and calculate new end
|
||||
} else { // if not defined, calc start ..
|
||||
params.start.subtract(params.dauer, 'h'); // .. from span (= dauer)
|
||||
}
|
||||
params.gstart = moment(params.start);
|
||||
params.start.subtract(params.mavg,'m'); // start earlier to calc right average
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// getAPIproblemSensors - Get senosor-IDs of problematic sensor
|
||||
// within map bounds
|
||||
//
|
||||
// Call:
|
||||
// http://feinstaub.rexfue.de/api/getprobsens/?bounds=[bounds]&ptype=1&datetime=2018-08-ß02T20:12:00
|
||||
//
|
||||
// mit:
|
||||
// bounds: corner coordinates of map or polygone for town border
|
||||
// ptype: type of problem (optional)
|
||||
// datetime: day for which to calculate (optional)
|
||||
//
|
||||
// Parameter:
|
||||
// db: database
|
||||
// bounds: corner coordinates of map or polygone for town border
|
||||
// stype: 'PM' or 'THP' or 'TH' os 'T' or 'H' (default: 'PM')
|
||||
// ptype: type of problem (undefined means ALL problems)
|
||||
// st: date to calculate for
|
||||
//
|
||||
// return:
|
||||
// array wit 24h-Average-Value for the last 24hours for every sensor
|
||||
//
|
||||
//
|
||||
//
|
||||
// ********************************************************************
|
||||
async function getApiMapSensors(db, bounds, stype, ptype, st) {
|
||||
// fetch list of sensor ids within bounds
|
||||
let slist = [];
|
||||
let collection = db.collection('properties');
|
||||
let loc;
|
||||
let name = 'PM';
|
||||
if (stype != undefined) {
|
||||
name = stype;
|
||||
}
|
||||
if (bounds.poly.length != 0) {
|
||||
loc = {
|
||||
'location.0.loc': {
|
||||
$geoWithin: {
|
||||
$geometry: {
|
||||
type: "Polygon",
|
||||
coordinates: [bounds.poly],
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loc = {
|
||||
'location.0.loc': {
|
||||
$geoWithin: {
|
||||
$box: [
|
||||
[bounds.west, bounds.south],
|
||||
[bounds.east, bounds.north]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let docs = await collection.find(loc, {_id: 1, name:1}).toArray(); // find all data within map borders (box)
|
||||
// .toArray(async function (err, docs) {
|
||||
// console.log(docs);
|
||||
for (let i = docs.length - 1; i >= 0; i--) {
|
||||
if (!((name == 'PM') == isPM(docs[i].name))) {
|
||||
docs.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
let erg = await getAPIdataSensor(db, docs[i]._id, 1, 24);
|
||||
|
||||
if (erg.values.length == 0) {
|
||||
docs[i].mean = -1;
|
||||
docs[i].std = 0;
|
||||
docs[i].type = 'noData';
|
||||
delete docs[i].name;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (!erg.values[0].hasOwnProperty('P1')) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// console.log(erg);
|
||||
let result = erg.values.map(x => x.P1);
|
||||
// console.log(result);
|
||||
let mean = mathe.mean(result);
|
||||
let std = mathe.std(result);
|
||||
console.log(mean);
|
||||
docs[i].mean = Math.round(mean);
|
||||
docs[i].std = std;
|
||||
delete docs[i].name;
|
||||
}
|
||||
// console.log(docs);
|
||||
// nun in docs alle Mittelwerte über die letzten 24h
|
||||
// nun sortieren
|
||||
let docs_sorted = docs.sort((a, b) => a.mean - b.mean);
|
||||
let docs_std = docs.sort((a, b) => a.std - b.std);
|
||||
// console.log(docs_sorted);
|
||||
return docs_sorted;
|
||||
}
|
||||
|
||||
function isPM(name) {
|
||||
let pms = ['SDS011','PMS7003','PMS3003','PMS5003','HPM','SDS021','PPD42NS'];
|
||||
if (pms.findIndex(n => n == name) != -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
// getAPICities - Get all cities, containing sensors
|
||||
//
|
||||
//
|
||||
// Call:
|
||||
// http://feinstaub.rexfue.de/api/getcities/?country=de
|
||||
//
|
||||
// with:
|
||||
// county: 2 character coutrycode to find the city (all or absent => world)
|
||||
// type: PM or THP (or absent => PM)
|
||||
//
|
||||
// Parameter:
|
||||
// db: database
|
||||
// county: 2 character coutrycode to find the citie (all => world)
|
||||
// pm: PM for particulate sensors, THP for temp/hum/press-sensors
|
||||
//
|
||||
// return:
|
||||
// JSON array with cities
|
||||
//
|
||||
//
|
||||
//
|
||||
// ********************************************************************
|
||||
async function getApiCities(db, country, sensorType) {
|
||||
let slist = [];
|
||||
let collection = db.collection('properties');
|
||||
let query = {};
|
||||
|
||||
// Build country query - in new DB, country is directly in location, not in address
|
||||
if(country != 'ALL') {
|
||||
query = {'location.country': country};
|
||||
}
|
||||
|
||||
// Filter by sensor type
|
||||
let matchtype = {};
|
||||
if (sensorType == 'radioactivity') {
|
||||
matchtype = {type: 'radioactivity'};
|
||||
} else if (sensorType == 'THP') {
|
||||
matchtype = {type: {$ne: 'radioactivity'}}; // Anything not radioactivity
|
||||
}
|
||||
// If sensorType is not specified or something else, get all
|
||||
|
||||
let docs = await collection.aggregate([
|
||||
{$match: matchtype},
|
||||
{ $match: query},
|
||||
{ $project: {
|
||||
_id:1,
|
||||
name:1,
|
||||
location: {
|
||||
'$map': {
|
||||
input: '$location',
|
||||
as: 'm',
|
||||
in: {
|
||||
country: '$$m.address.country',
|
||||
city: '$$m.address.city',
|
||||
plz: '$$m.address.plz'
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
{ $unwind: '$location'},
|
||||
{ $group: {
|
||||
sensorids: { $addToSet: '$_id'},
|
||||
_id: '$location.city',
|
||||
plz: { $addToSet: '$location.plz'},
|
||||
country: { $first: '$location.country' }
|
||||
}},
|
||||
{ $project: {
|
||||
_id: 0,
|
||||
city: '$_id',
|
||||
sensors: { count: {$size: '$sensorids'}, ids : '$sensorids'},
|
||||
plz: '$plz',
|
||||
country: '$country',
|
||||
}}
|
||||
]).toArray();
|
||||
// console.log(docs);
|
||||
return { count: docs.length, cities: docs };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
||||
module.exports.api = { getCity };
|
||||
|
||||
134
routes/data.js
Normal file
134
routes/data.js
Normal file
@@ -0,0 +1,134 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
var moment = require('moment');
|
||||
|
||||
// Get latest 10 readings out from the database
|
||||
router.get('/latest10', function(req,res) {
|
||||
var collection = req.strom;
|
||||
collection.find({},{limit:5, sort: { date: -1}},function(e,docs) {
|
||||
res.json(docs);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Get readings for the last hour out from the database
|
||||
router.get('/onehour', function(req,res) {
|
||||
var db = req.db;
|
||||
var collection = db.get('strom');
|
||||
var st = req.query.start;
|
||||
st = st.substring(0,st.length-1);
|
||||
var start = moment(st);
|
||||
var end = moment(st);
|
||||
start.subtract(1,'h');
|
||||
collection.find({date: { $gte: new Date(start), $lt: new Date(end)}},{sort: { date: 1} },function(e,docs) {
|
||||
// console.dir(docs);
|
||||
res.json(docs);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
router.get('/oneyear', function(req,res) {
|
||||
var year = req.query.year;
|
||||
if(year == undefined) {
|
||||
year = moment().year();
|
||||
}
|
||||
var st = req.query.start;
|
||||
st = st.substring(0,10);
|
||||
// console.log("st: " + st);
|
||||
var start = moment(st);
|
||||
var db = req.db;
|
||||
var collection = db.get('stromDay');
|
||||
var sDat = moment("01-01-"+year, "MM-DD-YYYY"); // Start-Datum 1.1.year
|
||||
var curDat = moment();
|
||||
var erg = []; // hier die Werte sammeln
|
||||
var last = { date: 0, wert: 0, cnt: 0};
|
||||
var first=0;
|
||||
// console.log("Start " + start.format());
|
||||
start.subtract(30,'days'); // Daten für 30 Tage holen
|
||||
// console.log("Start-30 " + start.format());
|
||||
collection.find( { date: { $gte: new Date(start) }}, {sort: { date: 1} },function(err,docs){
|
||||
if(err) {
|
||||
console.log("StromM-Error: ");
|
||||
console.dir(err);
|
||||
} else {
|
||||
var lang = (docs.length > 31) ? 31 : docs.length;
|
||||
for(var i=0; i<lang; i++) {
|
||||
// console.log(i + " " + docs[i].date);
|
||||
if(last.date != 0) {
|
||||
// console.log("last.date: " + last.date);
|
||||
erg.push({ date: last.date, wert: (docs[i].zstand - last.wert)});
|
||||
if( (docs[i].zstand - last.wert) != 0) {
|
||||
last.cnt++;
|
||||
}
|
||||
} else {
|
||||
first = docs[i].zstand;
|
||||
}
|
||||
last.date = docs[i].date;
|
||||
last.wert = docs[i].zstand;
|
||||
}
|
||||
}
|
||||
// console.log(erg);
|
||||
var avg = (last.wert - first)/last.cnt;
|
||||
res.send ({ average: avg, erg: erg });
|
||||
});
|
||||
|
||||
/*
|
||||
async.whilst(
|
||||
function() { return sDat < curDat; },
|
||||
function (callback) {
|
||||
collection.findOne( { date: { $gte: new Date(sDat) }}, {sort: { date: 1} }, function(err,data) {
|
||||
if(err) {
|
||||
console.log("StromM-Error: " + err);
|
||||
callback(err);
|
||||
} else {
|
||||
if(last.date != 0) {
|
||||
erg.push({ date: last.date, wert: (data.all - last.wert)});
|
||||
if( (data.all - last.wert) != 0) {
|
||||
last.cnt++;
|
||||
}
|
||||
} else {
|
||||
first = data.all;
|
||||
}
|
||||
last.date = data.date;
|
||||
last.wert = data.all;
|
||||
}
|
||||
sDat.add(1, 'days'); // add 1 day of seconds
|
||||
callback();
|
||||
});
|
||||
},
|
||||
function (err) {
|
||||
var avg = (last.wert - first)/last.cnt;
|
||||
res.send ({ average: avg, erg: erg });
|
||||
}
|
||||
);
|
||||
*/
|
||||
});
|
||||
|
||||
|
||||
|
||||
//Get readings for the last hour out from the database
|
||||
router.get('/latest10I', function(req,res) {
|
||||
var collection = req.strom;
|
||||
var start = moment();
|
||||
// console.log("Start=" + start);
|
||||
start.subtract(20,'s');
|
||||
collection.find({date: { $gte: new Date(start)}},{sort: { date: 1} },function(e,docs) {
|
||||
res.json(docs);
|
||||
});
|
||||
});
|
||||
|
||||
//Get the reading of the 1. of January for the current year
|
||||
router.get('/zstand', function(req,res) {
|
||||
var db = req.db;
|
||||
var curYear = moment().year();
|
||||
var collection = db.get('zaehler');
|
||||
collection.findOne({'year' : curYear},function(e,docs) {
|
||||
res.json(docs);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
||||
482
routes/fsdata.js
Normal file
482
routes/fsdata.js
Normal file
@@ -0,0 +1,482 @@
|
||||
"use strict";
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const moment = require('moment');
|
||||
const mathe = require('mathjs');
|
||||
const util = require('./utilities');
|
||||
|
||||
// Mongo wird in app.js geöffnet und verbunden und bleibt immer verbunden !!
|
||||
|
||||
let sv_factor = {'SBM-20': 1 / 2.47, 'SBM-19': 1 / 9.81888, 'Si22G': 0.081438};
|
||||
|
||||
//Get readings for all data out ot the database
|
||||
router.get('/getfs/:week', function (req, res) {
|
||||
let week = req.params.week;
|
||||
let db = req.app.get('dbase');
|
||||
let st = req.query.start;
|
||||
let sid = parseInt(req.query.sensorid);
|
||||
let sname = req.query.sensorname;
|
||||
let avg = req.query.avgTime;
|
||||
let live = (req.query.live == 'true');
|
||||
let movingAvg = (req.query.moving=='true');
|
||||
let longAVG = req.query.longAVG;
|
||||
let system = req.query.os;
|
||||
|
||||
if (week == 'oneday') {
|
||||
console.log(`Operating System = ${system}`);
|
||||
getDWMData(db, sid, st, avg, live, movingAvg, 1, longAVG)
|
||||
.then(erg => res.json(erg));
|
||||
} else if (week == 'oneweek') {
|
||||
getDWMData(db, sid, st, avg, live, movingAvg, 7)
|
||||
.then(erg => res.json(erg));
|
||||
} else if (week == 'onemonth') {
|
||||
getDWMData(db, sid, st, 1440, false, false, 31)
|
||||
.then(erg => res.json(erg));
|
||||
} else if (week == 'korr') {
|
||||
getSensorProperties(db,sid)
|
||||
.then(erg => res.json(erg));
|
||||
} else {
|
||||
res.json({'error': 'MIST VERDAMMTER!!'});
|
||||
}
|
||||
});
|
||||
|
||||
// fetch name of given sensor-id
|
||||
function getSensorName(db,sid) {
|
||||
const p = new Promise((resolve,reject) => {
|
||||
let coll = db.collection('properties');
|
||||
coll.findOne({_id: parseInt(sid)})
|
||||
.then(erg => {
|
||||
// name is now an array, get the current (last) entry
|
||||
if (erg && erg.name && Array.isArray(erg.name)) {
|
||||
resolve(erg.name[erg.name.length - 1].name);
|
||||
} else {
|
||||
resolve(erg ? erg.name : null);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('getSensorName',err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
return p
|
||||
}
|
||||
|
||||
// fetch the properties for the given sensor
|
||||
async function getSensorProperties(db,sid) {
|
||||
let start = new Date();
|
||||
console.log("Get properties for", sid,"from DB");
|
||||
let sensorEntries = [{'sid':sid}];
|
||||
let coll = db.collection('properties');
|
||||
let properties;
|
||||
try {
|
||||
properties = await coll.findOne({_id: sid});
|
||||
}
|
||||
catch(e) {
|
||||
console.log("getSensorProperties",e);
|
||||
return {};
|
||||
}
|
||||
console.log("got properties - time:", new Date() - start);
|
||||
if(properties == null) return null;
|
||||
// name is now an array, get the current (last) entry
|
||||
let currentName = properties.name;
|
||||
if (Array.isArray(properties.name)) {
|
||||
currentName = properties.name[properties.name.length - 1].name;
|
||||
}
|
||||
|
||||
// Overwrite name field with string for frontend compatibility
|
||||
properties.name = currentName;
|
||||
sensorEntries[0]['name'] = currentName;
|
||||
|
||||
// Check if othersensors exists (might not in new DB structure)
|
||||
if (!properties.othersensors || properties.othersensors.length === 0) {
|
||||
properties.othersensors = sensorEntries;
|
||||
return properties;
|
||||
}
|
||||
|
||||
let mustbeobject = false;
|
||||
for(let i = 0, j=1; i<properties.othersensors.length; i++) {
|
||||
let es = properties.othersensors[i];
|
||||
let e = {};
|
||||
if (es != null) {
|
||||
if ( typeof es === 'object') {
|
||||
mustbeobject=true;
|
||||
e.sid = es.id;
|
||||
e.name = es.name;
|
||||
} else {
|
||||
if(mustbeobject) { continue; }
|
||||
e.sid = es;
|
||||
e.name = await getSensorName(db, es);
|
||||
}
|
||||
}
|
||||
sensorEntries[j] = e;
|
||||
j++;
|
||||
}
|
||||
sensorEntries.sort(function (a, b) {
|
||||
if (a.sid < b.sid) {
|
||||
return -1;
|
||||
}
|
||||
if (a.sid > b.sid) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
properties.othersensors = sensorEntries;
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
async function readRadiationMovingAverage(db, sid, start, end, average, factor) {
|
||||
let docs = [];
|
||||
let collection = db.collection('radioactivity_sensors');
|
||||
try {
|
||||
docs = await collection.find({
|
||||
sensorid: sid,
|
||||
datetime: {
|
||||
$gte: new Date(start),
|
||||
$lt: new Date(end)
|
||||
}
|
||||
}, {sort: {datetime: 1}}).toArray();
|
||||
} catch (e) {
|
||||
console.log('readRadiationMovingAverage',e);
|
||||
return [];
|
||||
}
|
||||
if (docs.length == 0) {
|
||||
return [];
|
||||
} else {
|
||||
let d = await util.calcMovingAverage(db, sid, docs, average , false, factor);
|
||||
return d.RAD;
|
||||
}
|
||||
}
|
||||
|
||||
async function readRadiationAverages(db, sid, start, end, average, factor) {
|
||||
let docs = [];
|
||||
let collection = db.collection('radioactivity_sensors');
|
||||
try {
|
||||
docs = await collection.aggregate([
|
||||
{$match: {sensorid: sid, datetime: {$gte: new Date(start), $lt: new Date(end)}}},
|
||||
{$sort: {datetime: 1}},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
$toDate: {
|
||||
$subtract: [
|
||||
{$toLong: '$datetime'},
|
||||
{$mod: [{$toLong: '$datetime'}, 1000 * 60 * average]} // aggregate every average minutes
|
||||
]
|
||||
}
|
||||
},
|
||||
cpmAvg: {$avg: '$values.counts_per_minute'},
|
||||
cpmSum: {$sum: '$values.counts_per_minute'},
|
||||
count: {$sum: 1}
|
||||
}
|
||||
},
|
||||
{ $addFields: { uSvphAvg: { $multiply: ["$cpmAvg", factor]}}},
|
||||
{$sort: {_id: 1}}
|
||||
]).toArray();
|
||||
}
|
||||
catch(e) {
|
||||
console.log('readRadiationAverages', e);
|
||||
return [];
|
||||
}
|
||||
return docs;
|
||||
}
|
||||
|
||||
async function readClimateAverages(db, sid, start, end, average) {
|
||||
let docs = [];
|
||||
let collection = db.collection('thp_sensors');
|
||||
try {
|
||||
docs = await collection.aggregate([
|
||||
{$match: {sensorid: sid, datetime: {$gte: new Date(start), $lt: new Date(end)}}},
|
||||
{$sort: {datetime: 1}},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
$toDate: {
|
||||
$subtract: [
|
||||
{$toLong: '$datetime'},
|
||||
{$mod: [{$toLong: '$datetime'}, 1000 * 60 * average]} // aggregate every average min
|
||||
]
|
||||
}
|
||||
},
|
||||
tempAvg: {$avg: '$values.temperature'}, // average over every 10min
|
||||
humiAvg: {$avg: '$values.humidity'},
|
||||
pressSeaAvg: {$avg: '$values.pressure_at_sealevel'},
|
||||
count: {$sum: 1}
|
||||
}
|
||||
},
|
||||
{$sort: {_id: 1}}
|
||||
]).toArray();
|
||||
}
|
||||
catch(e) {
|
||||
console.log('readClimateAverage', e);
|
||||
return [];
|
||||
}
|
||||
return docs;
|
||||
}
|
||||
|
||||
function calcTimeRange(st, range, live, avg) {
|
||||
let start = moment(st);
|
||||
let end = moment(st);
|
||||
if(range == 1) { // one day
|
||||
if (live == true) {
|
||||
start.subtract(24, 'h');
|
||||
start.subtract(avg, 'm');
|
||||
} else {
|
||||
start.subtract(avg, 'm');
|
||||
end.add(24, 'h');
|
||||
}
|
||||
} else if (range == 7) { // one week (7 days)
|
||||
if (live == true) {
|
||||
start.subtract(24 * 8, 'h');
|
||||
} else {
|
||||
start.subtract(24 * 8, 'h');
|
||||
// end.add(24 * 7, 'h');
|
||||
console.log(start.format(), end.format());
|
||||
}
|
||||
} else if (range == 31) { // one month (31 days)
|
||||
start=start.startOf('day');
|
||||
end = end.startOf('day');
|
||||
start.subtract(33, 'd');
|
||||
} else if (range >= 48) { // 48 hours
|
||||
if(live == true) {
|
||||
start.subtract(range, 'h')
|
||||
}
|
||||
}
|
||||
return { start: start, end: end };
|
||||
}
|
||||
|
||||
// get data for one day, one week or one month from the database
|
||||
async function getDWMData(db, sensorid, st, avg, live, doMoving, span, longAVG) {
|
||||
let docs = [];
|
||||
let ret = {radiation: [], climate: []};
|
||||
// first fetch properties for this sensor
|
||||
let properties = await getSensorProperties(db,sensorid);
|
||||
// calculate time range
|
||||
let timerange = calcTimeRange(st, span, live, avg);
|
||||
for (let n = 0; n<properties.othersensors.length; n++) {
|
||||
let sid = properties.othersensors[n].sid;
|
||||
let sname = properties.othersensors[n].name;
|
||||
try {
|
||||
if (sname.startsWith("Radiation")) {
|
||||
let factor = sv_factor[sname.substring(10)] / 60;
|
||||
if(doMoving) {
|
||||
docs = await readRadiationMovingAverage(db, sid, timerange.start, timerange.end, avg, factor);
|
||||
} else {
|
||||
docs = await readRadiationAverages(db, sid, timerange.start, timerange.end, avg, factor);
|
||||
}
|
||||
let avg48 = null;
|
||||
if (longAVG != undefined) {
|
||||
let tr = calcTimeRange(st, longAVG, true, avg);
|
||||
let avg48_docs = await readRadiationAverages(db, sid, tr.start, tr.end, longAVG*60, factor);
|
||||
avg48 = avg48_docs[0];
|
||||
}
|
||||
ret['radiation'] = {sid: sid, sname: sname, avg48: avg48, values: docs};
|
||||
} else if (sname == "BME280") {
|
||||
docs = await readClimateAverages(db, sid, timerange.start, timerange.end, 10);
|
||||
if (docs.length != 0) {
|
||||
ret['climate'] = {sid: sid, sname: sname, values: docs};
|
||||
}
|
||||
} else {
|
||||
ret['error'] = "Sensor not of right type (unknown)";
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('getDayData', e);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// für die Wochenanzeige die Daten als gleitenden Mittelwert über 24h durchrechnen
|
||||
// ===============================================================
|
||||
// PM (FEINSTAUB) FUNCTIONS REMOVED - Not needed in new DB
|
||||
// ===============================================================
|
||||
/*
|
||||
// und in einem neuen Array übergeben
|
||||
function movAvgSDSWeek(data) {
|
||||
var neuData = [];
|
||||
const oneDay = 3600*24;
|
||||
|
||||
// first convert date to timestam (in secs)
|
||||
for (var i=0; i<data.length; i++) {
|
||||
data[i].datetime = ( new Date(data[i].datetime)) / 1000; // the math does the convertion
|
||||
}
|
||||
|
||||
// now calculate the moving average over 24 hours
|
||||
let left=0, roll_sum=0, nd = [];
|
||||
for (let right =0; right < data.length; right++) {
|
||||
// if(right == 200) {
|
||||
// console.log("right = 200");
|
||||
// }
|
||||
|
||||
if(data[right].P1 != undefined) {
|
||||
roll_sum += data[right].P1;
|
||||
}
|
||||
if(data[right].P10 != undefined) {
|
||||
roll_sum += data[right].P10;
|
||||
}
|
||||
|
||||
while( data[left].datetime <= data[right].datetime - oneDay) {
|
||||
if(data[left].P1 != undefined) {
|
||||
roll_sum -= data[left].P1;
|
||||
}
|
||||
if(data[left].P10 != undefined) {
|
||||
roll_sum -= data[left].P10;
|
||||
}
|
||||
left += 1;
|
||||
}
|
||||
nd[right] = { 'P1024': roll_sum/ (right-left+1)+5};
|
||||
}
|
||||
|
||||
for (var i=1, j=0; i< data.length; i++, j++) {
|
||||
var sum1=0, sum2 = 0, cnt1 = 0, cnt2 = 0;
|
||||
var di = data[i].datetime;
|
||||
for (var k=i; k>=0 ; k--) {
|
||||
if (data[k].datetime+oneDay < di) {
|
||||
break;
|
||||
}
|
||||
if (data[k].P1 !== undefined) {
|
||||
sum1 += data[k].P1;
|
||||
cnt1++;
|
||||
}
|
||||
if (data[k].P2 !== undefined) {
|
||||
sum2 += data[k].P2;
|
||||
cnt2++;
|
||||
}
|
||||
if (data[k].P10 !== undefined) {
|
||||
sum1 += data[k].P10;
|
||||
cnt1++;
|
||||
}
|
||||
if (data[k].P2_5 !== undefined) {
|
||||
sum2 += data[k].P2_5;
|
||||
cnt2++;
|
||||
}
|
||||
}
|
||||
neuData[j] = {'P10': sum1 / cnt1, 'P2_5': sum2 / cnt2, 'date': data[i].datetime} ;
|
||||
}
|
||||
// finally shrink datasize, so that max. 1000 values will be returned
|
||||
var neu1 = [];
|
||||
var step = Math.round(neuData.length / 500);
|
||||
// if (step == 0) step = 1;
|
||||
step = 1;
|
||||
for (var i = 0, j=0; i < neuData.length; i+=step, j++) {
|
||||
var d = neuData.slice(i,i+step)
|
||||
var p1=0, p2=0;
|
||||
for(var k=0; k<d.length; k++) {
|
||||
if (d[k].P10 > p1) {
|
||||
p1 = d[k].P10;
|
||||
}
|
||||
if (d[k].P2_5 > p2) {
|
||||
p2 = d[k].P2_5;
|
||||
}
|
||||
}
|
||||
neu1[j] = {'P10': p1, 'P2_5': p2, 'date': neuData[i].date*1000} ;
|
||||
}
|
||||
return { 'ndold': neu1, 'ndnew': nd };
|
||||
}
|
||||
|
||||
|
||||
|
||||
function calcMinMaxAvgSDS(data,isp10) {
|
||||
var p1=[], p2=[];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].P10 != undefined) {
|
||||
p1.push(data[i].P10);
|
||||
}
|
||||
if (data[i].P2_5 != undefined) {
|
||||
p2.push(data[i].P2_5);
|
||||
}
|
||||
if (data[i].P1 != undefined) {
|
||||
p1.push(data[i].P1);
|
||||
}
|
||||
if (data[i].P2 != undefined) {
|
||||
p2.push(data[i].P2);
|
||||
}
|
||||
}
|
||||
return {
|
||||
'P10_max': mathe.max(p1),
|
||||
'P2_5_max': mathe.max(p2),
|
||||
'P10_min': mathe.min(p1),
|
||||
'P2_5_min': mathe.min(p2),
|
||||
'P10_avg' : mathe.mean(p1),
|
||||
'P2_5_avg' : mathe.mean(p2) };
|
||||
}
|
||||
*/
|
||||
|
||||
function calcMinMaxAvgDHT(data) {
|
||||
var t=[], h=[];
|
||||
for (var i=0; i<data.length; i++) {
|
||||
if(data[i].temperature != undefined) {
|
||||
t.push(data[i].temperature);
|
||||
}
|
||||
if(data[i].humidity != undefined) {
|
||||
h.push(data[i].humidity);
|
||||
}
|
||||
}
|
||||
return {
|
||||
'temp_max': mathe.max(t),
|
||||
'humi_max': mathe.max(h),
|
||||
'temp_min': mathe.min(t),
|
||||
'humi_min': mathe.min(h),
|
||||
'temp_avg' : mathe.mean(t),
|
||||
'humi_avg' : mathe.mean(h) };
|
||||
}
|
||||
|
||||
|
||||
async function calcMinMaxAvgBMP(db, sid, data) {
|
||||
var t = [], p = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].temperature != undefined) {
|
||||
t.push(data[i].temperature);
|
||||
}
|
||||
if (data[i].pressure != undefined) {
|
||||
p.push(data[i].pressure);
|
||||
}
|
||||
}
|
||||
let altitude = await util.getAltitude(db, sid);
|
||||
p = util.calcSealevelPressure(p, '', altitude);
|
||||
return {
|
||||
'temp_max': mathe.max(t), 'press_max': mathe.max(p),
|
||||
'temp_min': mathe.min(t), 'press_min': mathe.min(p),
|
||||
'temp_avg': mathe.mean(t), 'press_avg': mathe.mean(p)
|
||||
};
|
||||
}
|
||||
|
||||
async function calcMinMaxAvgBME(db, sid, data) {
|
||||
var t = [], h = [], p = [], sumt = 0;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].temperature != undefined) {
|
||||
t.push(data[i].temperature);
|
||||
}
|
||||
if (data[i].humidity != undefined) {
|
||||
h.push(data[i].humidity);
|
||||
}
|
||||
if (data[i].pressure != undefined) {
|
||||
p.push(data[i].pressure);
|
||||
}
|
||||
}
|
||||
let altitude = await util.getAltitude(db, sid);
|
||||
p = util.calcSealevelPressure(p, '', altitude);
|
||||
return {
|
||||
'temp_max': mathe.max(t), 'humi_max': mathe.max(h), 'press_max': mathe.max(p),
|
||||
'temp_min': mathe.min(t), 'humi_min': mathe.min(h), 'press_min': mathe.min(p),
|
||||
'temp_avg': mathe.mean(t), 'humi_avg': mathe.mean(h), 'press_avg': mathe.mean(p)
|
||||
};
|
||||
}
|
||||
|
||||
/* PM (FEINSTAUB) FUNCTIONS REMOVED
|
||||
function isPM(name) {
|
||||
if ((name == "SDS011") || (name.startsWith("PPD")) || (name.startsWith("PMS"))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Statistiken für den Sensor holen und übergeben - PM only
|
||||
async function getStatistics(db,sensorid) {
|
||||
// REMOVED - Was PM-specific
|
||||
return { error: 'PM statistics removed in new DB' };
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
module.exports = router;
|
||||
59
routes/index.js
Normal file
59
routes/index.js
Normal file
@@ -0,0 +1,59 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
//Version
|
||||
var pkg = require('../package.json');
|
||||
|
||||
|
||||
var idx = 'index';
|
||||
var mapit = 'mapit';
|
||||
var tit = 'Geiger';
|
||||
|
||||
const MAINT = (process.env.MAINTENANCE=="true");
|
||||
|
||||
if (MAINT==true) {
|
||||
idx = 'maintenance';
|
||||
mapit = 'maintenance';
|
||||
}
|
||||
|
||||
// GET home page.
|
||||
router.get('/', function(req, res, next) {
|
||||
res.render(mapit, {
|
||||
title: tit+'-Map',
|
||||
version: pkg.version,
|
||||
date: pkg.date,
|
||||
name: 'map',
|
||||
param: req.query.addr,
|
||||
stday: req.query.stday,
|
||||
showAllMap: req.query.mall,
|
||||
csensor: req.query.sid,
|
||||
zoom:req.query.zoom,
|
||||
city:req.query.city,
|
||||
stype: tit,
|
||||
splash: req.query.splash == 'true',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
router.get('/:nam', function(req, res, next) {
|
||||
var name = req.params.nam;
|
||||
if(name == 'index') {
|
||||
idx = 'index';
|
||||
name = '140';
|
||||
title += '_I';
|
||||
} else {
|
||||
res.render(mapit, {
|
||||
title: tit+'-Map',
|
||||
version : pkg.version,
|
||||
date : pkg.date,
|
||||
param: req.query.addr,
|
||||
stday: req.query.stday,
|
||||
zoom:req.query.zoom,
|
||||
city:req.query.city,
|
||||
csensor: name,
|
||||
splash: req.query.splash == 'true',
|
||||
name: 'map'});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
||||
294
routes/mapdata.js
Executable file
294
routes/mapdata.js
Executable file
@@ -0,0 +1,294 @@
|
||||
"use strict";
|
||||
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
var moment = require('moment');
|
||||
const axios = require('axios');
|
||||
let $ = require('jquery');
|
||||
var fs = require('fs');
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
|
||||
// URL to get coordinates for cities
|
||||
const NOMINATIM_URL="https://nominatim.openstreetmap.org/search?format=json&limit=3&q=";
|
||||
|
||||
// Mongo wird in app.js geöffnet und verbunden und bleibt immer verbunden !!
|
||||
|
||||
// Fetch the actual out of the dbase
|
||||
router.get('/getaktdata/', async function (req, res) {
|
||||
var db = req.app.get('dbase'); // db wird in req übergeben (von app.js)
|
||||
let box = req.query.box;
|
||||
let poly = [];
|
||||
var collection = db.collection('properties'); // Using properties collection now
|
||||
var aktData = []; // hier die daten sammeln
|
||||
var now = moment(); // akt. Uhrzeit
|
||||
var lastDate = 0;
|
||||
let south=null,north=null,east=null,west=null;
|
||||
let loc = {};
|
||||
if(req.query.poly != undefined) {
|
||||
poly = JSON.parse(req.query.poly);
|
||||
}
|
||||
if (!((box == "") || (box == undefined))) {
|
||||
south = parseFloat(box[0][1]);
|
||||
north = parseFloat(box[1][1]);
|
||||
east = parseFloat(box[1][0]);
|
||||
west = parseFloat(box[0][0]);
|
||||
console.log("getaktdata: S=", south, " N=", north, " E=", east, " W=", west)
|
||||
}
|
||||
console.log("getaktdata: now fetching data from DB");
|
||||
|
||||
// Build geo query - location is now in location array (last element)
|
||||
if(poly.length != 0) {
|
||||
loc = {
|
||||
'location.loc': {
|
||||
$geoWithin: {
|
||||
$geometry: {
|
||||
type: "Polygon",
|
||||
coordinates: [poly],
|
||||
}
|
||||
}
|
||||
},
|
||||
type: 'radioactivity' // Only radiation sensors for map
|
||||
}
|
||||
} else if (south !== null) {
|
||||
loc = {
|
||||
'location.loc': {
|
||||
$geoWithin: {
|
||||
$box: [
|
||||
[west, south],
|
||||
[east, north]
|
||||
]
|
||||
}
|
||||
},
|
||||
type: 'radioactivity' // Only radiation sensors for map
|
||||
}
|
||||
} else {
|
||||
loc = {
|
||||
type: 'radioactivity' // Only radiation sensors for map
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let docs = await collection.find(loc).toArray();
|
||||
|
||||
if (docs == null) {
|
||||
console.log("getaktdata: docs==null");
|
||||
res.json({"avgs": [], "lastDate": null});
|
||||
return;
|
||||
}
|
||||
console.log("getaktdata: data fetched, length=",docs.length);
|
||||
|
||||
for (var i = 0; i < docs.length; i++) {
|
||||
var item = docs[i];
|
||||
|
||||
// Skip if no values or no location
|
||||
if (!item.values || !item.location || item.location.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get current location (last in array)
|
||||
let currentLoc = item.location[item.location.length - 1];
|
||||
|
||||
// Get current name (last in array)
|
||||
let currentName = item.name;
|
||||
if (Array.isArray(item.name)) {
|
||||
currentName = item.name[item.name.length - 1].name;
|
||||
}
|
||||
|
||||
var oneAktData = {};
|
||||
oneAktData['location'] = currentLoc.loc.coordinates;
|
||||
oneAktData['id'] = item._id;
|
||||
|
||||
// Handle timestamp - might be in $date format
|
||||
let timestamp = item.values.timestamp;
|
||||
if (timestamp && timestamp.$date) {
|
||||
timestamp = new Date(timestamp.$date);
|
||||
} else {
|
||||
timestamp = new Date(timestamp);
|
||||
}
|
||||
|
||||
oneAktData['lastSeen'] = timestamp;
|
||||
// Extract sensor type from name (e.g., "Radiation SBM-20" -> "SBM-20")
|
||||
oneAktData['name'] = currentName.replace('Radiation ', '');
|
||||
// indoor is now a number (0 or 1)
|
||||
oneAktData['indoor'] = currentLoc.indoor === 1;
|
||||
|
||||
var dt = timestamp;
|
||||
if ((now - dt) >= (7 * 24 * 3600 * 1000)) { // älter als 1 Woche ->
|
||||
oneAktData['cpm'] = -2; // -2 zurückgeben
|
||||
} else if ((now - dt) >= (2 * 3600 * 1000)) { // älter als 2 Stunde ->
|
||||
oneAktData['cpm'] = -1; // -1 zurückgeben
|
||||
} else {
|
||||
oneAktData['cpm'] = -5; // bedeutet -> nicht anzeigen
|
||||
if (item.values.hasOwnProperty('counts_per_minute')) {
|
||||
oneAktData['cpm'] = item.values.counts_per_minute.toFixed(0); // und merken
|
||||
}
|
||||
if (dt > lastDate) {
|
||||
lastDate = dt;
|
||||
}
|
||||
}
|
||||
aktData.push(oneAktData); // dies ganzen Werte nun in das Array
|
||||
}
|
||||
res.json({"avgs": aktData, "lastDate": lastDate}); // alles bearbeitet -> Array senden
|
||||
}
|
||||
catch(e) {
|
||||
console.log("Problem mit getaktdata", e);
|
||||
res.json({"avgs": [], "lastDate": null});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
/* ===============================================================
|
||||
// AKW (Nuclear Power Plant) FUNCTIONS - COMMENTED OUT FOR NOW
|
||||
// Will be needed later, so not deleting
|
||||
// ===============================================================
|
||||
|
||||
// Fetch all akw data out of the dbase
|
||||
router.get('/getakwdata/', async function (req, res) {
|
||||
const db = req.app.get('dbase'); // db wird in req übergeben (von app.js)
|
||||
let collection = db.collection('akws'); // die 'korrelation' verwenden
|
||||
let erg = [];
|
||||
let docs = [];
|
||||
console.log("getakwdata: now fetching data from DB");
|
||||
try {
|
||||
docs = await collection.find().toArray(); // find all
|
||||
if (docs == null) {
|
||||
console.log("getakwdata: docs==null");
|
||||
res.json(erg);
|
||||
return;
|
||||
}
|
||||
console.log("getawkdata: data fetched from akws, length=",docs.length);
|
||||
for (var i = 0; i < docs.length; i++) {
|
||||
var item = docs[i];
|
||||
var oneAktData = {};
|
||||
oneAktData['location'] = {
|
||||
type: 'Point',
|
||||
coordinates: [item.lon, item.lat]
|
||||
};
|
||||
oneAktData['name'] = item.Name;
|
||||
oneAktData['active'] = item.Status == 'aktiv';
|
||||
oneAktData['start'] = item.Baujahr;
|
||||
oneAktData['end'] = item.Stillgeleg;
|
||||
oneAktData['type'] = item.Status === 'aktiv' ? 'akw_a' : 'akw_s';
|
||||
oneAktData['link'] = item.Wiki_Link;
|
||||
erg.push(oneAktData); // dies ganzen Werte nun in das Array
|
||||
}
|
||||
|
||||
collection = db.collection('th1_akws');
|
||||
docs = await collection.find().toArray();
|
||||
if (docs == null) {
|
||||
console.log("getakwdata: docs==null");
|
||||
res.json(erg);
|
||||
return;
|
||||
}
|
||||
console.log("getawkdata: data fetched from th_akws, length=", docs.length);
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
const item = docs[i];
|
||||
let oneAktData = {};
|
||||
let loc = item.geo.substr(6).split(' ');
|
||||
let lon = parseFloat(loc[0]);
|
||||
let lat = parseFloat(loc[1]);
|
||||
oneAktData['location'] = {
|
||||
type: 'Point',
|
||||
coordinates: [lon, lat]
|
||||
};
|
||||
oneAktData['name'] = item.name;
|
||||
oneAktData['typeText'] = item.types;
|
||||
oneAktData['type'] = item.types == "Nuclear power plant" ? 'akw_a' : 'other';
|
||||
oneAktData['link'] = item.item;
|
||||
if (item.itemServiceretirement != undefined) {
|
||||
oneAktData['ende'] = item.itemServiceretirement.substr(0,4);
|
||||
}
|
||||
if (item.itemServiceentry != undefined) {
|
||||
oneAktData['begin'] = item.itemServiceentry.substr(0,4);
|
||||
}
|
||||
// Push only NOT 'Nuclear Power Plants' into data array
|
||||
// if(item.types != 'Nuclear power plant') {
|
||||
erg.push(oneAktData);
|
||||
// }
|
||||
}
|
||||
res.json(erg);
|
||||
}
|
||||
catch(e) {
|
||||
console.log("Problem mit getakwdata", e);
|
||||
res.json({"akws": [], "research": [], "fusion": [], "waste": [],});
|
||||
return;
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
router.get('/getStuttgart/', function (req, res) {
|
||||
fs.readFile('public/Stuttgart.gpx',function(err,data) {
|
||||
res.send(data);
|
||||
})
|
||||
});
|
||||
|
||||
router.get('/getcoord/', function (req, res) {
|
||||
getCoordinates(req.query.city)
|
||||
.then(erg => res.json(erg));
|
||||
});
|
||||
|
||||
router.get('/getIcon/:col', function (req, res) {
|
||||
let color = req.params.col;
|
||||
// fs.readFile('public/radioak4_30.png',function(err,data) {
|
||||
fs.readFile('public/nuclear-'+color+'.svg',function(err,data) {
|
||||
res.send(data);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
router.get('/regionSensors/', function (req, res) {
|
||||
var db = req.app.get('dbase'); // db wird in req übergeben (von app.js)
|
||||
var spoints = JSON.parse(req.query.points);
|
||||
getRegionSensors(db,spoints)
|
||||
.then(erg => res.json(erg));
|
||||
});
|
||||
|
||||
async function getRegionSensors(db,p) {
|
||||
let properties = [];
|
||||
let pcoll = db.collection("properties");
|
||||
|
||||
properties = await pcoll.find({
|
||||
'location.loc': {
|
||||
$geoWithin: {
|
||||
$geometry: {
|
||||
type: "Polygon",
|
||||
coordinates: [ p ],
|
||||
}
|
||||
}
|
||||
},
|
||||
type: 'radioactivity' // Only radiation sensors
|
||||
},{_id: 1, name: 1}
|
||||
).toArray();
|
||||
|
||||
let sids = [];
|
||||
properties.forEach(x => {
|
||||
sids.push(x._id);
|
||||
});
|
||||
console.log('Anzahl gefundene Sensoren:',sids.length);
|
||||
return sids;
|
||||
}
|
||||
|
||||
router.get('/storeSensors/', function (req, res) {
|
||||
let data = req.query.sensors;
|
||||
fs.writeFile('stuttgart.txt',data,(err) => {
|
||||
if (err) throw(err);
|
||||
console.log("Sensoren gespeichert");
|
||||
});
|
||||
});
|
||||
|
||||
async function getCoordinates(city) {
|
||||
let start = moment()
|
||||
let url = NOMINATIM_URL + city;
|
||||
const response = await axios.get(encodeURI(url));
|
||||
const data = response.data;
|
||||
if(data.length !== 0) {
|
||||
console.log(`Fetching of city ${city} needs ${(moment() - start) / 1000} seconds.`)
|
||||
return data[0];
|
||||
} else {
|
||||
console.log(`City ${city} not found` )
|
||||
return {lat: 0, lon: 0}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
245
routes/utilities.js
Normal file
245
routes/utilities.js
Normal file
@@ -0,0 +1,245 @@
|
||||
"use strict";
|
||||
const moment = require('moment');
|
||||
|
||||
// *********************************************
|
||||
// Calculate moving average over the data array.
|
||||
//
|
||||
// params:
|
||||
// data: array of data
|
||||
// mav: time in minutes to average
|
||||
// name: name of sensor
|
||||
// api: default=false, true = API -> no akt. values
|
||||
//
|
||||
// return:
|
||||
// array with averaged values
|
||||
// TODO <----- die ersten Einträge in newData mit 0 füllen bis zum Beginn des average
|
||||
// *********************************************
|
||||
async function calcMovingAverage(db, sid, data, mav, api, factor) {
|
||||
var newDataT = [], newDataR = [];
|
||||
var avgTime = mav*60; // average time in sec
|
||||
|
||||
let havepressure = false; // true: we have pressure
|
||||
|
||||
if (avgTime === 0) { // if there's nothing to average, then
|
||||
avgTime = 1;
|
||||
}
|
||||
// first convert date to timestamp (in secs)
|
||||
for (var i=0; i<data.length; i++) {
|
||||
// Handle both old datetime and new datetime.$date format
|
||||
if (data[i].datetime && data[i].datetime.$date) {
|
||||
data[i].datetime = (new Date(data[i].datetime.$date)) / 1000;
|
||||
} else {
|
||||
data[i].datetime = (new Date(data[i].datetime)) / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
let left=0, roll_sum3=0, roll_sum4=0, roll_sum5=0, roll_sum6=0;
|
||||
|
||||
// Check if we have pressure data
|
||||
if(data[0] && data[0].values && data[0].values.pressure != undefined) {
|
||||
havepressure = true;
|
||||
} else if (data[0] && data[0].pressure != undefined) {
|
||||
havepressure = true;
|
||||
}
|
||||
|
||||
for (let right =0; right < data.length; right++) {
|
||||
// Handle values object (new structure)
|
||||
let temperature = data[right].values ? data[right].values.temperature : data[right].temperature;
|
||||
let humidity = data[right].values ? data[right].values.humidity : data[right].humidity;
|
||||
let pressure = data[right].values ? data[right].values.pressure : data[right].pressure;
|
||||
let counts_per_minute = data[right].values ? data[right].values.counts_per_minute : data[right].counts_per_minute;
|
||||
|
||||
if (temperature != undefined) {
|
||||
roll_sum3 += temperature;
|
||||
}
|
||||
if (humidity != undefined) {
|
||||
roll_sum4 += humidity;
|
||||
}
|
||||
if (pressure != undefined) {
|
||||
roll_sum5 += pressure;
|
||||
}
|
||||
if (counts_per_minute != undefined) {
|
||||
roll_sum6 += counts_per_minute;
|
||||
}
|
||||
|
||||
while (data[left].datetime <= data[right].datetime - avgTime) {
|
||||
let temp_left = data[left].values ? data[left].values.temperature : data[left].temperature;
|
||||
let humi_left = data[left].values ? data[left].values.humidity : data[left].humidity;
|
||||
let press_left = data[left].values ? data[left].values.pressure : data[left].pressure;
|
||||
let cpm_left = data[left].values ? data[left].values.counts_per_minute : data[left].counts_per_minute;
|
||||
|
||||
if (temp_left != undefined) {
|
||||
roll_sum3 -= temp_left;
|
||||
}
|
||||
if (humi_left != undefined) {
|
||||
roll_sum4 -= humi_left;
|
||||
}
|
||||
if (press_left != undefined) {
|
||||
roll_sum5 -= press_left;
|
||||
}
|
||||
if (cpm_left != undefined) {
|
||||
roll_sum6 -= cpm_left;
|
||||
}
|
||||
left += 1;
|
||||
}
|
||||
|
||||
if (api == true) {
|
||||
newDataT[right] = {'dt': moment.unix(data[right].datetime)};
|
||||
if (roll_sum3 != 0) newDataT[right]['T'] = (roll_sum3 / (right - left + 1)).toFixed(1);
|
||||
if (roll_sum4 != 0) newDataT[right]['H'] = (roll_sum4 / (right - left + 1)).toFixed(0);
|
||||
if (roll_sum5 != 0) newDataT[right]['P'] = (roll_sum5 / (right - left + 1)).toFixed(2);
|
||||
|
||||
newDataR[right] = {'date': data[right].datetime * 1000};
|
||||
if (roll_sum6 != 0) newDataR[right]['cpm'] = roll_sum6 / (right - left + 1);
|
||||
} else {
|
||||
newDataT[right] = {'date': data[right].datetime * 1000};
|
||||
if (roll_sum3 != 0) newDataT[right]['temp_mav'] = roll_sum3 / (right - left + 1);
|
||||
if (roll_sum4 != 0) newDataT[right]['humi_mav'] = roll_sum4 / (right - left + 1);
|
||||
if (roll_sum5 != 0) newDataT[right]['press_mav'] = roll_sum5 / (right - left + 1);
|
||||
|
||||
newDataR[right] = {'_id': moment.unix(data[right].datetime).toDate()};
|
||||
if (roll_sum6 != 0) {
|
||||
let val = roll_sum6 / (right - left + 1);
|
||||
newDataR[right]['cpmAvg'] = val;
|
||||
newDataR[right]['uSvphAvg'] = val * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (havepressure == true) {
|
||||
let altitude = await getAltitude(db, sid);
|
||||
if (api == true) {
|
||||
newDataT = calcSealevelPressure(newDataT, 'P', altitude);
|
||||
for (let i = 0; i < newDataT.length; i++) {
|
||||
newDataT[i].P = (newDataT[i].P / 100).toFixed(0);
|
||||
}
|
||||
} else {
|
||||
newDataT = calcSealevelPressure(newDataT, 'press_mav', altitude);
|
||||
}
|
||||
}
|
||||
if (api == true) {
|
||||
// Return THP or Radiation data depending on what's available
|
||||
return (roll_sum3 != 0 || roll_sum4 != 0 || roll_sum5 != 0) ? newDataT : newDataR;
|
||||
}
|
||||
return { 'THP' : newDataT , 'RAD' : newDataR};
|
||||
}
|
||||
|
||||
// Berechnung des barometrischen Druckes auf Seehöhe
|
||||
//
|
||||
// Formel (lt. WikiPedia):
|
||||
//
|
||||
// p[0] = p[h] * ((T[h] / (T[h] + 0,0065 * h) ) ^-5.255)
|
||||
//
|
||||
// mit
|
||||
// p[0] Druck auf NN (in hPa)
|
||||
// p[h] gemessener Druck auf Höhe h (in m)
|
||||
// T[h] gemessene Temperatur auf Höhe h in K (== t+273,15)
|
||||
// h Höhe über NN in m
|
||||
//
|
||||
// press -> aktuelle Druck am Ort
|
||||
// temp -> aktuelle Temperatur
|
||||
// alti -> Höhe über NN im m
|
||||
//
|
||||
// NEU NEU NEU
|
||||
// Formel aus dem BMP180 Datenblatt
|
||||
//
|
||||
// p0 = ph / pow(1.0 - (altitude/44330.0), 5.255);
|
||||
//
|
||||
//
|
||||
//
|
||||
// Rückgabe: normierter Druck auf Sehhhöhe
|
||||
//
|
||||
function calcSealevelPressure(data, p, alti) {
|
||||
if (!((alti == 0) || (alti == undefined))) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (p=='') {
|
||||
data[i] = data[i] / Math.pow(1.0 - (alti / 44330.0), 5.255);
|
||||
} else {
|
||||
data[i][p] = data[i][p] / Math.pow(1.0 - (alti / 44330.0), 5.255);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// Aus der 'properties'-collection die altitude für die
|
||||
// übergebene sid rausholen
|
||||
async function getAltitude(db,sid) {
|
||||
let collection = db.collection('properties');
|
||||
try {
|
||||
let values = await collection.findOne({"_id":sid});
|
||||
return values.location[values.location.length-1].altitude;
|
||||
}
|
||||
catch(e) {
|
||||
console.log("GetAltitude Error",e);
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Get address from coordinates using OpenStreetMap Nominatim API
|
||||
// params: db, sensorid
|
||||
// returns: {address: {street, plz, city}, err}
|
||||
async function getAddress(db, sid) {
|
||||
const axios = require('axios');
|
||||
let ret = {address: {street: "", plz: "", city: ""}, err: null};
|
||||
|
||||
// Get sensor properties to extract coordinates
|
||||
let collection = db.collection('properties');
|
||||
let props;
|
||||
try {
|
||||
props = await collection.findOne({"_id": sid});
|
||||
if (!props || !props.location || props.location.length === 0) {
|
||||
return {address: ret.address, err: "No location found for sensor"};
|
||||
}
|
||||
} catch(e) {
|
||||
console.log("getAddress - DB Error:", e);
|
||||
return {address: ret.address, err: e.message};
|
||||
}
|
||||
|
||||
// Get coordinates from last location entry
|
||||
let coord = props.location[props.location.length - 1].loc.coordinates;
|
||||
let lat = coord[1];
|
||||
let lon = coord[0];
|
||||
|
||||
// Call Nominatim API for reverse geocoding
|
||||
let url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json`;
|
||||
|
||||
try {
|
||||
const response = await axios(encodeURI(url), {
|
||||
headers: {
|
||||
'User-Agent': 'MultiGeiger-Web/2.9.6' // Nominatim requires User-Agent
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
return {address: ret.address, err: `Nominatim API returned status ${response.status}`};
|
||||
}
|
||||
|
||||
let akt = response.data.address;
|
||||
|
||||
// Try to find city name in various fields
|
||||
const CITY = ['city', 'town', 'village', 'suburb', 'county'];
|
||||
let city = "unknown";
|
||||
for (let c of CITY) {
|
||||
if(akt[c] !== undefined) {
|
||||
city = akt[c];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret.address = {
|
||||
street: (akt.road ? akt.road : ""),
|
||||
plz: (akt.postcode ? akt.postcode : ""),
|
||||
city: city
|
||||
};
|
||||
} catch (e) {
|
||||
console.log("getAddress - API Error:", e.message);
|
||||
return {address: ret.address, err: e.message};
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
module.exports.calcMovingAverage = calcMovingAverage;
|
||||
module.exports.calcSealevelPressure = calcSealevelPressure;
|
||||
module.exports.getAltitude = getAltitude;
|
||||
module.exports.getAddress = getAddress;
|
||||
Reference in New Issue
Block a user