Commit 16d00e88 authored by Pithon Kabiro's avatar Pithon Kabiro
Browse files

Merge branch 'wip_temperature-diff-fix' into 'master'

Edit calculation of temperature difference

Tweaks and bug fixes to the logic for calculation of temperature 
difference

See merge request !19
1 merge request!19Edit calculation of temperature difference
Showing with 362 additions and 282 deletions
+362 -282
...@@ -163,10 +163,9 @@ const calculateMinimumObservationValuesWithinInterval = function ( ...@@ -163,10 +163,9 @@ const calculateMinimumObservationValuesWithinInterval = function (
) )
); );
} }
// Calculate minimum values of observations - monthly // Calculate minimum values of observations - monthly
// Note the use of the two nested `map` methods // Note the use of the two nested `map` methods
if (aggregationInterval === "monthly") { else if (aggregationInterval === "monthly") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
calculateMinimumObservationValuesWithinMonthInterval( calculateMinimumObservationValuesWithinMonthInterval(
...@@ -211,10 +210,9 @@ const calculateMaximumObservationValuesWithinInterval = function ( ...@@ -211,10 +210,9 @@ const calculateMaximumObservationValuesWithinInterval = function (
) )
); );
} }
// Calculate maximum values of observations - monthly // Calculate maximum values of observations - monthly
// Note the use of the two nested `map` methods // Note the use of the two nested `map` methods
if (aggregationInterval === "monthly") { else if (aggregationInterval === "monthly") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
calculateMaximumObservationValuesWithinMonthInterval( calculateMaximumObservationValuesWithinMonthInterval(
...@@ -259,10 +257,9 @@ const calculateSumOfObservationValuesWithinInterval = function ( ...@@ -259,10 +257,9 @@ const calculateSumOfObservationValuesWithinInterval = function (
) )
); );
} }
// Calculate sum of values of observations - monthly // Calculate sum of values of observations - monthly
// Note the use of the two nested `map` methods // Note the use of the two nested `map` methods
if (aggregationInterval === "monthly") { else if (aggregationInterval === "monthly") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
calculateSumOfObservationValuesWithinMonthInterval( calculateSumOfObservationValuesWithinMonthInterval(
...@@ -307,10 +304,9 @@ const calculateAverageOfObservationValuesWithinInterval = function ( ...@@ -307,10 +304,9 @@ const calculateAverageOfObservationValuesWithinInterval = function (
) )
); );
} }
// Calculate average of values of observations - monthly // Calculate average of values of observations - monthly
// Note the use of the two nested `map` methods // Note the use of the two nested `map` methods
if (aggregationInterval === "monthly") { else if (aggregationInterval === "monthly") {
return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
calculateAverageOfObservationValuesWithinMonthInterval( calculateAverageOfObservationValuesWithinMonthInterval(
......
...@@ -16,18 +16,17 @@ const createTimeStringsForInterval = function (phenomenonSamplingRate) { ...@@ -16,18 +16,17 @@ const createTimeStringsForInterval = function (phenomenonSamplingRate) {
if ( if (
phenomenonSamplingRate !== fifteenMinutes && phenomenonSamplingRate !== fifteenMinutes &&
phenomenonSamplingRate !== sixtyMinutes phenomenonSamplingRate !== sixtyMinutes
) ) {
throw new Error( throw new Error(
`Check that the provided phenomenon sampling rate string is in this format: "15min" or "60min"` `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) { else if (phenomenonSamplingRate === fifteenMinutes) {
return [startTime, endTimeFifteenMinutes]; return [startTime, endTimeFifteenMinutes];
} }
// 60 min sampling rate // 60 min sampling rate
if (phenomenonSamplingRate === sixtyMinutes) { else if (phenomenonSamplingRate === sixtyMinutes) {
return [startTime, endTimeSixtyMinutes]; return [startTime, endTimeSixtyMinutes];
} }
}; };
...@@ -66,13 +65,15 @@ const getIndexOfTimestamp = function (inputTimestampArr, timestampOfInterest) { ...@@ -66,13 +65,15 @@ const getIndexOfTimestamp = function (inputTimestampArr, timestampOfInterest) {
); );
// If the timestamp does not exist in the timestamp array // If the timestamp does not exist in the timestamp array
if (timestampIndex === -1) if (timestampIndex === -1) {
throw new Error( throw new Error(
"A start or end timestamp could not be found in the timestamp array" "A start or end timestamp could not be found in the timestamp array"
); );
}
// If the timestamp exists in the timestamp array // If the timestamp exists in the timestamp array
else {
return timestampIndex; return timestampIndex;
}
}; };
/** /**
...@@ -210,23 +211,25 @@ const extractObservationValuesWithinMonthInterval = function ( ...@@ -210,23 +211,25 @@ const extractObservationValuesWithinMonthInterval = function (
const [yearNum, monthNum] = const [yearNum, monthNum] =
extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr); extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr);
if (monthNum < 1 || monthNum > 12) return;
// All the months start on the first // All the months start on the first
const startDateStr = `${calendarMonthStr}-01`; const startDateStr = `${calendarMonthStr}-01`;
if (monthNum < 1 || monthNum > 12) {
throw new Error("The specified digit for the month of the year is invalid");
}
// February // February
if (monthNum === 2) { else if (monthNum === 2) {
// Leap year // Leap year
if (checkIfLeapYear(yearNum)) if (checkIfLeapYear(yearNum)) {
return extractObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
`${calendarMonthStr}-29` `${calendarMonthStr}-29`
); );
}
// Non-leap year // Non-leap year
else {
return extractObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
...@@ -234,23 +237,30 @@ const extractObservationValuesWithinMonthInterval = function ( ...@@ -234,23 +237,30 @@ const extractObservationValuesWithinMonthInterval = function (
`${calendarMonthStr}-28` `${calendarMonthStr}-28`
); );
} }
}
// Months with 30 days // Months with 30 days
if (monthNum === 4 || monthNum === 6 || monthNum === 9 || monthNum === 11) else if (
monthNum === 4 ||
monthNum === 6 ||
monthNum === 9 ||
monthNum === 11
) {
return extractObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
`${calendarMonthStr}-30` `${calendarMonthStr}-30`
); );
}
// Months with 31 days // Months with 31 days
else {
return extractObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
`${calendarMonthStr}-31` `${calendarMonthStr}-31`
); );
}
}; };
/** /**
......
"use strict"; "use strict";
import { checkForAndDeleteUniqueObservationsFromLargerArray } from "./chartHelpers.mjs"; import {
checkForAndDeleteUniqueObservationsFromLargerArray,
extractSamplingRateFromDatastreamName,
extractBuildingIdFromDatastreamName,
} from "./chartHelpers.mjs";
import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./fetchData.mjs"; import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./fetchData.mjs";
...@@ -8,54 +12,17 @@ import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcessing ...@@ -8,54 +12,17 @@ import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcessing
/** /**
* Calculate the temperature difference, dT, between Vorlauf temperature [VL] and * Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
* Rücklauf temperature [RL] (i.e., dT = VL - RL). In addition, create synthetic metadata * Rücklauf temperature [RL] (i.e., dT = VL - RL) for a single building
* for the temperature difference *
* @async * @param {Array} observationsPlusMetadataArr A 1*2 array, where the first element is an array made up of two arrays of observations and the second element is an array of two metadata objects
* @param {String} baseUrl Base URL of the STA server * @returns {Array} An array made up of timestamps + values for dT
* @param {Object} urlParams The URL parameters to be sent together with the GET request
* @param {Array} buildingSamplingRateNestedArr A N*1 array (where N >= 1) containing a nested array of buildings & sampling rates as strings, i.e. [["101", "15min"]] or [["101", "15min"], ["102", "60min"]] or [["101", "15min"], ["102", "60min"], ["225", "60min"]], etc
* @returns {Promise} A promise that contains a 1*2 array (the first element is an array that contans N Observations arrays; and the second element is an array of N Datastream metadata objects) when fulfilled
*/ */
export const calculateVorlaufMinusRuecklaufTemperature = async function ( const calculateVorlaufMinusRuecklaufTemperatureObservations = function (
baseUrl, observationsPlusMetadataArr
urlParams,
buildingSamplingRateNestedArr
) { ) {
try { // Extract Vorlauf temperature, Ruecklauf temperature; first element of array
// Arrays to store our results const [[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]] =
const combinedObservationsArr = []; observationsPlusMetadataArr;
const combinedMetadataArr = [];
const buildingDataPointSamplingRateNestedTwiceArr =
buildingSamplingRateNestedArr.map((bldgSmplngRate) => {
// The building ID is the first element, sampling rate is second element
return [
[bldgSmplngRate[0], "vl", bldgSmplngRate[1]],
[bldgSmplngRate[0], "rl", bldgSmplngRate[1]],
];
});
// Note: We have to use a for/of loop here due to the asynchronous nature of our code
for (const bldgDataPtSamplingRateNestedArr of buildingDataPointSamplingRateNestedTwiceArr) {
// Use the first element of the nested array to extract building ID + sampling rate
// Note: we skip the second element
const [buildingId, , samplingRate] = bldgDataPtSamplingRateNestedArr[0];
const BUILDING_ID = buildingId;
const SAMPLING_RATE = samplingRate;
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
baseUrl,
urlParams,
bldgDataPtSamplingRateNestedArr
);
// Extract Vorlauf temperature, Ruecklauf temperature and metadata
const [
[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr],
[metadataVorlauf, metadataRuecklauf],
] = observationsPlusMetadata;
// Compare the lengths of the observations arrays for VL and RL, // Compare the lengths of the observations arrays for VL and RL,
// delete the unique observation(s), if necessary // delete the unique observation(s), if necessary
...@@ -75,30 +42,49 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function ( ...@@ -75,30 +42,49 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function (
(rlTempObs) => rlTempObs[1] (rlTempObs) => rlTempObs[1]
); );
// The arrays have equal length, we need only use one of them for looping // Check that the final observation arrays have equal lengths
if (
vorlaufTemperatureObsFinalArr.length !==
ruecklaufTemperatureObsFinalArr.length
) {
throw new Error(
"The two observations arrays (VL and RL) have different lengths"
);
}
// The final observation arrays now have equal length, we need only use one of them for looping
// Resulting array contains the following pairs (timestamp + dT) // Resulting array contains the following pairs (timestamp + dT)
const vorlaufMinusRuecklaufTemperatureObs = vorlaufTemperatureObsArr.map( else {
(vlTempObs, i) => { return vorlaufTemperatureObsFinalArr.map((vlTempObsFinal, i) => {
// Use timestamp from VL, since is equal to that of RL // Use timestamp from VL, since is equal to that of RL
const timestamp = vlTempObs[0]; const timestamp = vlTempObsFinal[0];
// Case 1: One of the observation values is `null`, // Case 1: One of the observation values is `null`,
// no need to calculate temperature difference // no need to calculate temperature difference
if (
vorlaufTemperatureValues[i] === null ||
ruecklaufTemperatureValues[i] === null
) {
return [timestamp, null];
}
// Case 2: Neither of the observation values is `null`, // Case 2: Neither of the observation values is `null`,
// calculate temperature difference // calculate temperature difference
return [ return vorlaufTemperatureValues[i] === null ||
ruecklaufTemperatureValues[i] === null
? [timestamp, null]
: [
timestamp, timestamp,
vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i], vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i],
]; ];
});
} }
); };
/**
* Create synthetic metadata for the temperature difference, dT, between Vorlauf temperature [VL] and
* Rücklauf temperature [RL] (i.e., dT = VL - RL) of a single building
*
* @param {Array} observationsPlusMetadataArr A 1*2 array, where the first element is an array made up of two arrays of observations and the second element is an array of two metadata objects
* @returns {Object} A metadata object for dT, made up of three properties: description, name and unit of measurement
*/
const createVorlaufMinusRuecklaufTemperatureMetadata = function (
observationsPlusMetadataArr
) {
// Extract metadata; second element of array, note that we skip the first element
const [, [metadataVorlauf, metadataRuecklauf]] = observationsPlusMetadataArr;
// From Vorlauf metadata, extract `name` and `unitOfMeasurement` // From Vorlauf metadata, extract `name` and `unitOfMeasurement`
const { const {
...@@ -126,8 +112,83 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function ( ...@@ -126,8 +112,83 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function (
"" ""
); );
// Create an array of the VL datastream name, this array has only one element.
// We need this structure since the functions `extractBuildingIdFromDatastreamName`
// and `extractSamplingRateFromDatastreamName` expect an array of datastream names
const datastreamNameVorlaufArr = [datastreamNameVorlauf];
// Extract the building ID from the datastream name
const buildingId = extractBuildingIdFromDatastreamName(
datastreamNameVorlaufArr
);
// Extract the sampling rate from the datastream name
const samplingRate = extractSamplingRateFromDatastreamName(
datastreamNameVorlaufArr
);
// Create our custom datastream name text // Create our custom datastream name text
const nameTempDifference = `BOSCH_${BUILDING_ID} / dT Temperature difference (VL-RL) DS:${SAMPLING_RATE}`; const nameTempDifference = `${buildingId} / dT Temperature difference (VL-RL) DS:${samplingRate}`;
return {
descriptionTempDifference,
nameTempDifference,
unitOfMeasurementVorlauf,
};
};
/**
* Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
* Rücklauf temperature [RL] (i.e., dT = VL - RL). In addition, create synthetic metadata
* for the temperature difference
* @async
* @param {String} baseUrl Base URL of the STA server
* @param {Object} urlParams The URL parameters to be sent together with the GET request
* @param {Array} buildingSamplingRateNestedArr A N*1 array (where N >= 1) containing a nested array of buildings & sampling rates as strings, i.e. [["101", "15min"]] or [["101", "15min"], ["102", "60min"]] or [["101", "15min"], ["102", "60min"], ["225", "60min"]], etc
* @returns {Promise} A promise that contains a 1*2 array (the first element is an array that contans N Observations arrays; and the second element is an array of N Datastream metadata objects) when fulfilled
*/
export const calculateVorlaufMinusRuecklaufTemperature = async function (
baseUrl,
urlParams,
buildingSamplingRateNestedArr
) {
try {
// Arrays to store our results
const combinedObservationsArr = [];
const combinedMetadataArr = [];
const buildingDataPointSamplingRateNestedTwiceArr =
buildingSamplingRateNestedArr.map((bldgSmplngRate) => {
// The building ID is the first element, sampling rate is second element
return [
[bldgSmplngRate[0], "vl", bldgSmplngRate[1]],
[bldgSmplngRate[0], "rl", bldgSmplngRate[1]],
];
});
// Note: We have to use a for/of loop here due to the asynchronous nature of our code
for (const bldgDataPtSamplingRateNestedArr of buildingDataPointSamplingRateNestedTwiceArr) {
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
baseUrl,
urlParams,
bldgDataPtSamplingRateNestedArr
);
// dT observations (timestamp + value)
const vorlaufMinusRuecklaufTemperatureObs =
calculateVorlaufMinusRuecklaufTemperatureObservations(
observationsPlusMetadata
);
// dT metadata
const {
descriptionTempDifference,
nameTempDifference,
unitOfMeasurementVorlauf,
} = createVorlaufMinusRuecklaufTemperatureMetadata(
observationsPlusMetadata
);
// The datastream object that we return needs to have these property names // The datastream object that we return needs to have these property names
const description = descriptionTempDifference; const description = descriptionTempDifference;
......
...@@ -48,11 +48,11 @@ const createSeriesOptionsForColumnChart = function ( ...@@ -48,11 +48,11 @@ const createSeriesOptionsForColumnChart = function (
if ( if (
formattedAggregatedResultForColumnChart.length !== formattedAggregatedResultForColumnChart.length !==
buildingIdsPhenomenonNamesArr.length buildingIdsPhenomenonNamesArr.length
) ) {
throw new Error( throw new Error(
"The observations array and phenomenon names array have different lengths" "The observations array and phenomenon names array have different lengths"
); );
} else {
return formattedAggregatedResultForColumnChart.map( return formattedAggregatedResultForColumnChart.map(
(formattedAggResArray, i) => { (formattedAggResArray, i) => {
return { return {
...@@ -62,6 +62,7 @@ const createSeriesOptionsForColumnChart = function ( ...@@ -62,6 +62,7 @@ const createSeriesOptionsForColumnChart = function (
}; };
} }
); );
}
}; };
/** /**
...@@ -83,16 +84,17 @@ const drawColumnChartHighcharts = function ( ...@@ -83,16 +84,17 @@ const drawColumnChartHighcharts = function (
aggregationType; aggregationType;
// Check whether the datastream properties are for aggregated observations // Check whether the datastream properties are for aggregated observations
if (extractedFormattedDatastreamProperties?.aggregationType === undefined) {
// Case 1: No aggregation // Case 1: No aggregation
if (extractedFormattedDatastreamProperties?.aggregationType === undefined) {
({ ({
datastreamNamesArr, datastreamNamesArr,
phenomenonNamesArr, phenomenonNamesArr,
buildingIdsPhenomenonNamesArr, buildingIdsPhenomenonNamesArr,
unitOfMeasurementSymbolsArr, unitOfMeasurementSymbolsArr,
} = extractedFormattedDatastreamProperties); } = extractedFormattedDatastreamProperties);
} else { }
// Case 2: Aggregation // Case 2: Aggregation
else {
({ ({
datastreamNamesArr, datastreamNamesArr,
phenomenonNamesArr, phenomenonNamesArr,
......
...@@ -53,18 +53,18 @@ const removeUniqueObservationsFromLargerArray = function ( ...@@ -53,18 +53,18 @@ const removeUniqueObservationsFromLargerArray = function (
largerObsArr largerObsArr
) { ) {
// Create a reversed copy of the indexes array, so that the larger index is removed first // Create a reversed copy of the indexes array, so that the larger index is removed first
const reversedUniqueIndexesArr = uniqueIndexesArr.reverse(); const uniqueIndexesReversedCopyArr = [...uniqueIndexesArr].reverse();
// Create a copy the larger observation array, will be modified in place // Create a copy of the larger observation array, will be modified in place
const processedLargerObsArr = largerObsArr; const largerObsCopyArr = [...largerObsArr];
reversedUniqueIndexesArr.forEach((index) => { uniqueIndexesReversedCopyArr.forEach((uniqueIndex) => {
if (index > -1) { if (uniqueIndex > -1) {
processedLargerObsArr.splice(index, 1); largerObsCopyArr.splice(uniqueIndex, 1);
} }
}); });
return processedLargerObsArr; return largerObsCopyArr;
}; };
/** /**
...@@ -75,10 +75,11 @@ const removeUniqueObservationsFromLargerArray = function ( ...@@ -75,10 +75,11 @@ const removeUniqueObservationsFromLargerArray = function (
*/ */
const getLargerArrayBetweenTwoInputArrays = function (firstArr, secondArr) { const getLargerArrayBetweenTwoInputArrays = function (firstArr, secondArr) {
if (firstArr.length === secondArr.length) return; if (firstArr.length === secondArr.length) return;
else if (firstArr.length > secondArr.length) {
if (firstArr.length > secondArr.length) return firstArr; return firstArr;
} else if (firstArr.length < secondArr.length) {
if (firstArr.length < secondArr.length) return secondArr; return secondArr;
}
}; };
/** /**
...@@ -89,10 +90,11 @@ const getLargerArrayBetweenTwoInputArrays = function (firstArr, secondArr) { ...@@ -89,10 +90,11 @@ const getLargerArrayBetweenTwoInputArrays = function (firstArr, secondArr) {
*/ */
const getSmallerArrayBetweenTwoInputArrays = function (firstArr, secondArr) { const getSmallerArrayBetweenTwoInputArrays = function (firstArr, secondArr) {
if (firstArr.length === secondArr.length) return; if (firstArr.length === secondArr.length) return;
else if (firstArr.length < secondArr.length) {
if (firstArr.length < secondArr.length) return firstArr; return firstArr;
} else if (firstArr.length > secondArr.length) {
if (firstArr.length > secondArr.length) return secondArr; return secondArr;
}
}; };
/** /**
...@@ -162,17 +164,17 @@ const checkForAndDeleteUniqueObservationsFromLargerArray = function ( ...@@ -162,17 +164,17 @@ const checkForAndDeleteUniqueObservationsFromLargerArray = function (
obsArrayTwo obsArrayTwo
) { ) {
if (obsArrayOne.length === obsArrayTwo.length) return; if (obsArrayOne.length === obsArrayTwo.length) return;
// Case 1: obsArrayTwo larger than obsArrayOne
// Case 1: obsArrayOne.length < obsArrayTwo.length else if (obsArrayOne.length < obsArrayTwo.length) {
if (obsArrayOne.length < obsArrayTwo.length) {
const [biggerObsArr, smallerObsArr] = const [biggerObsArr, smallerObsArr] =
deleteUniqueObservationsFromLargerArray(obsArrayOne, obsArrayTwo); deleteUniqueObservationsFromLargerArray(obsArrayOne, obsArrayTwo);
return [smallerObsArr, biggerObsArr]; return [smallerObsArr, biggerObsArr];
} }
// Case 2: obsArrayOne larger than obsArrayTwo
// Case 2: obsArrayOne.length > obsArrayTwo.length else if (obsArrayOne.length > obsArrayTwo.length) {
return deleteUniqueObservationsFromLargerArray(obsArrayOne, obsArrayTwo); return deleteUniqueObservationsFromLargerArray(obsArrayOne, obsArrayTwo);
}
}; };
/** /**
...@@ -194,14 +196,16 @@ const convertHexColorToRGBColor = function (hexCode) { ...@@ -194,14 +196,16 @@ const convertHexColorToRGBColor = function (hexCode) {
"#91e8e1": "rgb(145, 232, 225)", "#91e8e1": "rgb(145, 232, 225)",
}; };
if (hexToRGBMapping?.[hexCode] === undefined) if (hexToRGBMapping?.[hexCode] === undefined) {
throw new Error( throw new Error(
"The provided hex code is not valid or is not supported by this function" "The provided hex code is not valid or is not supported by this function"
); );
}
// Extract the RGB color elements as a single string // Extract the RGB color elements as a single string
// The individual color elements are separated by commas // The individual color elements are separated by commas
else {
return (hexToRGBMapping?.[hexCode]).slice(4, -1); return (hexToRGBMapping?.[hexCode]).slice(4, -1);
}
}; };
/** /**
...@@ -264,11 +268,13 @@ const createPartialTitleForLineOrColumnChart = function ( ...@@ -264,11 +268,13 @@ const createPartialTitleForLineOrColumnChart = function (
) { ) {
// Case 1: No aggregation; return empty string // Case 1: No aggregation; return empty string
if (!aggregationInterval && !aggregationType) return ``; if (!aggregationInterval && !aggregationType) return ``;
// Case 2: Aggregation; capitalize the first characters // Case 2: Aggregation; capitalize the first characters
else {
return `${ return `${
aggregationInterval.slice(0, 1).toUpperCase() + aggregationInterval.slice(1) aggregationInterval.slice(0, 1).toUpperCase() +
aggregationInterval.slice(1)
} ${aggregationType.slice(0, 1).toUpperCase() + aggregationType.slice(1)}`; } ${aggregationType.slice(0, 1).toUpperCase() + aggregationType.slice(1)}`;
}
}; };
/** /**
...@@ -289,12 +295,13 @@ const createFullTitleForLineOrColumnChart = function ( ...@@ -289,12 +295,13 @@ const createFullTitleForLineOrColumnChart = function (
aggregationInterval, aggregationInterval,
aggregationType aggregationType
)}${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; )}${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`;
// Case 2: Aggregation // Case 2: Aggregation
else {
return `${createPartialTitleForLineOrColumnChart( return `${createPartialTitleForLineOrColumnChart(
aggregationInterval, aggregationInterval,
aggregationType aggregationType
)}: ${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; )}: ${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`;
}
}; };
/** /**
...@@ -376,6 +383,8 @@ export { ...@@ -376,6 +383,8 @@ export {
checkForAndDeleteUniqueObservationsFromLargerArray, checkForAndDeleteUniqueObservationsFromLargerArray,
createCombinedTextDelimitedByAmpersand, createCombinedTextDelimitedByAmpersand,
createCombinedTextDelimitedByComma, createCombinedTextDelimitedByComma,
extractSamplingRateFromDatastreamName,
extractBuildingIdFromDatastreamName,
createFullTitleForLineOrColumnChart, createFullTitleForLineOrColumnChart,
createTitleForHeatmap, createTitleForHeatmap,
createSubtitleForChart, createSubtitleForChart,
......
...@@ -53,11 +53,11 @@ const createSeriesOptionsForLineChart = function ( ...@@ -53,11 +53,11 @@ const createSeriesOptionsForLineChart = function (
if ( if (
formattedObsArraysForLineChart.length !== formattedObsArraysForLineChart.length !==
buildingIdsPhenomenonNamesArr.length buildingIdsPhenomenonNamesArr.length
) ) {
throw new Error( throw new Error(
"The observations array and phenomenon names array have different lengths" "The observations array and phenomenon names array have different lengths"
); );
} else {
return formattedObsArraysForLineChart.map((formattedObsArray, i) => { return formattedObsArraysForLineChart.map((formattedObsArray, i) => {
return { return {
name: `${buildingIdsPhenomenonNamesArr[i]}`, name: `${buildingIdsPhenomenonNamesArr[i]}`,
...@@ -66,6 +66,7 @@ const createSeriesOptionsForLineChart = function ( ...@@ -66,6 +66,7 @@ const createSeriesOptionsForLineChart = function (
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
}; };
}); });
}
}; };
/** /**
......
...@@ -45,9 +45,10 @@ const formatSensorThingsApiResponseForScatterPlot = function ( ...@@ -45,9 +45,10 @@ const formatSensorThingsApiResponseForScatterPlot = function (
return createCombinedObservationValues(obsArrayOneFinal, obsArrayTwoFinal); return createCombinedObservationValues(obsArrayOneFinal, obsArrayTwoFinal);
} }
// When our observation arrays already have SAME lengths // When our observation arrays already have SAME lengths
else {
return createCombinedObservationValues(obsArrayOne, obsArrayTwo); return createCombinedObservationValues(obsArrayOne, obsArrayTwo);
}
}; };
/** /**
...@@ -105,21 +106,22 @@ const createYAxisTitleTextScatterPlot = function ( ...@@ -105,21 +106,22 @@ const createYAxisTitleTextScatterPlot = function (
// y-axis phenomenon symbols start at array index 1 // y-axis phenomenon symbols start at array index 1
const unitOfMeasurementSymbolsYAxisArr = unitOfMeasurementSymbolsArr.slice(1); const unitOfMeasurementSymbolsYAxisArr = unitOfMeasurementSymbolsArr.slice(1);
const combinedNameSymbolArr = phenomenonNamesYAxisArr.map(
(phenomenonNameYAxis, i) =>
`${phenomenonNameYAxis} [${unitOfMeasurementSymbolsYAxisArr[i]}]`
);
// The phenomenon names and unit of measurement arrays should have equal lengths // The phenomenon names and unit of measurement arrays should have equal lengths
// Use one of the arrays for looping // Use one of the arrays for looping
if ( if (
phenomenonNamesYAxisArr.length !== unitOfMeasurementSymbolsYAxisArr.length phenomenonNamesYAxisArr.length !== unitOfMeasurementSymbolsYAxisArr.length
) ) {
throw new Error( throw new Error(
"The phenomenon names array and unit of measurement symbols array have different lengths" "The phenomenon names array and unit of measurement symbols array have different lengths"
); );
} else {
const combinedNameSymbolArr = phenomenonNamesYAxisArr.map(
(phenomenonNameYAxis, i) =>
`${phenomenonNameYAxis} [${unitOfMeasurementSymbolsYAxisArr[i]}]`
);
return createCombinedTextDelimitedByComma(combinedNameSymbolArr); return createCombinedTextDelimitedByComma(combinedNameSymbolArr);
}
}; };
/** /**
...@@ -159,11 +161,11 @@ const createSeriesOptionsForScatterPlot = function ( ...@@ -159,11 +161,11 @@ const createSeriesOptionsForScatterPlot = function (
// Use one of the arrays for looping // Use one of the arrays for looping
if ( if (
formattedObsArraysForScatterPlot.length !== phenomenonNamesYAxisArr.length formattedObsArraysForScatterPlot.length !== phenomenonNamesYAxisArr.length
) ) {
throw new Error( throw new Error(
"The observations array and phenomenon names array have different lengths" "The observations array and phenomenon names array have different lengths"
); );
} else {
return formattedObsArraysForScatterPlot.map((formattedObsArray, i) => { return formattedObsArraysForScatterPlot.map((formattedObsArray, i) => {
return { return {
name: `${phenomenonNamesYAxisArr[i]}, ${phenomenonNameXAxis}`, name: `${phenomenonNamesYAxisArr[i]}, ${phenomenonNameXAxis}`,
...@@ -171,6 +173,7 @@ const createSeriesOptionsForScatterPlot = function ( ...@@ -171,6 +173,7 @@ const createSeriesOptionsForScatterPlot = function (
color: seriesColors[i], color: seriesColors[i],
}; };
}); });
}
}; };
/** /**
......
...@@ -111,24 +111,22 @@ const getSelectedOptionsFromAllDropDownLists = function () { ...@@ -111,24 +111,22 @@ const getSelectedOptionsFromAllDropDownLists = function () {
); );
// Ensure that all the options have at least one selection // Ensure that all the options have at least one selection
if (selectedChartTypeArr.length === 0) if (selectedChartTypeArr.length === 0) {
throw new Error("Please ensure that the chart type is selected"); throw new Error("Please ensure that the chart type is selected");
} else if (selectedBuildingDataPointOptionsSplitArr.length === 0) {
if (selectedBuildingDataPointOptionsSplitArr.length === 0)
throw new Error("Please ensure that at least one data point is selected"); throw new Error("Please ensure that at least one data point is selected");
} else if (selectedAggregationOptionsArr.length === 0) {
if (selectedSamplingRateArr.length === 0)
throw new Error("Please ensure that the aggregation type is selected"); throw new Error("Please ensure that the aggregation type is selected");
} else if (selectedSamplingRateArr.length === 0) {
if (selectedSamplingRateArr.length === 0)
throw new Error("Please ensure that the sampling rate is selected"); throw new Error("Please ensure that the sampling rate is selected");
} else {
return [ return [
selectedChartTypeArr, selectedChartTypeArr,
selectedBuildingDataPointOptionsSplitArr, selectedBuildingDataPointOptionsSplitArr,
selectedAggregationOptionsArr, selectedAggregationOptionsArr,
selectedSamplingRateArr, selectedSamplingRateArr,
]; ];
}
}; };
/** /**
...@@ -231,14 +229,6 @@ const extractTemperatureDifferenceOptions = function ( ...@@ -231,14 +229,6 @@ const extractTemperatureDifferenceOptions = function (
const bldgDataPntSamplingRateAbbrvArr = const bldgDataPntSamplingRateAbbrvArr =
buildingDataPointSamplingRateAbbrevArr[foundIndex]; buildingDataPointSamplingRateAbbrevArr[foundIndex];
// Extract the building and sampling rate strings
// Note: we have ignored the second element
// const [bldgAbbrv, , samplingRateAbbrv] = bldgDataPntSamplingRateAbbrvArr;
// Create a new array that contains two elements,
// the building and sampling rate abbreviated strings
// const bldgSamplingRateAbbrvArr = [bldgAbbrv, samplingRateAbbrv];
temperatureDifferenceOptionsAbbrevArr.push(bldgDataPntSamplingRateAbbrvArr); temperatureDifferenceOptionsAbbrevArr.push(bldgDataPntSamplingRateAbbrvArr);
}); });
...@@ -326,9 +316,8 @@ const checkIfSelectedBuildingDataPointsOptionsAreValid = function ( ...@@ -326,9 +316,8 @@ const checkIfSelectedBuildingDataPointsOptionsAreValid = function (
throw new Error("A heatmap can only display one data point at a time"); throw new Error("A heatmap can only display one data point at a time");
} }
} }
// A scatter plot requires at least two data points // A scatter plot requires at least two data points
if (selectedChartType === "Scatter Plot") { else if (selectedChartType === "Scatter Plot") {
if (selectedBuildingsDataPointsSamplingRateAbbrevNestedArr.length >= 2) if (selectedBuildingsDataPointsSamplingRateAbbrevNestedArr.length >= 2)
return true; return true;
else if ( else if (
...@@ -418,29 +407,6 @@ const getBuildingSensorSamplingRateAbbreviation = function ( ...@@ -418,29 +407,6 @@ const getBuildingSensorSamplingRateAbbreviation = function (
}, },
}; };
if (
fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm] === undefined
)
throw new Error(
"The provided building ID is not valid or is not supported by this function"
);
if (
fullFormToAbbreviationMapping["phenomenon"]?.[phenomenonFullForm] ===
undefined
)
throw new Error(
"The provided data point is not valid or is not supported by this function"
);
if (
fullFormToAbbreviationMapping["samplingRate"]?.[samplingRateFullForm] ===
undefined
)
throw new Error(
"The provided sampling rate is not valid or is not supported by this function"
);
const buildingAbbrev = const buildingAbbrev =
fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm]; fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm];
...@@ -450,7 +416,21 @@ const getBuildingSensorSamplingRateAbbreviation = function ( ...@@ -450,7 +416,21 @@ const getBuildingSensorSamplingRateAbbreviation = function (
const samplingRateAbbrev = const samplingRateAbbrev =
fullFormToAbbreviationMapping["samplingRate"]?.[samplingRateFullForm]; fullFormToAbbreviationMapping["samplingRate"]?.[samplingRateFullForm];
if (buildingAbbrev === undefined) {
throw new Error(
"The provided building ID is not valid or is not supported by this function"
);
} else if (phenomenonAbbrev === undefined) {
throw new Error(
"The provided data point is not valid or is not supported by this function"
);
} else if (samplingRateAbbrev === undefined) {
throw new Error(
"The provided sampling rate is not valid or is not supported by this function"
);
} else {
return [buildingAbbrev, phenomenonAbbrev, samplingRateAbbrev]; return [buildingAbbrev, phenomenonAbbrev, samplingRateAbbrev];
}
}; };
/** /**
...@@ -483,11 +463,11 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function ( ...@@ -483,11 +463,11 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function (
// Assume that the buildings and data points arrays have equal length // Assume that the buildings and data points arrays have equal length
// use one of the arrays for looping // use one of the arrays for looping
if (selectedBuildingsArr.length !== selectedDataPointsArr.length) if (selectedBuildingsArr.length !== selectedDataPointsArr.length) {
throw new Error( throw new Error(
"The buildings array and data points array have different lengths" "The buildings array and data points array have different lengths"
); );
} else {
return selectedBuildingsArr.map((selectedBuilding, i) => return selectedBuildingsArr.map((selectedBuilding, i) =>
getBuildingSensorSamplingRateAbbreviation( getBuildingSensorSamplingRateAbbreviation(
selectedBuilding, selectedBuilding,
...@@ -495,6 +475,7 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function ( ...@@ -495,6 +475,7 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function (
...selectedSamplingRateArr ...selectedSamplingRateArr
) )
); );
}
}; };
export { export {
......
...@@ -11,14 +11,18 @@ const matchUnitOfMeasurementSymbolStringToSymbol = function ( ...@@ -11,14 +11,18 @@ const matchUnitOfMeasurementSymbolStringToSymbol = function (
const unicodeCodePointDegreeSymbol = "\u00B0"; const unicodeCodePointDegreeSymbol = "\u00B0";
const unicodeCodePointSuperscriptThree = "\u00B3"; const unicodeCodePointSuperscriptThree = "\u00B3";
if (unitOfMeasurementSymbolString === "degC") // Symbol - temperature
if (unitOfMeasurementSymbolString === "degC") {
return `${unicodeCodePointDegreeSymbol}C`; return `${unicodeCodePointDegreeSymbol}C`;
}
if (unitOfMeasurementSymbolString === "m3/h") // Symbol - flow rate
else if (unitOfMeasurementSymbolString === "m3/h") {
return `m${unicodeCodePointSuperscriptThree}/h`; return `m${unicodeCodePointSuperscriptThree}/h`;
}
// If no symbol exists // If no symbol exists
else {
return unitOfMeasurementSymbolString; return unitOfMeasurementSymbolString;
}
}; };
/** /**
...@@ -94,39 +98,37 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( ...@@ -94,39 +98,37 @@ const extractPropertiesFromFormattedDatastreamMetadata = function (
if ( if (
formattedDatastreamsMetadataArr === undefined || formattedDatastreamsMetadataArr === undefined ||
isMetadataForAggregation === undefined isMetadataForAggregation === undefined
) ) {
throw new Error( throw new Error(
"This function expects two arguments, ensure that both have been supplied" "This function expects two arguments, ensure that both have been supplied"
); );
} else if (
if (
formattedDatastreamsMetadataArr && formattedDatastreamsMetadataArr &&
isMetadataForAggregation && isMetadataForAggregation &&
(aggregationInterval === undefined || aggregationType === undefined) (aggregationInterval === undefined || aggregationType === undefined)
) ) {
throw new Error( throw new Error(
"This function expects four arguments, ensure that all of them have been supplied" "This function expects four arguments, ensure that all of them have been supplied"
); );
} else if (
if (
isMetadataForAggregation && isMetadataForAggregation &&
aggregationInterval !== "daily" && aggregationInterval !== "daily" &&
aggregationInterval !== "monthly" aggregationInterval !== "monthly"
) ) {
throw new Error( throw new Error(
`The supported aggegation interval strings are "daily" or "monthly"` `The supported aggegation interval strings are "daily" or "monthly"`
); );
} else if (
if (
isMetadataForAggregation && isMetadataForAggregation &&
aggregationType !== "minimum" && aggregationType !== "minimum" &&
aggregationType !== "maximum" && aggregationType !== "maximum" &&
aggregationType !== "sum" && aggregationType !== "sum" &&
aggregationType !== "average" aggregationType !== "average"
) ) {
throw new Error( throw new Error(
`The supported aggegation type strings are "minimum", "maximum", "sum" or "average"` `The supported aggegation type strings are "minimum", "maximum", "sum" or "average"`
); );
}
// Create arrays from the properties of the formatted datastream metadata // Create arrays from the properties of the formatted datastream metadata
const datastreamDescriptionsArr = formattedDatastreamsMetadataArr.map( const datastreamDescriptionsArr = formattedDatastreamsMetadataArr.map(
...@@ -150,7 +152,7 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( ...@@ -150,7 +152,7 @@ const extractPropertiesFromFormattedDatastreamMetadata = function (
); );
// Case 1: Metadata NOT USED for aggregation; "isMetadataForAggregation" = false // Case 1: Metadata NOT USED for aggregation; "isMetadataForAggregation" = false
if (!isMetadataForAggregation) if (!isMetadataForAggregation) {
return { return {
datastreamDescriptionsArr, datastreamDescriptionsArr,
datastreamNamesArr, datastreamNamesArr,
...@@ -158,8 +160,9 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( ...@@ -158,8 +160,9 @@ const extractPropertiesFromFormattedDatastreamMetadata = function (
buildingIdsPhenomenonNamesArr, buildingIdsPhenomenonNamesArr,
unitOfMeasurementSymbolsArr, unitOfMeasurementSymbolsArr,
}; };
}
// Case 2: Metadata USED for aggregation; "isMetadataForAggregation" = true // Case 2: Metadata USED for aggregation; "isMetadataForAggregation" = true
else {
return { return {
datastreamDescriptionsArr, datastreamDescriptionsArr,
datastreamNamesArr, datastreamNamesArr,
...@@ -169,6 +172,7 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( ...@@ -169,6 +172,7 @@ const extractPropertiesFromFormattedDatastreamMetadata = function (
aggregationInterval, aggregationInterval,
aggregationType, aggregationType,
}; };
}
}; };
export { export {
......
...@@ -82,14 +82,27 @@ export const getDatastreamIdFromBuildingNumber = function ( ...@@ -82,14 +82,27 @@ export const getDatastreamIdFromBuildingNumber = function (
}, },
}; };
if ( if (buildingToDatastreamMapping?.[buildingNumber] === undefined) {
throw new Error(
"The supplied building number is not valid or is not supported by this function"
);
} else if (
buildingToDatastreamMapping?.[buildingNumber]?.[phenomenon] === undefined
) {
throw new Error(
"The supplied phenomenon name is not valid or is not supported by this function"
);
} else if (
buildingToDatastreamMapping?.[buildingNumber]?.[phenomenon]?.[ buildingToDatastreamMapping?.[buildingNumber]?.[phenomenon]?.[
samplingRate samplingRate
] === undefined ] === undefined
) ) {
return; throw new Error(
"The supplied sampling rate is not valid or is not supported by this function"
);
} else {
return Number( return Number(
buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate] buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate]
); );
}
}; };
Supports Markdown
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