diff --git a/public/js/src_modules/aggregate.mjs b/public/js/src_modules/aggregate.mjs index 02f5998067cca546ae6434e59558a19fe5b7f6be..0bf8ae0acb4233f0e9f0e80c9c802b56193f8abe 100644 --- a/public/js/src_modules/aggregate.mjs +++ b/public/js/src_modules/aggregate.mjs @@ -163,10 +163,9 @@ const calculateMinimumObservationValuesWithinInterval = function ( ) ); } - // Calculate minimum values of observations - monthly // Note the use of the two nested `map` methods - if (aggregationInterval === "monthly") { + else if (aggregationInterval === "monthly") { return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => calculateMinimumObservationValuesWithinMonthInterval( @@ -211,10 +210,9 @@ const calculateMaximumObservationValuesWithinInterval = function ( ) ); } - // Calculate maximum values of observations - monthly // Note the use of the two nested `map` methods - if (aggregationInterval === "monthly") { + else if (aggregationInterval === "monthly") { return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => calculateMaximumObservationValuesWithinMonthInterval( @@ -259,10 +257,9 @@ const calculateSumOfObservationValuesWithinInterval = function ( ) ); } - // Calculate sum of values of observations - monthly // Note the use of the two nested `map` methods - if (aggregationInterval === "monthly") { + else if (aggregationInterval === "monthly") { return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => calculateSumOfObservationValuesWithinMonthInterval( @@ -307,10 +304,9 @@ const calculateAverageOfObservationValuesWithinInterval = function ( ) ); } - // Calculate average of values of observations - monthly // Note the use of the two nested `map` methods - if (aggregationInterval === "monthly") { + else if (aggregationInterval === "monthly") { return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) => uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => calculateAverageOfObservationValuesWithinMonthInterval( diff --git a/public/js/src_modules/aggregateHelpers.mjs b/public/js/src_modules/aggregateHelpers.mjs index 64d6176a041f61777cf28d6e74e7f7dc2d9b6c0a..85c1583e80d34174eed88809a77c7e5cb15bad98 100644 --- a/public/js/src_modules/aggregateHelpers.mjs +++ b/public/js/src_modules/aggregateHelpers.mjs @@ -16,18 +16,17 @@ const createTimeStringsForInterval = function (phenomenonSamplingRate) { if ( phenomenonSamplingRate !== fifteenMinutes && phenomenonSamplingRate !== sixtyMinutes - ) + ) { throw new Error( `Check that the provided phenomenon sampling rate string is in this format: "15min" or "60min"` ); - + } // 15 min sampling rate - if (phenomenonSamplingRate === fifteenMinutes) { + else if (phenomenonSamplingRate === fifteenMinutes) { return [startTime, endTimeFifteenMinutes]; } - // 60 min sampling rate - if (phenomenonSamplingRate === sixtyMinutes) { + else if (phenomenonSamplingRate === sixtyMinutes) { return [startTime, endTimeSixtyMinutes]; } }; @@ -66,13 +65,15 @@ const getIndexOfTimestamp = function (inputTimestampArr, timestampOfInterest) { ); // If the timestamp does not exist in the timestamp array - if (timestampIndex === -1) + if (timestampIndex === -1) { throw new Error( "A start or end timestamp could not be found in the timestamp array" ); - + } // If the timestamp exists in the timestamp array - return timestampIndex; + else { + return timestampIndex; + } }; /** @@ -210,47 +211,56 @@ const extractObservationValuesWithinMonthInterval = function ( const [yearNum, monthNum] = extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr); - if (monthNum < 1 || monthNum > 12) return; - // All the months start on the first const startDateStr = `${calendarMonthStr}-01`; + if (monthNum < 1 || monthNum > 12) { + throw new Error("The specified digit for the month of the year is invalid"); + } // February - if (monthNum === 2) { + else if (monthNum === 2) { // Leap year - if (checkIfLeapYear(yearNum)) + if (checkIfLeapYear(yearNum)) { return extractObservationValuesWithinDatesInterval( obsArray, samplingRate, startDateStr, `${calendarMonthStr}-29` ); - + } // 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( obsArray, samplingRate, startDateStr, - `${calendarMonthStr}-28` + `${calendarMonthStr}-30` ); } - - // Months with 30 days - if (monthNum === 4 || monthNum === 6 || monthNum === 9 || monthNum === 11) + // Months with 31 days + else { return extractObservationValuesWithinDatesInterval( obsArray, samplingRate, startDateStr, - `${calendarMonthStr}-30` + `${calendarMonthStr}-31` ); - - // Months with 31 days - return extractObservationValuesWithinDatesInterval( - obsArray, - samplingRate, - startDateStr, - `${calendarMonthStr}-31` - ); + } }; /** diff --git a/public/js/src_modules/calculateTemperatureDiff.mjs b/public/js/src_modules/calculateTemperatureDiff.mjs index 5b7c55097489f9e000ca603efb4faafe68eb4dee..615c0238a117fca5463000d8cf3dfddacfa81cd4 100644 --- a/public/js/src_modules/calculateTemperatureDiff.mjs +++ b/public/js/src_modules/calculateTemperatureDiff.mjs @@ -1,11 +1,142 @@ "use strict"; -import { checkForAndDeleteUniqueObservationsFromLargerArray } from "./chartHelpers.mjs"; +import { + checkForAndDeleteUniqueObservationsFromLargerArray, + extractSamplingRateFromDatastreamName, + extractBuildingIdFromDatastreamName, +} from "./chartHelpers.mjs"; import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./fetchData.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 * Rücklauf temperature [RL] (i.e., dT = VL - RL). In addition, create synthetic metadata @@ -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 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, @@ -51,83 +175,20 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function ( 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, - // 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], - ]; - } - ); + // dT observations (timestamp + value) + const vorlaufMinusRuecklaufTemperatureObs = + calculateVorlaufMinusRuecklaufTemperatureObservations( + observationsPlusMetadata + ); - // From Vorlauf metadata, extract `name` and `unitOfMeasurement` + // dT metadata 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 + descriptionTempDifference, + nameTempDifference, + unitOfMeasurementVorlauf, + } = createVorlaufMinusRuecklaufTemperatureMetadata( + observationsPlusMetadata ); - 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; diff --git a/public/js/src_modules/chartColumn.mjs b/public/js/src_modules/chartColumn.mjs index 35fc3d5203968d2f5f2e1338f99fad622f3771e7..9c151f9a1e5d25c7625071b8a6ccd36dfffc2824 100644 --- a/public/js/src_modules/chartColumn.mjs +++ b/public/js/src_modules/chartColumn.mjs @@ -48,20 +48,21 @@ const createSeriesOptionsForColumnChart = function ( if ( formattedAggregatedResultForColumnChart.length !== buildingIdsPhenomenonNamesArr.length - ) + ) { throw new Error( "The observations array and phenomenon names array have different lengths" ); - - return formattedAggregatedResultForColumnChart.map( - (formattedAggResArray, i) => { - return { - name: `${buildingIdsPhenomenonNamesArr[i]}`, - data: formattedAggResArray, - turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release - }; - } - ); + } else { + return formattedAggregatedResultForColumnChart.map( + (formattedAggResArray, i) => { + return { + name: `${buildingIdsPhenomenonNamesArr[i]}`, + data: formattedAggResArray, + turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release + }; + } + ); + } }; /** @@ -83,16 +84,17 @@ const drawColumnChartHighcharts = function ( aggregationType; // Check whether the datastream properties are for aggregated observations + // Case 1: No aggregation if (extractedFormattedDatastreamProperties?.aggregationType === undefined) { - // Case 1: No aggregation ({ datastreamNamesArr, phenomenonNamesArr, buildingIdsPhenomenonNamesArr, unitOfMeasurementSymbolsArr, } = extractedFormattedDatastreamProperties); - } else { - // Case 2: Aggregation + } + // Case 2: Aggregation + else { ({ datastreamNamesArr, phenomenonNamesArr, diff --git a/public/js/src_modules/chartHelpers.mjs b/public/js/src_modules/chartHelpers.mjs index b079ae35b56db19f7fd644ede100f3f4aff823a5..1ef57452f34c79c8ad086d2ccf4a9911cdc54f80 100644 --- a/public/js/src_modules/chartHelpers.mjs +++ b/public/js/src_modules/chartHelpers.mjs @@ -53,18 +53,18 @@ const removeUniqueObservationsFromLargerArray = function ( largerObsArr ) { // 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 - const processedLargerObsArr = largerObsArr; + // Create a copy of the larger observation array, will be modified in place + const largerObsCopyArr = [...largerObsArr]; - reversedUniqueIndexesArr.forEach((index) => { - if (index > -1) { - processedLargerObsArr.splice(index, 1); + uniqueIndexesReversedCopyArr.forEach((uniqueIndex) => { + if (uniqueIndex > -1) { + largerObsCopyArr.splice(uniqueIndex, 1); } }); - return processedLargerObsArr; + return largerObsCopyArr; }; /** @@ -75,10 +75,11 @@ const removeUniqueObservationsFromLargerArray = function ( */ const getLargerArrayBetweenTwoInputArrays = function (firstArr, secondArr) { if (firstArr.length === secondArr.length) return; - - if (firstArr.length > secondArr.length) return firstArr; - - if (firstArr.length < secondArr.length) return secondArr; + else if (firstArr.length > secondArr.length) { + return firstArr; + } else if (firstArr.length < secondArr.length) { + return secondArr; + } }; /** @@ -89,10 +90,11 @@ const getLargerArrayBetweenTwoInputArrays = function (firstArr, secondArr) { */ const getSmallerArrayBetweenTwoInputArrays = function (firstArr, secondArr) { if (firstArr.length === secondArr.length) return; - - if (firstArr.length < secondArr.length) return firstArr; - - if (firstArr.length > secondArr.length) return secondArr; + else if (firstArr.length < secondArr.length) { + return firstArr; + } else if (firstArr.length > secondArr.length) { + return secondArr; + } }; /** @@ -162,17 +164,17 @@ const checkForAndDeleteUniqueObservationsFromLargerArray = function ( obsArrayTwo ) { if (obsArrayOne.length === obsArrayTwo.length) return; - - // Case 1: obsArrayOne.length < obsArrayTwo.length - if (obsArrayOne.length < obsArrayTwo.length) { + // Case 1: obsArrayTwo larger than obsArrayOne + else if (obsArrayOne.length < obsArrayTwo.length) { const [biggerObsArr, smallerObsArr] = deleteUniqueObservationsFromLargerArray(obsArrayOne, obsArrayTwo); return [smallerObsArr, biggerObsArr]; } - - // Case 2: obsArrayOne.length > obsArrayTwo.length - return deleteUniqueObservationsFromLargerArray(obsArrayOne, obsArrayTwo); + // Case 2: obsArrayOne larger than obsArrayTwo + else if (obsArrayOne.length > obsArrayTwo.length) { + return deleteUniqueObservationsFromLargerArray(obsArrayOne, obsArrayTwo); + } }; /** @@ -194,14 +196,16 @@ const convertHexColorToRGBColor = function (hexCode) { "#91e8e1": "rgb(145, 232, 225)", }; - if (hexToRGBMapping?.[hexCode] === undefined) + if (hexToRGBMapping?.[hexCode] === undefined) { throw new Error( "The provided hex code is not valid or is not supported by this function" ); - + } // Extract the RGB color elements as a single string // 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 ( ) { // Case 1: No aggregation; return empty string if (!aggregationInterval && !aggregationType) return ``; - // Case 2: Aggregation; capitalize the first characters - return `${ - aggregationInterval.slice(0, 1).toUpperCase() + aggregationInterval.slice(1) - } ${aggregationType.slice(0, 1).toUpperCase() + aggregationType.slice(1)}`; + else { + return `${ + aggregationInterval.slice(0, 1).toUpperCase() + + aggregationInterval.slice(1) + } ${aggregationType.slice(0, 1).toUpperCase() + aggregationType.slice(1)}`; + } }; /** @@ -289,12 +295,13 @@ const createFullTitleForLineOrColumnChart = function ( aggregationInterval, aggregationType )}${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; - // Case 2: Aggregation - return `${createPartialTitleForLineOrColumnChart( - aggregationInterval, - aggregationType - )}: ${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; + else { + return `${createPartialTitleForLineOrColumnChart( + aggregationInterval, + aggregationType + )}: ${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; + } }; /** @@ -376,6 +383,8 @@ export { checkForAndDeleteUniqueObservationsFromLargerArray, createCombinedTextDelimitedByAmpersand, createCombinedTextDelimitedByComma, + extractSamplingRateFromDatastreamName, + extractBuildingIdFromDatastreamName, createFullTitleForLineOrColumnChart, createTitleForHeatmap, createSubtitleForChart, diff --git a/public/js/src_modules/chartLine.mjs b/public/js/src_modules/chartLine.mjs index 2538662fd06e9f6e517d4cc5a89b52f57564e000..f34b8a90a79b2e9415cb63cdb79b91bdcfb0bb1b 100644 --- a/public/js/src_modules/chartLine.mjs +++ b/public/js/src_modules/chartLine.mjs @@ -53,19 +53,20 @@ const createSeriesOptionsForLineChart = function ( if ( formattedObsArraysForLineChart.length !== buildingIdsPhenomenonNamesArr.length - ) + ) { throw new Error( "The observations array and phenomenon names array have different lengths" ); - - return formattedObsArraysForLineChart.map((formattedObsArray, i) => { - return { - name: `${buildingIdsPhenomenonNamesArr[i]}`, - data: formattedObsArray, - color: seriesColorsArr[i], - turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release - }; - }); + } else { + return formattedObsArraysForLineChart.map((formattedObsArray, i) => { + return { + name: `${buildingIdsPhenomenonNamesArr[i]}`, + data: formattedObsArray, + color: seriesColorsArr[i], + turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release + }; + }); + } }; /** diff --git a/public/js/src_modules/chartScatterPlot.mjs b/public/js/src_modules/chartScatterPlot.mjs index e84e6118f365ecbb91b62defe8d9838c10dea958..c12fd350e5cbd80b7fe4f928c500ca279ee34edb 100644 --- a/public/js/src_modules/chartScatterPlot.mjs +++ b/public/js/src_modules/chartScatterPlot.mjs @@ -45,9 +45,10 @@ const formatSensorThingsApiResponseForScatterPlot = function ( return createCombinedObservationValues(obsArrayOneFinal, obsArrayTwoFinal); } - // When our observation arrays already have SAME lengths - return createCombinedObservationValues(obsArrayOne, obsArrayTwo); + else { + return createCombinedObservationValues(obsArrayOne, obsArrayTwo); + } }; /** @@ -105,21 +106,22 @@ const createYAxisTitleTextScatterPlot = function ( // y-axis phenomenon symbols start at array index 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 // Use one of the arrays for looping if ( phenomenonNamesYAxisArr.length !== unitOfMeasurementSymbolsYAxisArr.length - ) + ) { throw new Error( "The phenomenon names array and unit of measurement symbols array have different lengths" ); - - const combinedNameSymbolArr = phenomenonNamesYAxisArr.map( - (phenomenonNameYAxis, i) => - `${phenomenonNameYAxis} [${unitOfMeasurementSymbolsYAxisArr[i]}]` - ); - - return createCombinedTextDelimitedByComma(combinedNameSymbolArr); + } else { + return createCombinedTextDelimitedByComma(combinedNameSymbolArr); + } }; /** @@ -159,18 +161,19 @@ const createSeriesOptionsForScatterPlot = function ( // Use one of the arrays for looping if ( formattedObsArraysForScatterPlot.length !== phenomenonNamesYAxisArr.length - ) + ) { throw new Error( "The observations array and phenomenon names array have different lengths" ); - - return formattedObsArraysForScatterPlot.map((formattedObsArray, i) => { - return { - name: `${phenomenonNamesYAxisArr[i]}, ${phenomenonNameXAxis}`, - data: formattedObsArray, - color: seriesColors[i], - }; - }); + } else { + return formattedObsArraysForScatterPlot.map((formattedObsArray, i) => { + return { + name: `${phenomenonNamesYAxisArr[i]}, ${phenomenonNameXAxis}`, + data: formattedObsArray, + color: seriesColors[i], + }; + }); + } }; /** diff --git a/public/js/src_modules/dropDownListHelpers.mjs b/public/js/src_modules/dropDownListHelpers.mjs index b505d7e198d17c8eacb630f6ac737ef89e442204..3f2a8ffca7993aca7491faf1063bf615b000b661 100644 --- a/public/js/src_modules/dropDownListHelpers.mjs +++ b/public/js/src_modules/dropDownListHelpers.mjs @@ -111,24 +111,22 @@ const getSelectedOptionsFromAllDropDownLists = function () { ); // 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"); - - if (selectedBuildingDataPointOptionsSplitArr.length === 0) + } else if (selectedBuildingDataPointOptionsSplitArr.length === 0) { throw new Error("Please ensure that at least one data point is selected"); - - if (selectedSamplingRateArr.length === 0) + } else if (selectedAggregationOptionsArr.length === 0) { throw new Error("Please ensure that the aggregation type is selected"); - - if (selectedSamplingRateArr.length === 0) + } else if (selectedSamplingRateArr.length === 0) { throw new Error("Please ensure that the sampling rate is selected"); - - return [ - selectedChartTypeArr, - selectedBuildingDataPointOptionsSplitArr, - selectedAggregationOptionsArr, - selectedSamplingRateArr, - ]; + } else { + return [ + selectedChartTypeArr, + selectedBuildingDataPointOptionsSplitArr, + selectedAggregationOptionsArr, + selectedSamplingRateArr, + ]; + } }; /** @@ -231,14 +229,6 @@ const extractTemperatureDifferenceOptions = function ( const bldgDataPntSamplingRateAbbrvArr = 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); }); @@ -326,9 +316,8 @@ const checkIfSelectedBuildingDataPointsOptionsAreValid = function ( throw new Error("A heatmap can only display one data point at a time"); } } - // A scatter plot requires at least two data points - if (selectedChartType === "Scatter Plot") { + else if (selectedChartType === "Scatter Plot") { if (selectedBuildingsDataPointsSamplingRateAbbrevNestedArr.length >= 2) return true; else if ( @@ -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 = fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm]; @@ -450,7 +416,21 @@ const getBuildingSensorSamplingRateAbbreviation = function ( const samplingRateAbbrev = 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 ( // Assume that the buildings and data points arrays have equal length // use one of the arrays for looping - if (selectedBuildingsArr.length !== selectedDataPointsArr.length) + if (selectedBuildingsArr.length !== selectedDataPointsArr.length) { throw new Error( "The buildings array and data points array have different lengths" ); - - return selectedBuildingsArr.map((selectedBuilding, i) => - getBuildingSensorSamplingRateAbbreviation( - selectedBuilding, - selectedDataPointsArr[i], - ...selectedSamplingRateArr - ) - ); + } else { + return selectedBuildingsArr.map((selectedBuilding, i) => + getBuildingSensorSamplingRateAbbreviation( + selectedBuilding, + selectedDataPointsArr[i], + ...selectedSamplingRateArr + ) + ); + } }; export { diff --git a/public/js/src_modules/fetchedDataProcessing.mjs b/public/js/src_modules/fetchedDataProcessing.mjs index ec6ee9206bd784e6b28e9adbbc4d320c1adc3cb1..b0d4d18cbec6d09013aa2ddf281bfafe177db535 100644 --- a/public/js/src_modules/fetchedDataProcessing.mjs +++ b/public/js/src_modules/fetchedDataProcessing.mjs @@ -11,14 +11,18 @@ const matchUnitOfMeasurementSymbolStringToSymbol = function ( const unicodeCodePointDegreeSymbol = "\u00B0"; const unicodeCodePointSuperscriptThree = "\u00B3"; - if (unitOfMeasurementSymbolString === "degC") + // Symbol - temperature + if (unitOfMeasurementSymbolString === "degC") { return `${unicodeCodePointDegreeSymbol}C`; - - if (unitOfMeasurementSymbolString === "m3/h") + } + // Symbol - flow rate + else if (unitOfMeasurementSymbolString === "m3/h") { return `m${unicodeCodePointSuperscriptThree}/h`; - + } // If no symbol exists - return unitOfMeasurementSymbolString; + else { + return unitOfMeasurementSymbolString; + } }; /** @@ -94,39 +98,37 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( if ( formattedDatastreamsMetadataArr === undefined || isMetadataForAggregation === undefined - ) + ) { throw new Error( "This function expects two arguments, ensure that both have been supplied" ); - - if ( + } else if ( formattedDatastreamsMetadataArr && isMetadataForAggregation && (aggregationInterval === undefined || aggregationType === undefined) - ) + ) { throw new Error( "This function expects four arguments, ensure that all of them have been supplied" ); - - if ( + } else if ( isMetadataForAggregation && aggregationInterval !== "daily" && aggregationInterval !== "monthly" - ) + ) { throw new Error( `The supported aggegation interval strings are "daily" or "monthly"` ); - - if ( + } else if ( isMetadataForAggregation && aggregationType !== "minimum" && aggregationType !== "maximum" && aggregationType !== "sum" && aggregationType !== "average" - ) + ) { throw new Error( `The supported aggegation type strings are "minimum", "maximum", "sum" or "average"` ); + } // Create arrays from the properties of the formatted datastream metadata const datastreamDescriptionsArr = formattedDatastreamsMetadataArr.map( @@ -150,7 +152,7 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( ); // Case 1: Metadata NOT USED for aggregation; "isMetadataForAggregation" = false - if (!isMetadataForAggregation) + if (!isMetadataForAggregation) { return { datastreamDescriptionsArr, datastreamNamesArr, @@ -158,17 +160,19 @@ const extractPropertiesFromFormattedDatastreamMetadata = function ( buildingIdsPhenomenonNamesArr, unitOfMeasurementSymbolsArr, }; - + } // Case 2: Metadata USED for aggregation; "isMetadataForAggregation" = true - return { - datastreamDescriptionsArr, - datastreamNamesArr, - phenomenonNamesArr, - buildingIdsPhenomenonNamesArr, - unitOfMeasurementSymbolsArr, - aggregationInterval, - aggregationType, - }; + else { + return { + datastreamDescriptionsArr, + datastreamNamesArr, + phenomenonNamesArr, + buildingIdsPhenomenonNamesArr, + unitOfMeasurementSymbolsArr, + aggregationInterval, + aggregationType, + }; + } }; export { diff --git a/public/js/src_modules/getDatastreamId.mjs b/public/js/src_modules/getDatastreamId.mjs index ff5303a8afb4f9a2e4f0691ee8ea5c22ef779c63..0334886f871548fef8154872a509478360872248 100644 --- a/public/js/src_modules/getDatastreamId.mjs +++ b/public/js/src_modules/getDatastreamId.mjs @@ -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]?.[ samplingRate ] === undefined - ) - return; - - return Number( - buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate] - ); + ) { + throw new Error( + "The supplied sampling rate is not valid or is not supported by this function" + ); + } else { + return Number( + buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate] + ); + } };