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
parents e02c4e2a 8b900e1d
...@@ -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
return timestampIndex; else {
return timestampIndex;
}
}; };
/** /**
...@@ -210,47 +211,56 @@ const extractObservationValuesWithinMonthInterval = function ( ...@@ -210,47 +211,56 @@ 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(
obsArray,
samplingRate,
startDateStr,
`${calendarMonthStr}-28`
);
}
}
// Months with 30 days
else if (
monthNum === 4 ||
monthNum === 6 ||
monthNum === 9 ||
monthNum === 11
) {
return extractObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
`${calendarMonthStr}-28` `${calendarMonthStr}-30`
); );
} }
// Months with 31 days
// Months with 30 days else {
if (monthNum === 4 || monthNum === 6 || monthNum === 9 || monthNum === 11)
return extractObservationValuesWithinDatesInterval( return extractObservationValuesWithinDatesInterval(
obsArray, obsArray,
samplingRate, samplingRate,
startDateStr, startDateStr,
`${calendarMonthStr}-30` `${calendarMonthStr}-31`
); );
}
// Months with 31 days
return extractObservationValuesWithinDatesInterval(
obsArray,
samplingRate,
startDateStr,
`${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";
import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcessing.mjs"; import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcessing.mjs";
/**
* Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
* Rücklauf temperature [RL] (i.e., dT = VL - RL) for 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 {Array} An array made up of timestamps + values for dT
*/
const calculateVorlaufMinusRuecklaufTemperatureObservations = function (
observationsPlusMetadataArr
) {
// Extract Vorlauf temperature, Ruecklauf temperature; first element of array
const [[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]] =
observationsPlusMetadataArr;
// Compare the lengths of the observations arrays for VL and RL,
// delete the unique observation(s), if necessary
const [vorlaufTemperatureObsFinalArr, ruecklaufTemperatureObsFinalArr] =
vorlaufTemperatureObsArr.length === ruecklaufTemperatureObsArr.length
? [vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]
: checkForAndDeleteUniqueObservationsFromLargerArray(
vorlaufTemperatureObsArr,
ruecklaufTemperatureObsArr
);
// Extract the temperature values
const vorlaufTemperatureValues = vorlaufTemperatureObsFinalArr.map(
(vlTempObs) => vlTempObs[1]
);
const ruecklaufTemperatureValues = ruecklaufTemperatureObsFinalArr.map(
(rlTempObs) => rlTempObs[1]
);
// 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)
else {
return vorlaufTemperatureObsFinalArr.map((vlTempObsFinal, i) => {
// Use timestamp from VL, since is equal to that of RL
const timestamp = vlTempObsFinal[0];
// Case 1: One of the observation values is `null`,
// no need to calculate temperature difference
// Case 2: Neither of the observation values is `null`,
// calculate temperature difference
return vorlaufTemperatureValues[i] === null ||
ruecklaufTemperatureValues[i] === null
? [timestamp, null]
: [
timestamp,
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`
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 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
const nameTempDifference = `${buildingId} / dT Temperature difference (VL-RL) DS:${samplingRate}`;
return {
descriptionTempDifference,
nameTempDifference,
unitOfMeasurementVorlauf,
};
};
/** /**
* 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). In addition, create synthetic metadata
...@@ -37,13 +168,6 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function ( ...@@ -37,13 +168,6 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function (
// Note: We have to use a for/of loop here due to the asynchronous nature of our code // Note: We have to use a for/of loop here due to the asynchronous nature of our code
for (const bldgDataPtSamplingRateNestedArr of buildingDataPointSamplingRateNestedTwiceArr) { 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 = const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
baseUrl, baseUrl,
...@@ -51,83 +175,20 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function ( ...@@ -51,83 +175,20 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function (
bldgDataPtSamplingRateNestedArr bldgDataPtSamplingRateNestedArr
); );
// Extract Vorlauf temperature, Ruecklauf temperature and metadata // dT observations (timestamp + value)
const [ const vorlaufMinusRuecklaufTemperatureObs =
[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr], calculateVorlaufMinusRuecklaufTemperatureObservations(
[metadataVorlauf, metadataRuecklauf], observationsPlusMetadata
] = observationsPlusMetadata; );
// Compare the lengths of the observations arrays for VL and RL,
// delete the unique observation(s), if necessary
const [vorlaufTemperatureObsFinalArr, ruecklaufTemperatureObsFinalArr] =
vorlaufTemperatureObsArr.length === ruecklaufTemperatureObsArr.length
? [vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]
: checkForAndDeleteUniqueObservationsFromLargerArray(
vorlaufTemperatureObsArr,
ruecklaufTemperatureObsArr
);
// Extract the temperature values
const vorlaufTemperatureValues = vorlaufTemperatureObsFinalArr.map(
(vlTempObs) => vlTempObs[1]
);
const ruecklaufTemperatureValues = ruecklaufTemperatureObsFinalArr.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) => {
// Use timestamp from VL, since is equal to that of RL
const timestamp = vlTempObs[0];
// Case 1: One of the observation values is `null`,
// 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`,
// calculate temperature difference
return [
timestamp,
vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i],
];
}
);
// From Vorlauf metadata, extract `name` and `unitOfMeasurement` // dT metadata
const { const {
name: datastreamNameVorlauf, descriptionTempDifference,
unitOfMeasurement: unitOfMeasurementVorlauf, nameTempDifference,
} = metadataVorlauf; unitOfMeasurementVorlauf,
} = createVorlaufMinusRuecklaufTemperatureMetadata(
// From Ruecklauf metadata, extract `name` observationsPlusMetadata
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 // The datastream object that we return needs to have these property names
const description = descriptionTempDifference; const description = descriptionTempDifference;
......
...@@ -48,20 +48,21 @@ const createSeriesOptionsForColumnChart = function ( ...@@ -48,20 +48,21 @@ 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 {
name: `${buildingIdsPhenomenonNamesArr[i]}`, name: `${buildingIdsPhenomenonNamesArr[i]}`,
data: formattedAggResArray, data: formattedAggResArray,
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
}; };
} }
); );
}
}; };
/** /**
...@@ -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
// Case 1: No aggregation
if (extractedFormattedDatastreamProperties?.aggregationType === undefined) { if (extractedFormattedDatastreamProperties?.aggregationType === undefined) {
// Case 1: No aggregation
({ ({
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
return (hexToRGBMapping?.[hexCode]).slice(4, -1); else {
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
return `${ else {
aggregationInterval.slice(0, 1).toUpperCase() + aggregationInterval.slice(1) return `${
} ${aggregationType.slice(0, 1).toUpperCase() + aggregationType.slice(1)}`; aggregationInterval.slice(0, 1).toUpperCase() +
aggregationInterval.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
return `${createPartialTitleForLineOrColumnChart( else {
aggregationInterval, return `${createPartialTitleForLineOrColumnChart(
aggregationType aggregationInterval,
)}: ${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; aggregationType
)}: ${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,19 +53,20 @@ const createSeriesOptionsForLineChart = function ( ...@@ -53,19 +53,20 @@ 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]}`,
data: formattedObsArray, data: formattedObsArray,
color: seriesColorsArr[i], color: seriesColorsArr[i],
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
return createCombinedObservationValues(obsArrayOne, obsArrayTwo); else {
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( return createCombinedTextDelimitedByComma(combinedNameSymbolArr);
(phenomenonNameYAxis, i) => }
`${phenomenonNameYAxis} [${unitOfMeasurementSymbolsYAxisArr[i]}]`
);
return createCombinedTextDelimitedByComma(combinedNameSymbolArr);
}; };
/** /**
...@@ -159,18 +161,19 @@ const createSeriesOptionsForScatterPlot = function ( ...@@ -159,18 +161,19 @@ 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}`,
data: formattedObsArray, data: formattedObsArray,
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];
return [buildingAbbrev, phenomenonAbbrev, samplingRateAbbrev]; 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];
}
}; };
/** /**
...@@ -483,18 +463,19 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function ( ...@@ -483,18 +463,19 @@ 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,
selectedDataPointsArr[i], selectedDataPointsArr[i],
...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
return unitOfMeasurementSymbolString; else {
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,17 +160,19 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( ...@@ -158,17 +160,19 @@ const extractPropertiesFromFormattedDatastreamMetadata = function (
buildingIdsPhenomenonNamesArr, buildingIdsPhenomenonNamesArr,
unitOfMeasurementSymbolsArr, unitOfMeasurementSymbolsArr,
}; };
}
// Case 2: Metadata USED for aggregation; "isMetadataForAggregation" = true // Case 2: Metadata USED for aggregation; "isMetadataForAggregation" = true
return { else {
datastreamDescriptionsArr, return {
datastreamNamesArr, datastreamDescriptionsArr,
phenomenonNamesArr, datastreamNamesArr,
buildingIdsPhenomenonNamesArr, phenomenonNamesArr,
unitOfMeasurementSymbolsArr, buildingIdsPhenomenonNamesArr,
aggregationInterval, unitOfMeasurementSymbolsArr,
aggregationType, aggregationInterval,
}; 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"
return Number( );
buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate] } else {
); 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