From a88d9637be559441b807d54ad23b6deff72e392f Mon Sep 17 00:00:00 2001 From: svensMPG <sven.schneider@hft-stuttgart.de> Date: Mon, 28 Jun 2021 11:55:25 +0200 Subject: [PATCH] refactored aggregation to own js file to seperate chartStuff from aggregation stuff --- public/js/aggregation.js | 185 +++++++++++++++++++++++++++++++++++++++ public/js/appCesium.js | 13 ++- public/js/appChart.js | 185 --------------------------------------- 3 files changed, 194 insertions(+), 189 deletions(-) create mode 100644 public/js/aggregation.js diff --git a/public/js/aggregation.js b/public/js/aggregation.js new file mode 100644 index 0000000..eb68716 --- /dev/null +++ b/public/js/aggregation.js @@ -0,0 +1,185 @@ + +/** + * + * @param {JSON} obj JSON object on which to replace a specific key. + * @param {String} oldKey is the old key in the JSON to be renamed to newKey + * @param {String} newKey is the key that should replace the oldKey + * usage: myjson.forEach((obj) => renameKey(obj, "oldkey", "newkey")); + */ + + function renameKey(obj, oldKey, newKey) { + obj[newKey] = obj[oldKey]; + delete obj[oldKey]; +} + + +/** + * + * @param {Array} arr is the Array to be converted into a JSON + * @returns {JSON} stringToJsonObject + */ +function convertArray2JSON(arr) { + var arrayToString = JSON.stringify(Object.assign({}, arr)); // convert array to string + var stringToJsonObject = JSON.parse(arrayToString); // convert string to json object + return stringToJsonObject; +} + + +function createDateFromDateTimeString(jsonData) { + let datx = []; + let daty = []; + const MONTH = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + + for (var u = 0; u < jsonData.length; u++) { + daty.push(jsonData[u].temperature); + let date = new Date(jsonData[u].datetime); + let datum = date.getDate(); + let month = MONTH[date.getMonth()]; + let hour = date.getHours() + ":00"; + let newDateStr = datum + "/" + month + "-" + hour; + datx.push(newDateStr); + } + return [datx, daty]; +} + + + + +/** + * Format the response from SensorThings API to make it suitable for heatmap + * @param {Array} obsArray Response from SensorThings API as array + * @param {Int8} hours Number of hours to aggregate over. If hours=0, + * it will be aggregated by date (e.g. all values recorded on 1st of May etc...) + * if hours to any number (also hours=24) data will be aggregated over every 24hours, + * even if the date changes (e.g. data from 1st of May from 10pm up 9pm on 2nd of May etc.) + * @param {String} method Specify how to aggregate date. Use: 'mean' = default, 'sum', 'min' or 'max' + * @returns {Array} Aggregated Response + */ + +export const aggregateResponse = function(obsArray, hours, method) { + if (!obsArray) return; + if (hours < 0) return; + + // check if we have a defined method or the method specified is accepted, rest is handeled in switch/case below + if (method == undefined) method = 'mean'; + + + // convert obsArray to json + + let jsonFromArr = []; + for (var i = 0; i < obsArray.length; i++) { + jsonFromArr.push(convertArray2JSON(obsArray[i])); + } + + // rename the keys in the jason + jsonFromArr.forEach((obj) => renameKey(obj, "0", "datetime")); + let jsonData = jsonFromArr; + jsonData.forEach((obj) => renameKey(obj, "1", "temperature")); + + + let newOutput = []; + var aggDates = []; + var aggregatedVals = []; + var vals = []; // store values temporarily to use for processing + + if (hours == 0) { // i.e. aggregate over one Date / Day + + var currentDate; + var oldDate; + for (var d = 0; d < jsonData.length; d++) { + let tmpDate = new Date(jsonData[d].datetime); + currentDate = tmpDate.getDate(); // gets the day of the month 1...31 + if (d === 0) oldDate = currentDate; + + if (currentDate == oldDate) { + vals.push(jsonData[d].temperature); + } else { + aggDates.push(new Date(tmpDate - 1)); + + if (vals.length == 0) { + aggregatedVals.push(-1); + oldDate = currentDate; + continue; + } + + switch (method) { + case 'mean': + aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); + break; + case 'sum': + aggregatedVals.push(vals.reduce(function(a, b) { return a + b; }, 0)); + break; + case 'min': + aggregatedVals.push(vals.reduce(function(a, b) { return Math.min(a, b); })); + break; + case 'max': + aggregatedVals.push(vals.reduce(function(a, b) { return Math.max(a, b); })); + break; + default: + aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); + } + vals = []; // clear the daily value vector + vals.push(jsonData[d].temperature); // now push first entry of new day into my temp value vector. + } + oldDate = currentDate; + } // end of for loop + + // create output to be in the same List format as the original data from obsArray. + + for (let i = 0; i < aggregatedVals.length; i++) { + newOutput.push([aggDates[i].toISOString(), aggregatedVals[i]]); + } + + } else { // i.e. aggregate over X hours, irrespective of the day. + let cnt = 0; + let cumHours = 0; + + for (var d = 0; d < jsonData.length; d++) { + if (cnt < hours) { + vals.push(jsonData[d].temperature); + cnt++; + } else { + cumHours += cnt; + cnt = 0; + aggDates.push(cumHours); + + if (vals.length == 0) { + aggregatedVals.push(-1); + continue; + } + + switch (method) { + case 'mean': + aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); + break; + case 'sum': + aggregatedVals.push(vals.reduce(function(a, b) { return a + b; }, 0)); + break; + case 'min': + aggregatedVals.push(vals.reduce(function(a, b) { return Math.min(a, b); })); + break; + case 'max': + aggregatedVals.push(vals.reduce(function(a, b) { return Math.max(a, b); })); + break; + default: + aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); + } + vals = []; // clear the daily value vector + vals.push(jsonData[d].temperature); // now push first entry of new day into my temp value vector. + cnt++; + } + oldDate = currentDate; + } // end of for loop + // create output to be in the same List format as the original data from obsArray. + + for (let i = 0; i < aggregatedVals.length; i++) { + newOutput.push([aggDates[i], aggregatedVals[i]]); + } + } // end else + + + + + + return newOutput; +} diff --git a/public/js/appCesium.js b/public/js/appCesium.js index ce706bb..1e9ca94 100644 --- a/public/js/appCesium.js +++ b/public/js/appCesium.js @@ -1,5 +1,11 @@ "use strict"; +// Functions +import { + aggregateResponse, +} from "./aggregation.js"; + + // Functions import { getDatastreamIdFromBuildingNumber, @@ -10,7 +16,6 @@ import { formatSTAResponseForLineChart, drawLineChartHC, followNextLink, - aggregateResponse, } from "./appChart.js"; // Constants @@ -338,10 +343,10 @@ const activate3DTileFeaturePicking = function() { console.log(err); }) .then((observationArr) => { - var agg = aggregateResponse(observationArr, 0, 'mean'); + var agg = aggregateResponse(observationArr, 0, 'sum'); console.log(agg); - drawHeatMapHC(formatSTAResponseForHeatMap(observationArr)); - drawLineChartHC(formatSTAResponseForLineChart(observationArr)); + drawHeatMapHC(formatSTAResponseForHeatMap(agg)); + drawLineChartHC(formatSTAResponseForLineChart(agg)); }); }, Cesium.ScreenSpaceEventType.LEFT_CLICK); diff --git a/public/js/appChart.js b/public/js/appChart.js index 3ab6c6a..bff06e3 100644 --- a/public/js/appChart.js +++ b/public/js/appChart.js @@ -120,191 +120,6 @@ export const PARAM_SELECT = "result,phenomenonTime"; // }); -/** - * - * @param {JSON} obj JSON object on which to replace a specific key. - * @param {String} oldKey is the old key in the JSON to be renamed to newKey - * @param {String} newKey is the key that should replace the oldKey - * usage: myjson.forEach((obj) => renameKey(obj, "oldkey", "newkey")); - */ - -function renameKey(obj, oldKey, newKey) { - obj[newKey] = obj[oldKey]; - delete obj[oldKey]; -} - - -/** - * - * @param {Array} arr is the Array to be converted into a JSON - * @returns {JSON} stringToJsonObject - */ -function convertArray2JSON(arr) { - var arrayToString = JSON.stringify(Object.assign({}, arr)); // convert array to string - var stringToJsonObject = JSON.parse(arrayToString); // convert string to json object - return stringToJsonObject; -} - - -function createDateFromDateTimeString(jsonData) { - let datx = []; - let daty = []; - const MONTH = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - - for (var u = 0; u < jsonData.length; u++) { - daty.push(jsonData[u].temperature); - let date = new Date(jsonData[u].datetime); - let datum = date.getDate(); - let month = MONTH[date.getMonth()]; - let hour = date.getHours() + ":00"; - let newDateStr = datum + "/" + month + "-" + hour; - datx.push(newDateStr); - } - return [datx, daty]; -} - - - - -/** - * Format the response from SensorThings API to make it suitable for heatmap - * @param {Array} obsArray Response from SensorThings API as array - * @param {Int8} hours Number of hours to aggregate over. If hours=0, - * it will be aggregated by date (e.g. all values recorded on 1st of May etc...) - * if hours to any number (also hours=24) data will be aggregated over every 24hours, - * even if the date changes (e.g. data from 1st of May from 10pm up 9pm on 2nd of May etc.) - * @param {String} method Specify how to aggregate date. Use: 'mean' = default, 'sum', 'min' or 'max' - * @returns {Array} Aggregated Response - */ - -export const aggregateResponse = function(obsArray, hours, method) { - if (!obsArray) return; - if (hours < 0) return; - - // check if we have a defined method or the method specified is accepted, rest is handeled in switch/case below - if (method == undefined) method = 'mean'; - - - // convert obsArray to json - - let jsonFromArr = []; - for (var i = 0; i < obsArray.length; i++) { - jsonFromArr.push(convertArray2JSON(obsArray[i])); - } - - // rename the keys in the jason - jsonFromArr.forEach((obj) => renameKey(obj, "0", "datetime")); - let jsonData = jsonFromArr; - jsonData.forEach((obj) => renameKey(obj, "1", "temperature")); - - - let newOutput = []; - var aggDates = []; - var aggregatedVals = []; - var vals = []; // store values temporarily to use for processing - - if (hours == 0) { // i.e. aggregate over one Date / Day - - var currentDate; - var oldDate; - for (var d = 0; d < jsonData.length; d++) { - let tmpDate = new Date(jsonData[d].datetime); - currentDate = tmpDate.getDate(); // gets the day of the month 1...31 - if (d === 0) oldDate = currentDate; - - if (currentDate == oldDate) { - vals.push(jsonData[d].temperature); - } else { - aggDates.push(new Date(tmpDate - 1)); - - if (vals.length == 0) { - aggregatedVals.push(-1); - oldDate = currentDate; - continue; - } - - switch (method) { - case 'mean': - aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); - break; - case 'sum': - aggregatedVals.push(vals.reduce(function(a, b) { return a + b; }, 0)); - break; - case 'min': - aggregatedVals.push(vals.reduce(function(a, b) { return Math.min(a, b); })); - break; - case 'max': - aggregatedVals.push(vals.reduce(function(a, b) { return Math.max(a, b); })); - break; - default: - aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); - } - vals = []; // clear the daily value vector - vals.push(jsonData[d].temperature); // now push first entry of new day into my temp value vector. - } - oldDate = currentDate; - } // end of for loop - - // create output to be in the same List format as the original data from obsArray. - - for (let i = 0; i < aggregatedVals.length; i++) { - newOutput.push([aggDates[i].toISOString(), aggregatedVals[i]]); - } - - } else { // i.e. aggregate over X hours, irrespective of the day. - let cnt = 0; - let cumHours = 0; - - for (var d = 0; d < jsonData.length; d++) { - if (cnt < hours) { - vals.push(jsonData[d].temperature); - cnt++; - } else { - cumHours += cnt; - cnt = 0; - aggDates.push(cumHours); - - if (vals.length == 0) { - aggregatedVals.push(-1); - continue; - } - - switch (method) { - case 'mean': - aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); - break; - case 'sum': - aggregatedVals.push(vals.reduce(function(a, b) { return a + b; }, 0)); - break; - case 'min': - aggregatedVals.push(vals.reduce(function(a, b) { return Math.min(a, b); })); - break; - case 'max': - aggregatedVals.push(vals.reduce(function(a, b) { return Math.max(a, b); })); - break; - default: - aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, 0)); - } - vals = []; // clear the daily value vector - vals.push(jsonData[d].temperature); // now push first entry of new day into my temp value vector. - cnt++; - } - oldDate = currentDate; - } // end of for loop - // create output to be in the same List format as the original data from obsArray. - - for (let i = 0; i < aggregatedVals.length; i++) { - newOutput.push([aggDates[i], aggregatedVals[i]]); - } - } // end else - - - - - - return newOutput; -} - /** * Format the response from SensorThings API to make it suitable for heatmap -- GitLab