"use strict"; // Request parameters // Observations WITHOUT data gap - Bau 225 const BASE_URL = "http://193.196.39.91:8080/frost-icity-tp31/v1.1/Datastreams(80)/Observations"; const PARAM_RESULT_FORMAT = "dataArray"; const PARAM_ORDER_BY = "phenomenonTime asc"; const PARAM_FILTER = "resultTime ge 2020-01-01T00:00:00.000Z and resultTime le 2021-01-01T00:00:00.000Z"; const PARAM_SELECT = "result,phenomenonTime"; // Observations WITH data gap - Bau 112 const BASE_URL2 = "http://193.196.39.91:8080/frost-icity-tp31-v2/v1.1/Datastreams(78)/Observations"; const PARAM_FILTER2 = "resultTime ge 2020-06-01T00:00:00.000Z and resultTime le 2021-01-01T00:00:00.000Z"; /** * Format the response from SensorThings API to make it suitable for heatmap * @param {*} obsArray - Response from SensorThings API as array * @returns {Array} */ const formatSTAResponseForHeatMap = function (obsArray) { const dataSTAFormatted = []; obsArray.forEach((obs) => { // Get the date/time string; first element in input array; remove trailing "Z" const obsDateTimeInput = obs[0].slice(0, -1); // Get the "date" part of an observation const obsDateInput = obs[0].slice(0, 10); // Create Date objects const obsDateTime = new Date(obsDateTimeInput); const obsDate = new Date(obsDateInput); // x-axis -> timestamp; will be the same for observations from the same date const timestamp = Date.parse(obsDate); // y-axis -> hourOfDay const hourOfDay = obsDateTime.getHours(); // value -> the observation's value; second element in input array const value = obs[1]; dataSTAFormatted.push([timestamp, hourOfDay, value]); }); return dataSTAFormatted; }; /** * Draw a heatmap using Highcharts library * @param {*} obsArray - Response from SensorThings API * @returns {void} */ const drawHeatMapHC = function (obsArray) { Highcharts.chart("chart-heatmap", { chart: { type: "heatmap", zoomType: "x", }, boost: { useGPUTranslations: true, }, title: { text: "Inlet flow (Vorlauf)", align: "left", x: 40, }, subtitle: { text: "Temperature variation by day and hour in 2020", align: "left", x: 40, }, xAxis: { type: "datetime", // min: Date.UTC(2017, 0, 1), // max: Date.UTC(2017, 11, 31, 23, 59, 59), labels: { align: "left", x: 5, y: 14, format: "{value:%B}", // long month }, showLastLabel: false, tickLength: 16, }, yAxis: { title: { text: null, }, labels: { format: "{value}:00", }, minPadding: 0, maxPadding: 0, startOnTick: false, endOnTick: false, // tickPositions: [0, 6, 12, 18, 24], tickPositions: [0, 3, 6, 9, 12, 15, 18, 21, 24], tickWidth: 1, min: 0, max: 23, reversed: true, }, colorAxis: { stops: [ [0, "#3060cf"], [0.5, "#fffbbc"], [0.9, "#c4463a"], [1, "#c4463a"], ], min: 60, max: 85, startOnTick: false, endOnTick: false, labels: { format: "{value}℃", }, }, series: [ { data: formatSTAResponseForHeatMap(obsArray), boostThreshold: 100, borderWidth: 0, nullColor: "#525252", colsize: 24 * 36e5, // one day tooltip: { headerFormat: "Temperature
", pointFormat: "{point.x:%e %b, %Y} {point.y}:00: {point.value} ℃", }, turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release }, ], }); }; /** * Convert the observations' phenomenonTime from an ISO 8601 string to Unix epoch * @param {*} obsArray - Response from SensorThings API as array * @returns {Array} */ const formatSTAResponseForLineChart = function (obsArray) { const dataSTAFormatted = []; obsArray.forEach((result) => { const timestampObs = new Date(result[0].slice(0, -1)).getTime(); // slice() removes trailing "Z" character in timestamp const valueObs = result[1]; dataSTAFormatted.push([timestampObs, valueObs]); }); return dataSTAFormatted; }; /** * Draw a line chart using Highcharts library * @param {*} obsArray - Response from SensorThings API * @returns {void} */ const drawLineChartHC = function (obsArray) { // Create the chart Highcharts.stockChart("chart-line", { chart: { zoomType: "x", }, rangeSelector: { selected: 1, }, title: { text: "AAPL Stock Price", }, series: [ { name: "AAPL", data: formatSTAResponseForLineChart(obsArray), tooltip: { valueDecimals: 2, }, turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release }, ], }); }; /** * Follows "@iot.nextLink" links in SensorThingsAPI's response * Appends new results to existing results * @async * @param {Object} responsePromise - Promise object * @returns {Object} - Object containing results from all the "@iot.nextLink" links */ const followNextLink = function (responsePromise) { return responsePromise .then(function (lastSuccess) { if (lastSuccess.data["@iot.nextLink"]) { return followNextLink( axios.get(lastSuccess.data["@iot.nextLink"]) ).then(function (nextLinkSuccess) { nextLinkSuccess.data.value = lastSuccess.data.value.concat( nextLinkSuccess.data.value ); return nextLinkSuccess; }); } else { return lastSuccess; } }) .catch(function (err) { console.log(err); }); }; // Get "ALL" the Observations that satisfy our query followNextLink( axios.get(BASE_URL, { params: { "$resultFormat": PARAM_RESULT_FORMAT, "$orderBy": PARAM_ORDER_BY, "$filter": PARAM_FILTER, "$select": PARAM_SELECT, }, }) ) .then((success) => { const successValue = success.data.value; // Array that will hold the combined observations const combinedObservations = []; successValue.forEach((dataObj) => { // Each page of results will have a dataArray that holds the observations const dataArrays = dataObj.dataArray; combinedObservations.push(...dataArrays); }); // DEBUG: Check total number of observations console.log(combinedObservations.length); // DEBUG: Print the array of observations console.log(combinedObservations); return combinedObservations; }) .catch((err) => { console.log(err); }) .then((observationArr) => { drawHeatMapHC(observationArr); drawLineChartHC(observationArr); });