Commit 8df4c888 authored by Pithon Kabiro's avatar Pithon Kabiro
Browse files

Add calculation of average to aggregation logic

parent 7196482d
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
<script src="https://code.highcharts.com/modules/boost-canvas.js"></script> <script src="https://code.highcharts.com/modules/boost-canvas.js"></script>
<script src="https://code.highcharts.com/modules/boost.js"></script> <script src="https://code.highcharts.com/modules/boost.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script> <script src="https://code.highcharts.com/modules/accessibility.js"></script>
<script src="https://code.highcharts.com/modules/offline-exporting.js"></script>
<!-- Cesium --> <!-- Cesium -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.48/Build/Cesium/Cesium.js"></script> <script src="https://cesium.com/downloads/cesiumjs/releases/1.48/Build/Cesium/Cesium.js"></script>
......
"use strict"; "use strict";
import { BASE_URL, QUERY_PARAMS_COMBINED } from "./src_modules/createUrl.js"; import {
BASE_URL,
QUERY_PARAMS_COMBINED,
} from "./src_modules/baseUrlPlusQueryParams.mjs";
import { import {
formatSensorThingsApiResponseForLineChart, formatSensorThingsApiResponseForLineChart,
drawLineChartHighcharts, drawLineChartHighcharts,
} from "./src_modules/chartLine.js"; } from "./src_modules/chartLine.mjs";
import { import {
formatSensorThingsApiResponseForHeatMap, formatSensorThingsApiResponseForHeatMap,
drawHeatMapHighcharts, drawHeatMapHighcharts,
} from "./src_modules/chartHeatmap.js"; } from "./src_modules/chartHeatmap.mjs";
import { import {
formatSensorThingsApiResponseForScatterPlot, formatSensorThingsApiResponseForScatterPlot,
drawScatterPlotHighcharts, drawScatterPlotHighcharts,
} from "./src_modules/chartScatterPlot.js"; } from "./src_modules/chartScatterPlot.mjs";
import { import {
formatAggregationResultForColumnChart, formatAggregationResultForColumnChart,
drawColumnChartHighcharts, drawColumnChartHighcharts,
} from "./src_modules/chartColumn.js"; } from "./src_modules/chartColumn.mjs";
import { import {
formatDatastreamMetadataForChart, formatDatastreamMetadataForChart,
extractPropertiesFromFormattedDatastreamMetadata, extractPropertiesFromFormattedDatastreamMetadata,
getMetadataPlusObservationsFromSingleOrMultipleDatastreams, getMetadataPlusObservationsFromSingleOrMultipleDatastreams,
calculateVorlaufMinusRuecklaufTemperature, } from "./src_modules/fetchData.mjs";
} from "./src_modules/fetchData.js";
import { calculateVorlaufMinusRuecklaufTemperature } from "./src_modules/calculateTemperatureDiff.mjs";
import { import {
calculateSumOfObservationValuesWithinDatesInterval, calculateSumOfObservationValuesWithinInterval,
calculateSumOfObservationValuesWithinMonthInterval,
extractUniqueCalendarDatesFromTimestamp, extractUniqueCalendarDatesFromTimestamp,
extractUniqueCalendarMonthsFromCalendarDates, extractUniqueCalendarMonthsFromCalendarDates,
} from "./src_modules/aggregate.js"; calculateAverageOfObservationValuesWithinInterval,
} from "./src_modules/aggregate.mjs";
/** /**
* Test plotting of temp difference (dT) using heatmap * Test plotting of temp difference (dT) using heatmap
...@@ -169,7 +173,7 @@ const testLineChartMultipleSeries = async function () { ...@@ -169,7 +173,7 @@ const testLineChartMultipleSeries = async function () {
}; };
/** /**
* Test drawing of column chart using aggregation result * Test drawing of column chart using aggregation / sum result - monthly
*/ */
const drawColumnChartMonthlySumTest = async function () { const drawColumnChartMonthlySumTest = async function () {
const sensorsOfInterestNestedArr = [ const sensorsOfInterestNestedArr = [
...@@ -200,42 +204,213 @@ const drawColumnChartMonthlySumTest = async function () { ...@@ -200,42 +204,213 @@ const drawColumnChartMonthlySumTest = async function () {
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr) extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
); );
// Calculate sum of values of observations - daily // Calculate sum of values of observations - monthly
// Note the two nested `map` methods const observationsSumMonthlyNestedArr =
const observationsSumDailyNestedArr = uniqueCalendarDatesNestedArr.map( calculateSumOfObservationValuesWithinInterval(
(uniqueCalendarDatesArr, i) => observationsNestedArr,
uniqueCalendarDatesArr.map((uniqueCalendarDate) => "60min",
calculateSumOfObservationValuesWithinDatesInterval( uniqueCalendarMonthsNestedArr,
observationsNestedArr[i], "monthly"
"60 min", );
uniqueCalendarDate,
uniqueCalendarDate // Format the observations
) const formattedObservationsSumMonthlyNestedArr =
observationsSumMonthlyNestedArr.map((obsSumMonthlyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i],
obsSumMonthlyArr
) )
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
); );
// Calculate sum of values of observations - monthly // Extract the formatted metadata properties
// Note the two nested `map` methods const extractedFormattedDatastreamProperties =
const observationsSumMonthlyNestedArr = uniqueCalendarMonthsNestedArr.map( extractPropertiesFromFormattedDatastreamMetadata(
(uniqueCalendarMonthsArr, i) => formattedMetadataNestedArr
uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => );
calculateSumOfObservationValuesWithinMonthInterval(
observationsNestedArr[i], drawColumnChartHighcharts(
"60 min", formattedObservationsSumMonthlyNestedArr,
uniqueCalendarMonth extractedFormattedDatastreamProperties
) );
};
/**
* Test drawing of column chart using aggregation / sum result - daily
*/
const drawColumnChartDailySumTest = async function () {
const sensorsOfInterestNestedArr = [
["125", "vl", "60min"],
["225", "vl", "60min"],
];
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// Unique calendar dates
const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
);
// Calculate sum of values of observations - daily
const observationsSumDailyNestedArr =
calculateSumOfObservationValuesWithinInterval(
observationsNestedArr,
"60min",
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - daily
const formattedObservationsSumDailyNestedArr =
observationsSumDailyNestedArr.map((obsSumDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsSumDailyArr
) )
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsSumDailyNestedArr,
extractedFormattedDatastreamProperties
);
};
/**
* Test drawing of line chart using aggregation / average result - monthly
*/
const drawLineChartMonthlyAverageTest = async function () {
const sensorsOfInterestNestedArr = [
["125", "vl", "60min"],
["225", "vl", "60min"],
];
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// Unique calendar dates
const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
); );
// Unique calendar months
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
(uniqueCalendarDatesArr) =>
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
);
// Calculate average of values of observations - monthly
const observationsAverageMonthlyNestedArr =
calculateAverageOfObservationValuesWithinInterval(
observationsNestedArr,
"60min",
uniqueCalendarMonthsNestedArr,
"monthly"
);
// Format the observations // Format the observations
const formattedObservationsNestedArr = observationsSumMonthlyNestedArr.map( const formattedObservationsAverageMonthlyNestedArr =
(obsSumMonthlyArr, i) => observationsAverageMonthlyNestedArr.map((obsAverageMonthlyArr, i) =>
formatAggregationResultForColumnChart( formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i], uniqueCalendarMonthsNestedArr[i],
obsSumMonthlyArr obsAverageMonthlyArr
) )
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsAverageMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
};
/**
* Test drawing of line chart using aggregation / average result - daily
*/
const drawLineChartDailyAverageTest = async function () {
const sensorsOfInterestNestedArr = [
["125", "vl", "60min"],
["225", "vl", "60min"],
];
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// Unique calendar dates
const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
); );
// Calculate average of values of observations - daily
const observationsAverageDailyNestedArr =
calculateAverageOfObservationValuesWithinInterval(
observationsNestedArr,
"60min",
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - daily
const formattedObservationsAverageDailyNestedArr =
observationsAverageDailyNestedArr.map((obsAverageDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsAverageDailyArr
)
);
// Format the metadata // Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) => const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj) formatDatastreamMetadataForChart(metadataObj)
...@@ -247,8 +422,8 @@ const drawColumnChartMonthlySumTest = async function () { ...@@ -247,8 +422,8 @@ const drawColumnChartMonthlySumTest = async function () {
formattedMetadataNestedArr formattedMetadataNestedArr
); );
drawColumnChartHighcharts( drawLineChartHighcharts(
formattedObservationsNestedArr, formattedObservationsAverageDailyNestedArr,
extractedFormattedDatastreamProperties extractedFormattedDatastreamProperties
); );
}; };
...@@ -257,3 +432,6 @@ const drawColumnChartMonthlySumTest = async function () { ...@@ -257,3 +432,6 @@ const drawColumnChartMonthlySumTest = async function () {
// drawHeatmapHCUsingTempDifference(); // drawHeatmapHCUsingTempDifference();
// testLineChartMultipleSeries(); // testLineChartMultipleSeries();
// drawColumnChartMonthlySumTest(); // drawColumnChartMonthlySumTest();
// drawColumnChartDailySumTest();
// drawLineChartMonthlyAverageTest();
// drawLineChartDailyAverageTest();
"use strict"; "use strict";
import { BASE_URL, QUERY_PARAMS_COMBINED } from "./src_modules/createUrl.js"; import {
BASE_URL,
QUERY_PARAMS_COMBINED,
} from "./src_modules/baseUrlPlusQueryParams.mjs";
import { import {
formatDatastreamMetadataForChart, formatDatastreamMetadataForChart,
extractPropertiesFromFormattedDatastreamMetadata, extractPropertiesFromFormattedDatastreamMetadata,
getMetadataPlusObservationsFromSingleOrMultipleDatastreams, getMetadataPlusObservationsFromSingleOrMultipleDatastreams,
} from "./src_modules/fetchData.js"; } from "./src_modules/fetchData.mjs";
import { import {
formatSensorThingsApiResponseForLineChart, formatSensorThingsApiResponseForLineChart,
drawLineChartHighcharts, drawLineChartHighcharts,
} from "./src_modules/chartLine.js"; } from "./src_modules/chartLine.mjs";
import { import {
formatSensorThingsApiResponseForHeatMap, formatSensorThingsApiResponseForHeatMap,
drawHeatMapHighcharts, drawHeatMapHighcharts,
} from "./src_modules/chartHeatmap.js"; } from "./src_modules/chartHeatmap.mjs";
import { import {
showLoadingSpinner, showLoadingSpinner,
hideLoadingSpinner, hideLoadingSpinner,
} from "./src_modules/loadingIndicator.js"; } from "./src_modules/loadingIndicator.mjs";
const buildingsAvailableSensorsArr = [ const buildingsAvailableSensorsArr = [
["--Select--", "", ""], ["--Select--", "", ""],
...@@ -276,13 +279,13 @@ const selectChartTypeFromDropDown = async function () { ...@@ -276,13 +279,13 @@ const selectChartTypeFromDropDown = async function () {
const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata; const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// Create formatted array(s) for observations - line chart // Create formatted array(s) for observations - line chart
const formattedObsLineChartArr = observationsNestedArr.map( const formattedObsLineChartNestedArr = observationsNestedArr.map(
(observationsArr) => (observationsArr) =>
formatSensorThingsApiResponseForLineChart(observationsArr) formatSensorThingsApiResponseForLineChart(observationsArr)
); );
// Create formatted array(s) for observations - heatmap // Create formatted array(s) for observations - heatmap
const formattedObsHeatMapArr = observationsNestedArr.map( const formattedObsHeatMapNestedArr = observationsNestedArr.map(
(observationsArr) => (observationsArr) =>
formatSensorThingsApiResponseForHeatMap(observationsArr) formatSensorThingsApiResponseForHeatMap(observationsArr)
); );
...@@ -298,14 +301,14 @@ const selectChartTypeFromDropDown = async function () { ...@@ -298,14 +301,14 @@ const selectChartTypeFromDropDown = async function () {
if (selectedChartType === "Line") { if (selectedChartType === "Line") {
drawLineChartHighcharts( drawLineChartHighcharts(
formattedObsLineChartArr, formattedObsLineChartNestedArr,
extractedFormattedDatastreamProperties extractedFormattedDatastreamProperties
); );
} else if (selectedChartType === "Heatmap") { } else if (selectedChartType === "Heatmap") {
// First need to extract the formatted observations from the nested array // First need to extract the formatted observations from the nested array
// Heatmap only needs one set of formatted observation values // Heatmap only needs one set of formatted observation values
drawHeatMapHighcharts( drawHeatMapHighcharts(
...formattedObsHeatMapArr, ...formattedObsHeatMapNestedArr,
extractedFormattedDatastreamProperties extractedFormattedDatastreamProperties
); );
} }
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
/** /**
* Create 24-hour time strings for a time interval delimited by a start time and an end time. It is assumed that the start time is at "00:00:00" and the end time is at "23:45:00" (when the sampling rate of observations is 15 min) or "23:00:00" (when the sampling rate of observations is 60 min) * Create 24-hour time strings for a time interval delimited by a start time and an end time. It is assumed that the start time is at "00:00:00" and the end time is at "23:45:00" (when the sampling rate of observations is 15 min) or "23:00:00" (when the sampling rate of observations is 60 min)
* @param {String} phenomenonSamplingRate The sampling rate of the phenomenon of interest represented as a string, e.g. "15 min", "60 min" * @param {String} phenomenonSamplingRate The sampling rate of the phenomenon of interest represented as a string, e.g. "15min", "60min"
* @returns {Array} An array of two 24-hour strings representing the start time and end time * @returns {Array} An array of two 24-hour strings representing the start time and end time
*/ */
const createTimeStringsForInterval = function (phenomenonSamplingRate) { const createTimeStringsForInterval = function (phenomenonSamplingRate) {
const fifteenMinutes = "15 min"; const fifteenMinutes = "15min";
const sixtyMinutes = "60 min"; const sixtyMinutes = "60min";
const startTime = "00:00:00"; const startTime = "00:00:00";
const endTimeFifteenMinutes = "23:45:00"; const endTimeFifteenMinutes = "23:45:00";
...@@ -17,7 +17,9 @@ const createTimeStringsForInterval = function (phenomenonSamplingRate) { ...@@ -17,7 +17,9 @@ const createTimeStringsForInterval = function (phenomenonSamplingRate) {
phenomenonSamplingRate !== fifteenMinutes && phenomenonSamplingRate !== fifteenMinutes &&
phenomenonSamplingRate !== sixtyMinutes phenomenonSamplingRate !== sixtyMinutes
) )
return; throw new Error(
`Check that the provided phenomenon sampling rate string is in this format: "15min" or "60min"`
);
// 15 min sampling rate // 15 min sampling rate
if (phenomenonSamplingRate === fifteenMinutes) { if (phenomenonSamplingRate === fifteenMinutes) {
...@@ -49,7 +51,7 @@ const createIso8601DateTimeString = function ( ...@@ -49,7 +51,7 @@ const createIso8601DateTimeString = function (
* @returns {Boolean} true if leap year, false if not * @returns {Boolean} true if leap year, false if not
*/ */
const checkIfLeapYear = function (year) { const checkIfLeapYear = function (year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}; };
/** /**
...@@ -74,28 +76,22 @@ const getIndexOfTimestamp = function (inputTimestampArr, timestampOfInterest) { ...@@ -74,28 +76,22 @@ const getIndexOfTimestamp = function (inputTimestampArr, timestampOfInterest) {
}; };
/** /**
* Calculate the sum of observation values within a time interval delimited by a start date and end date. The start date may be the same as the end date. * Calculate the indexes of the start and end timestamps
* @param {Array} obsArray An array of observations (timestamp + value) that is response from SensorThings API * @param {Array} obsTimestampArr An array of observations timestamps
* @param {String} samplingRate The sampling rate of observations as a string, e.g. "15 min", "60 min" * @param {String} samplingRate The sampling rate of observations as a string, e.g. "15min", "60min"
* @param {String} startDate A 24-hour date string representing the start date * @param {String} startDate A 24-hour date string representing the start date
* @param {String} endDate A 24-hour date string representing the end date * @param {String} endDate A 24-hour date string representing the end date
* @returns {Number} A floating-point number representing the sum of observation values * @returns {Array} A 1*2 array tht contains integers representing the start index and end index respectively
*/ */
const calculateSumOfObservationValuesWithinDatesInterval = function ( const calculateIndexStartEndTimestamp = function (
obsArray, obsTimestampArr,
samplingRate, samplingRate,
startDate, startDate,
endDate endDate
) { ) {
// Extract the timestamps and values from the observations // Create and extract 24-hour strings for the start and end of interval
const obsTimestampArr = obsArray.map((obs) => obs[0]); const [startTimeString, endTimeString] =
const obsValuesArr = obsArray.map((obs) => obs[1]); createTimeStringsForInterval(samplingRate);
// Create array of 24-hour strings for the start and end of interval
const startPlusEndTimeStrings = createTimeStringsForInterval(samplingRate);
// Extract 24-hour strings for the start and end of interval
const [startTimeString, endTimeString] = startPlusEndTimeStrings;
// Create ISO 8601 strings for the start and end of interval // Create ISO 8601 strings for the start and end of interval
const startIso8601DateTimeString = createIso8601DateTimeString( const startIso8601DateTimeString = createIso8601DateTimeString(
...@@ -117,27 +113,74 @@ const calculateSumOfObservationValuesWithinDatesInterval = function ( ...@@ -117,27 +113,74 @@ const calculateSumOfObservationValuesWithinDatesInterval = function (
endIso8601DateTimeString endIso8601DateTimeString
); );
return [indexStartTimestamp, indexEndTimestamp];
};
/**
* Extract the set of observation values that fall within a time interval delimited by a start date and end date. The start date may be the same as the end date.
* @param {Array} obsArray An array of observations (timestamp + value) that is response from SensorThings API
* @param {String} samplingRate The sampling rate of observations as a string, e.g. "15min", "60min"
* @param {String} startDate A 24-hour date string representing the start date
* @param {String} endDate A 24-hour date string representing the end date
* @returns {Array} An array of observation values that fall within our time interval
*/
const extractObservationValuesWithinDatesInterval = function (
obsArray,
samplingRate,
startDate,
endDate
) {
// Extract the timestamps and values from the observations
const obsTimestampArr = obsArray.map((obs) => obs[0]);
const obsValuesArr = obsArray.map((obs) => obs[1]);
// Calculate the indexes of the timestamps for the start and end of interval
const [indexStartTimestamp, indexEndTimestamp] =
calculateIndexStartEndTimestamp(
obsTimestampArr,
samplingRate,
startDate,
endDate
);
// Extract the observations that fall within our time interval // Extract the observations that fall within our time interval
const obsValuesForTimeIntervalArr = obsValuesArr.slice( return obsValuesArr.slice(indexStartTimestamp, indexEndTimestamp + 1);
indexStartTimestamp, };
indexEndTimestamp + 1
);
return obsValuesForTimeIntervalArr.reduce( /**
* Calculate the sum of observation values that fall within a time interval delimited by a start date and end date
* @param {Array} obsValuesForDaysIntervalArr An array of observation values that fall within our time interval
* @returns {Number} A floating-point number representing the sum of observation values
*/
const calculateSumOfObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
return obsValuesForDaysIntervalArr.reduce(
(accumulator, currentValue) => accumulator + currentValue (accumulator, currentValue) => accumulator + currentValue
); );
}; };
/** /**
* Calculate the sum of observation values within a time interval delimited by the start date and end date of a calendar month * Calculate the average (arithmetic mean) of observation values that fall within a time interval delimited by a start date and end date
* @param {Array} obsArray An array of observations (timestamp + value) that is response from SensorThings API * @param {Array} obsValuesForDaysIntervalArr An array of observation values that fall within our time interval
* @param {String} samplingRate The sampling rate of observations as a string, e.g. "15 min", "60 min" * @returns {Number} A floating-point number representing the average (arithmetic mean) of observation values
*/
const calculateAverageOfObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
return (
calculateSumOfObservationValuesWithinDatesInterval(
obsValuesForDaysIntervalArr
) / obsValuesForDaysIntervalArr.length
);
};
/**
* Extract the year and month digits from a calendar month string
* @param {String} calendarMonthStr Calendar month string in "YYYY-MM" format * @param {String} calendarMonthStr Calendar month string in "YYYY-MM" format
* @returns {Number} A floating-point number representing the sum of observation values * @returns {Array} A 1*2 array tht contains integers representing the year and month respectively
*/ */
const calculateSumOfObservationValuesWithinMonthInterval = function ( const extractMonthYearDigitsFromCalendarMonthString = function (
obsArray,
samplingRate,
calendarMonthStr calendarMonthStr
) { ) {
// Extract year as integer // Extract year as integer
...@@ -146,6 +189,25 @@ const calculateSumOfObservationValuesWithinMonthInterval = function ( ...@@ -146,6 +189,25 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
// Extract month as integer // Extract month as integer
const monthNum = parseInt(calendarMonthStr.slice(-2), 10); const monthNum = parseInt(calendarMonthStr.slice(-2), 10);
return [yearNum, monthNum];
};
/**
* Extract the set of observation values that fall within a time interval delimited by the first day and last day of a calendar month
* @param {Array} obsArray An array of observations (timestamp + value) that is response from SensorThings API
* @param {String} samplingRate The sampling rate of observations as a string, e.g. "15min", "60min"
* @param {String} calendarMonthStr Calendar month string in "YYYY-MM" format
* @returns {Array} An array of observation values that fall within one calendar month
*/
const extractObservationValuesWithinMonthInterval = function (
obsArray,
samplingRate,
calendarMonthStr
) {
// Extract the year and month digits from the calendar month string
const [yearNum, monthNum] =
extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr);
if (monthNum < 1 || monthNum > 12) return; if (monthNum < 1 || monthNum > 12) return;
// All the months start on the first // All the months start on the first
...@@ -155,7 +217,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function ( ...@@ -155,7 +217,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
if (monthNum === 2) { if (monthNum === 2) {
// Leap year // Leap year
if (checkIfLeapYear(yearNum)) if (checkIfLeapYear(yearNum))
return calculateSumOfObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
...@@ -163,7 +225,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function ( ...@@ -163,7 +225,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
); );
// Non-leap year // Non-leap year
return calculateSumOfObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
...@@ -173,7 +235,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function ( ...@@ -173,7 +235,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
// Months with 30 days // Months with 30 days
if (monthNum === 4 || monthNum === 6 || monthNum === 9 || monthNum === 11) if (monthNum === 4 || monthNum === 6 || monthNum === 9 || monthNum === 11)
return calculateSumOfObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
...@@ -181,7 +243,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function ( ...@@ -181,7 +243,7 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
); );
// Months with 31 days // Months with 31 days
return calculateSumOfObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
...@@ -189,6 +251,130 @@ const calculateSumOfObservationValuesWithinMonthInterval = function ( ...@@ -189,6 +251,130 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
); );
}; };
/**
* Calculate the sum of observation values that fall within a time interval delimited by the first day and last day of a calendar month
* @param {Array} obsValuesForMonthIntervalArr An array of observation values that fall within one calendar month
* @returns {Number} A floating-point number representing the sum of observation values within one calendar month
*/
const calculateSumOfObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
return obsValuesForMonthIntervalArr.reduce(
(accumulator, currentValue) => accumulator + currentValue
);
};
/**
* Calculate the average (arithmetic mean) of observation values that fall within a time interval delimited by the first day and last day of a calendar month
* @param {Array} obsValuesForMonthIntervalArr An array of observation values that fall within one calendar month
* @returns {Number} A floating-point number representing the average (arithmetic mean) of observation values within one calendar month
*/
const calculateAverageOfObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
return (
calculateSumOfObservationValuesWithinMonthInterval(
obsValuesForMonthIntervalArr
) / obsValuesForMonthIntervalArr.length
);
};
/**
* Calculate the sum of observation values within a time interval delimited by a start date and end date. The time interval may be daily or monthly
* @param {Array} obsNestedArr A 1*N array that contains N nested arrays of observations (timestamp + value)
* @param {String} samplingRate The sampling rate of observations as a string, e.g. "15min", "60min"
* @param {Array} uniqueCalendarDatesOrMonthsArr A 1*N array of unique calendar dates or calendar months strings
* @param {String} aggregationInterval The aggregation interval as a string e.g. "daily", "monthly"
* @returns {Array} A 1*N array that contains N nested arrays of sum values
*/
const calculateSumOfObservationValuesWithinInterval = function (
obsNestedArr,
samplingRate,
uniqueCalendarDatesOrMonthsArr,
aggregationInterval
) {
// Calculate sum of values of observations - daily
// Note the use of the two nested `map` methods
if (aggregationInterval === "daily") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarDatesArr, i) =>
uniqueCalendarDatesArr.map((uniqueCalendarDate) =>
calculateSumOfObservationValuesWithinDatesInterval(
extractObservationValuesWithinDatesInterval(
obsNestedArr[i],
samplingRate,
uniqueCalendarDate,
uniqueCalendarDate
)
)
)
);
}
// Calculate sum of values of observations - monthly
// Note the use of the two nested `map` methods
if (aggregationInterval === "monthly") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
calculateSumOfObservationValuesWithinMonthInterval(
extractObservationValuesWithinMonthInterval(
obsNestedArr[i],
samplingRate,
uniqueCalendarMonth
)
)
)
);
}
};
/**
* Calculate the average (arithmetic mean) of observation values within a time interval delimited by a start date and end date. The time interval may be daily or monthly
* @param {Array} obsNestedArr A 1*N array that contains N nested arrays of observations (timestamp + value)
* @param {String} samplingRate The sampling rate of observations as a string, e.g. "15min", "60min"
* @param {Array} uniqueCalendarDatesOrMonthsArr A 1*N array of unique calendar dates or calendar months strings
* @param {String} aggregationInterval The aggregation interval as a string e.g. "daily", "monthly"
* @returns {Array} A 1*N array that contains N nested arrays of average (arithmetic mean) values
*/
const calculateAverageOfObservationValuesWithinInterval = function (
obsNestedArr,
samplingRate,
uniqueCalendarDatesOrMonthsArr,
aggregationInterval
) {
// Calculate average of values of observations - daily
// Note the use of the two nested `map` methods
if (aggregationInterval === "daily") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarDatesArr, i) =>
uniqueCalendarDatesArr.map((uniqueCalendarDate) =>
calculateAverageOfObservationValuesWithinDatesInterval(
extractObservationValuesWithinDatesInterval(
obsNestedArr[i],
samplingRate,
uniqueCalendarDate,
uniqueCalendarDate
)
)
)
);
}
// Calculate average of values of observations - monthly
// Note the use of the two nested `map` methods
if (aggregationInterval === "monthly") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
calculateAverageOfObservationValuesWithinMonthInterval(
extractObservationValuesWithinMonthInterval(
obsNestedArr[i],
samplingRate,
uniqueCalendarMonth
)
)
)
);
}
};
/** /**
* Extract unique calendar dates from date/time strings in ISO 8601 format * Extract unique calendar dates from date/time strings in ISO 8601 format
* @param {Array} obsArray An array of observations (timestamp + value) that is response from SensorThings API * @param {Array} obsArray An array of observations (timestamp + value) that is response from SensorThings API
...@@ -227,8 +413,8 @@ const extractUniqueCalendarMonthsFromCalendarDates = function ( ...@@ -227,8 +413,8 @@ const extractUniqueCalendarMonthsFromCalendarDates = function (
}; };
export { export {
calculateSumOfObservationValuesWithinDatesInterval, calculateSumOfObservationValuesWithinInterval,
calculateSumOfObservationValuesWithinMonthInterval,
extractUniqueCalendarDatesFromTimestamp, extractUniqueCalendarDatesFromTimestamp,
extractUniqueCalendarMonthsFromCalendarDates, extractUniqueCalendarMonthsFromCalendarDates,
calculateAverageOfObservationValuesWithinInterval,
}; };
"use strict";
import {
extractPhenomenonNameFromDatastreamName,
getMetadataPlusObservationsFromSingleOrMultipleDatastreams,
} from "./fetchData.mjs";
/**
* Calculate the temperature difference, dT, between Vorlauf temperature [VL] and Rücklauf temperature [RL] (i.e., dT = VL - RL)
* @param {String} baseUrl Base URL of the STA server
* @param {Object} urlParams The URL parameters to be sent together with the GET request
* @param {String} buildingId The building ID as a string
* @param {String} samplingRate The sampling rate as a string
* @returns {Promise} A promise that contains an array (that is made up of a temperature difference array and a metadata object) when fulfilled
*/
export const calculateVorlaufMinusRuecklaufTemperature = async function (
baseUrl,
urlParams,
buildingId,
samplingRate
) {
try {
const bldgSensorSamplingRateNestedArr = [
[buildingId, "vl", samplingRate],
[buildingId, "rl", samplingRate],
];
const BUILDING_ID = buildingId;
const SAMPLING_RATE = samplingRate;
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
baseUrl,
urlParams,
bldgSensorSamplingRateNestedArr
);
// Extract Vorlauf temperature, Ruecklauf temperature and metadata
const [
[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr],
[metadataVorlauf, metadataRuecklauf],
] = observationsPlusMetadata;
// Extract the temperature values
const vorlaufTemperatureValues = vorlaufTemperatureObsArr.map(
(vlTempObs) => vlTempObs[1]
);
const ruecklaufTemperatureValues = ruecklaufTemperatureObsArr.map(
(rlTempObs) => rlTempObs[1]
);
// The arrays have equal length, we need only use one of them for looping
// Resulting array contains the following pairs (timestamp + dT)
const vorlaufMinusRuecklaufTemperatureObs = vorlaufTemperatureObsArr.map(
(vlTempObs, i) => [
vlTempObs[0], // timestamp
vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i],
]
);
// From Vorlauf metadata, extract `name` and `unitOfMeasurement`
const {
name: datastreamNameVorlauf,
unitOfMeasurement: unitOfMeasurementVorlauf,
} = metadataVorlauf;
// From Ruecklauf metadata, extract `name`
const { name: datastreamNameRuecklauf } = metadataRuecklauf;
// Extract the phenomenon names from the Datastream names
const phenomenonNameVorlauf = extractPhenomenonNameFromDatastreamName(
datastreamNameVorlauf
);
const phenomenonNameRuecklauf = extractPhenomenonNameFromDatastreamName(
datastreamNameRuecklauf
);
// Create our custom datastream description text
// The resulting datastream description string has two `temperature` substrings;
// replace the first occurence with an empty string
const descriptionTempDifference =
`Computed dT: ${phenomenonNameVorlauf} minus ${phenomenonNameRuecklauf}`.replace(
"temperature",
""
);
// Create our custom datastream name text
const nameTempDifference = `BOSCH_${BUILDING_ID} / dT Temperature difference (VL-RL) DS:${SAMPLING_RATE}`;
// The datastream object that we return needs to have these property names
const description = descriptionTempDifference;
const name = nameTempDifference;
const unitOfMeasurement = unitOfMeasurementVorlauf;
return [
vorlaufMinusRuecklaufTemperatureObs,
{
description,
name,
unitOfMeasurement,
},
];
} catch (err) {
console.error(err);
}
};
"use strict";
import { chartExportOptions } from "./chartExport.mjs";
/** /**
* Format a computed aggregation result to make it suitable for a column chart * Format a computed aggregation result to make it suitable for a column chart
* @param {Array} calendarDatesMonthsStrArr An array of unique calendar dates strings (in "YYYY-MM-DD" fromat) or unique calendar months strings (in "YYYY-MM" format) * @param {Array} calendarDatesMonthsStrArr An array of unique calendar dates strings (in "YYYY-MM-DD" fromat) or unique calendar months strings (in "YYYY-MM" format)
...@@ -123,6 +127,8 @@ const drawColumnChartHighcharts = function ( ...@@ -123,6 +127,8 @@ const drawColumnChartHighcharts = function (
}, },
}, },
exporting: chartExportOptions,
series: seriesOptionsArr, series: seriesOptionsArr,
}); });
}; };
......
"use strict";
export const chartExportOptions = {
buttons: {
contextButton: {
menuItems: ["downloadPNG", "downloadJPEG", "downloadPDF", "downloadSVG"],
},
},
};
"use strict"; "use strict";
import { chartExportOptions } from "./chartExport.mjs";
/** /**
* Format the response from SensorThings API to make it suitable for use in a heatmap * Format the response from SensorThings API to make it suitable for use in a heatmap
* @param {Array} obsArray Array of observations (timestamp + value) that is response from SensorThings API * @param {Array} obsArray Array of observations (timestamp + value) that is response from SensorThings API
...@@ -142,11 +144,32 @@ const drawHeatMapHighcharts = function ( ...@@ -142,11 +144,32 @@ const drawHeatMapHighcharts = function (
startOnTick: false, startOnTick: false,
endOnTick: false, endOnTick: false,
labels: { labels: {
// format: "{value}℃",
format: `{value}${PHENOMENON_SYMBOL}`, format: `{value}${PHENOMENON_SYMBOL}`,
}, },
}, },
exporting: chartExportOptions,
tooltip: {
formatter() {
const headerString = `${PHENOMENON_NAME}<br/>`;
// Check whether the point value is null or not; this will determine the string that we'll render
const pointString =
this.point.value === null
? `${Highcharts.dateFormat("%e %b, %Y", this.point.x)} ${
this.point.y
}:00:00 <b>null</b>`
: `${Highcharts.dateFormat("%e %b, %Y", this.point.x)} ${
this.point.y
}:00:00 <b>${this.point.value.toFixed(
2
)} ${PHENOMENON_SYMBOL}</b>`;
return headerString + pointString;
},
},
series: [ series: [
{ {
data: formattedObsArrayForHeatmap, data: formattedObsArrayForHeatmap,
...@@ -154,14 +177,6 @@ const drawHeatMapHighcharts = function ( ...@@ -154,14 +177,6 @@ const drawHeatMapHighcharts = function (
borderWidth: 0, borderWidth: 0,
nullColor: "#525252", nullColor: "#525252",
colsize: 24 * 36e5, // one day colsize: 24 * 36e5, // one day
tooltip: {
headerFormat: `${PHENOMENON_NAME}<br/>`,
valueDecimals: 2,
pointFormat:
// "{point.x:%e %b, %Y} {point.y}:00: <b>{point.value} ℃</b>",
`{point.x:%e %b, %Y} {point.y}:00: <b>{point.value} ${PHENOMENON_SYMBOL}</b>`,
nullFormat: `{point.x:%e %b, %Y} {point.y}:00: <b>null</b>`,
},
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
}, },
], ],
......
"use strict"; "use strict";
import { chartExportOptions } from "./chartExport.mjs";
/** /**
* Format the response from SensorThings API to make it suitable for use in a line chart * Format the response from SensorThings API to make it suitable for use in a line chart
* @param {Array} obsArray Array of observations (timestamp + value) that is response from SensorThings API * @param {Array} obsArray Array of observations (timestamp + value) that is response from SensorThings API
...@@ -42,23 +44,21 @@ const createCombinedTextForLineChartTitles = function ( ...@@ -42,23 +44,21 @@ const createCombinedTextForLineChartTitles = function (
* Creates an options object for each series drawn in the line chart * Creates an options object for each series drawn in the line chart
* @param {Array} formattedObsArraysForLineChart An array of formatted observation array(s) from one or more datastreams * @param {Array} formattedObsArraysForLineChart An array of formatted observation array(s) from one or more datastreams
* @param {Array} phenomenonNamesArr An array of phenomenon name(s) * @param {Array} phenomenonNamesArr An array of phenomenon name(s)
* @param {Array} phenomenonSymbolsArr An array of phenomenon symbol(s)
* @returns {Array} An array made up of series options object(s) * @returns {Array} An array made up of series options object(s)
*/ */
const createSeriesOptionsForLineChart = function ( const createSeriesOptionsForLineChart = function (
formattedObsArraysForLineChart, formattedObsArraysForLineChart,
phenomenonNamesArr, phenomenonNamesArr
phenomenonSymbolsArr
) { ) {
// An array of colors provided by the Highcharts object // An array of colors provided by the Highcharts object
const seriesColors = Highcharts.getOptions().colors; const seriesColors = Highcharts.getOptions().colors;
// Create an array of seriesOptions objects // Create an array of seriesOptions objects
// Assumes that the observation array of arrays, phenomenon names array and phenomenon symbols array are of equal length // Assumes that the observation array of arrays and phenomenon names array are of equal length
// Use one of the arrays for looping // Use one of the arrays for looping
return formattedObsArraysForLineChart.map((formattedObsArray, i) => { return formattedObsArraysForLineChart.map((formattedObsArray, i) => {
return { return {
name: `${phenomenonNamesArr[i]} (${phenomenonSymbolsArr[i]})`, name: `${phenomenonNamesArr[i]}`,
data: formattedObsArray, data: formattedObsArray,
color: seriesColors[i], color: seriesColors[i],
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
...@@ -112,11 +112,27 @@ const drawLineChartHighcharts = function ( ...@@ -112,11 +112,27 @@ const drawLineChartHighcharts = function (
}, },
tooltip: { tooltip: {
pointFormat: formatter() {
'<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> <br/>', // Our tooltip is split
valueDecimals: 2, // this.x -- common for all points
// this.points -- an array containing properties for each series
return [`${Highcharts.dateFormat("%A, %b %e, %Y", this.x)}`].concat(
this.points
? this.points.map(
(point, i) =>
`<span style="color:${point.color}">${
point.series.name
}</span>: <b>${point.y.toFixed(2)} ${
unitOfMeasurementSymbolsArr[i]
}</b>`
)
: []
);
},
}, },
exporting: chartExportOptions,
series: seriesOptionsArr, series: seriesOptionsArr,
}); });
}; };
......
"use strict"; "use strict";
import { chartExportOptions } from "./chartExport.mjs";
/** /**
* Determines the timestamps that are missing from a smaller set of observations. Based on the comparison of two observation arrays, where one array is larger than the other * Determines the timestamps that are missing from a smaller set of observations. Based on the comparison of two observation arrays, where one array is larger than the other
* @param {Array} obsTimestampArrayOne An array of timestamps for the first set of observations * @param {Array} obsTimestampArrayOne An array of timestamps for the first set of observations
...@@ -323,13 +325,23 @@ const drawScatterPlotHighcharts = function ( ...@@ -323,13 +325,23 @@ const drawScatterPlotHighcharts = function (
}, },
}, },
}, },
tooltip: {
headerFormat: "{series.name}<br>",
pointFormat: `<b>{point.y:.2f} ${SERIES_1_SYMBOL}, {point.x:.2f} ${SERIES_2_SYMBOL}</b>`,
},
}, },
}, },
tooltip: {
formatter() {
const headerString = `${this.series.name}<br>`;
const pointString = `<b>${this.point.y.toFixed(
2
)} ${SERIES_1_SYMBOL}, ${this.point.x.toFixed(
2
)} ${SERIES_2_SYMBOL}</b>`;
return headerString + pointString;
},
},
exporting: chartExportOptions,
series: [ series: [
{ {
name: SERIES_COMBINED_NAME, name: SERIES_COMBINED_NAME,
......
"use strict"; "use strict";
/** import { getDatastreamIdFromBuildingNumber } from "./getDatastreamId.mjs";
* Retrieve the datastream ID that corresponds to a particular building
* @param {Number | String} buildingNumber Integer representing the building ID
* @param {String} phenomenon String representing the phenomenon of interest
* @param {String} samplingRate String representing the sampling rate of the observations
* @returns {Number} Datastream corresponding to the input building
*/
const getDatastreamIdFromBuildingNumber = function (
buildingNumber,
phenomenon,
samplingRate
) {
const buildingToDatastreamMapping = {
101: {
vl: { "15min": "69", "60min": "75" },
rl: { "15min": "81", "60min": "87" },
// These Datastreams do not yet have Observations
// flow: { "15min": "93", "60min": "99" },
// power: { "15min": "105", "60min": "111" },
// energy: { "15min": "117", "60min": "123" },
// energy_verb: { "15min": "129", "60min": "135" },
},
102: {
vl: { "15min": "70", "60min": "76" },
rl: { "15min": "82", "60min": "88" },
// These Datastreams do not yet have Observations
// flow: { "15min": "94", "60min": "100" },
// power: { "15min": "106", "60min": "112" },
// energy: { "15min": "118", "60min": "124" },
// energy_verb: { "15min": "130", "60min": "136" },
},
107: {
vl: { "15min": "71", "60min": "77" },
rl: { "15min": "83", "60min": "89" },
// These Datastreams do not yet have Observations
// flow: { "15min": "95", "60min": "101" },
// power: { "15min": "107", "60min": "113" },
// energy: { "15min": "119", "60min": "125" },
// energy_verb: { "15min": "131", "60min": "137" },
},
"112, 118": {
vl: { "15min": "72", "60min": "78" },
rl: { "15min": "84", "60min": "90" },
// These Datastreams do not yet have Observations
// flow: { "15min": "96", "60min": "102" },
// power: { "15min": "108", "60min": "114" },
// energy: { "15min": "120", "60min": "126" },
// energy_verb: { "15min": "132", "60min": "138" },
},
125: {
vl: { "15min": "73", "60min": "79" },
rl: { "15min": "85", "60min": "91" },
// These Datastreams do not yet have Observations
// flow: { "15min": "97", "60min": "103" },
// power: { "15min": "109", "60min": "115" },
// energy: { "15min": "121", "60min": "127" },
// energy_verb: { "15min": "133", "60min": "139" },
},
225: {
vl: { "15min": "74", "60min": "80" },
rl: { "15min": "86", "60min": "92" },
flow: { "15min": "98", "60min": "104" },
power: { "15min": "110", "60min": "116" },
energy: { "15min": "122", "60min": "128" },
energy_verb: { "15min": "134", "60min": "140" },
},
weather_station_521: {
outside_temp: { "15min": "141", "60min": "142" },
},
};
if (
buildingToDatastreamMapping?.[buildingNumber]?.[phenomenon]?.[
samplingRate
] === undefined
)
return;
return Number(
buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate]
);
};
/** /**
* Create URL to fetch the details of single Datastream * Create URL to fetch the details of single Datastream
...@@ -412,109 +320,9 @@ const getMetadataPlusObservationsFromSingleOrMultipleDatastreams = ...@@ -412,109 +320,9 @@ const getMetadataPlusObservationsFromSingleOrMultipleDatastreams =
} }
}; };
/**
* Calculate the temperature difference, dT, between Vorlauf temperature [VL] and Rücklauf temperature [RL] (i.e., dT = VL - RL)
* @param {String} baseUrl Base URL of the STA server
* @param {Object} urlParams The URL parameters to be sent together with the GET request
* @param {String} buildingId The building ID as a string
* @param {String} samplingRate The sampling rate as a string
* @returns {Promise} A promise that contains an array (that is made up of a temperature difference array and a metadata object) when fulfilled
*/
const calculateVorlaufMinusRuecklaufTemperature = async function (
baseUrl,
urlParams,
buildingId,
samplingRate
) {
try {
const bldgSensorSamplingRateNestedArr = [
[buildingId, "vl", samplingRate],
[buildingId, "rl", samplingRate],
];
const BUILDING_ID = buildingId;
const SAMPLING_RATE = samplingRate;
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
baseUrl,
urlParams,
bldgSensorSamplingRateNestedArr
);
// Extract Vorlauf temperature, Ruecklauf temperature and metadata
const [
[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr],
[metadataVorlauf, metadataRuecklauf],
] = observationsPlusMetadata;
// Extract the temperature values
const vorlaufTemperatureValues = vorlaufTemperatureObsArr.map(
(vlTempObs) => vlTempObs[1]
);
const ruecklaufTemperatureValues = ruecklaufTemperatureObsArr.map(
(rlTempObs) => rlTempObs[1]
);
// The arrays have equal length, we need only use one of them for looping
// Resulting array contains the following pairs (timestamp + dT)
const vorlaufMinusRuecklaufTemperatureObs = vorlaufTemperatureObsArr.map(
(vlTempObs, i) => [
vlTempObs[0], // timestamp
vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i],
]
);
// From Vorlauf metadata, extract `name` and `unitOfMeasurement`
const {
name: datastreamNameVorlauf,
unitOfMeasurement: unitOfMeasurementVorlauf,
} = metadataVorlauf;
// From Ruecklauf metadata, extract `name`
const { name: datastreamNameRuecklauf } = metadataRuecklauf;
// Extract the phenomenon names from the Datastream names
const phenomenonNameVorlauf = extractPhenomenonNameFromDatastreamName(
datastreamNameVorlauf
);
const phenomenonNameRuecklauf = extractPhenomenonNameFromDatastreamName(
datastreamNameRuecklauf
);
// Create our custom datastream description text
// The resulting datastream description string has two `temperature` substrings;
// replace the first occurence with an empty string
const descriptionTempDifference =
`Computed dT: ${phenomenonNameVorlauf} minus ${phenomenonNameRuecklauf}`.replace(
"temperature",
""
);
// Create our custom datastream name text
const nameTempDifference = `BOSCH_${BUILDING_ID} / dT Temperature difference (VL-RL) DS:${SAMPLING_RATE}`;
// The datastream object that we return needs to have these property names
const description = descriptionTempDifference;
const name = nameTempDifference;
const unitOfMeasurement = unitOfMeasurementVorlauf;
return [
vorlaufMinusRuecklaufTemperatureObs,
{
description,
name,
unitOfMeasurement,
},
];
} catch (err) {
console.error(err);
}
};
export { export {
extractPhenomenonNameFromDatastreamName,
formatDatastreamMetadataForChart, formatDatastreamMetadataForChart,
extractPropertiesFromFormattedDatastreamMetadata, extractPropertiesFromFormattedDatastreamMetadata,
getMetadataPlusObservationsFromSingleOrMultipleDatastreams, getMetadataPlusObservationsFromSingleOrMultipleDatastreams,
calculateVorlaufMinusRuecklaufTemperature,
}; };
"use strict";
/**
* Retrieve the datastream ID that corresponds to a particular building
* @param {Number | String} buildingNumber Integer representing the building ID
* @param {String} phenomenon String representing the phenomenon of interest
* @param {String} samplingRate String representing the sampling rate of the observations
* @returns {Number} Datastream corresponding to the input building
*/
export const getDatastreamIdFromBuildingNumber = function (
buildingNumber,
phenomenon,
samplingRate
) {
const buildingToDatastreamMapping = {
101: {
vl: { "15min": "69", "60min": "75" },
rl: { "15min": "81", "60min": "87" },
// These Datastreams do not yet have Observations
// flow: { "15min": "93", "60min": "99" },
// power: { "15min": "105", "60min": "111" },
// energy: { "15min": "117", "60min": "123" },
// energy_verb: { "15min": "129", "60min": "135" },
},
102: {
vl: { "15min": "70", "60min": "76" },
rl: { "15min": "82", "60min": "88" },
// These Datastreams do not yet have Observations
// flow: { "15min": "94", "60min": "100" },
// power: { "15min": "106", "60min": "112" },
// energy: { "15min": "118", "60min": "124" },
// energy_verb: { "15min": "130", "60min": "136" },
},
107: {
vl: { "15min": "71", "60min": "77" },
rl: { "15min": "83", "60min": "89" },
// These Datastreams do not yet have Observations
// flow: { "15min": "95", "60min": "101" },
// power: { "15min": "107", "60min": "113" },
// energy: { "15min": "119", "60min": "125" },
// energy_verb: { "15min": "131", "60min": "137" },
},
"112, 118": {
vl: { "15min": "72", "60min": "78" },
rl: { "15min": "84", "60min": "90" },
// These Datastreams do not yet have Observations
// flow: { "15min": "96", "60min": "102" },
// power: { "15min": "108", "60min": "114" },
// energy: { "15min": "120", "60min": "126" },
// energy_verb: { "15min": "132", "60min": "138" },
},
125: {
vl: { "15min": "73", "60min": "79" },
rl: { "15min": "85", "60min": "91" },
// These Datastreams do not yet have Observations
// flow: { "15min": "97", "60min": "103" },
// power: { "15min": "109", "60min": "115" },
// energy: { "15min": "121", "60min": "127" },
// energy_verb: { "15min": "133", "60min": "139" },
},
225: {
vl: { "15min": "74", "60min": "80" },
rl: { "15min": "86", "60min": "92" },
flow: { "15min": "98", "60min": "104" },
power: { "15min": "110", "60min": "116" },
energy: { "15min": "122", "60min": "128" },
energy_verb: { "15min": "134", "60min": "140" },
},
weather_station_521: {
outside_temp: { "15min": "141", "60min": "142" },
},
};
if (
buildingToDatastreamMapping?.[buildingNumber]?.[phenomenon]?.[
samplingRate
] === undefined
)
return;
return Number(
buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate]
);
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment