"use strict";
/**
* Get the selected option(s) from a dropdown list
*
* @param {String} selectorStr A CSS selector string representing the dropdown list
* @returns {Array} An array of string(s) representing the value(s) of the selected `` elements
*/
const getSelectedOptionsFromDropDownList = function (selectorStr) {
// Array to store our final result
const selectedOptionsArr = [];
// Select all the matching elements as a NodeList
const optionElements = document.querySelectorAll(`${selectorStr} option`);
optionElements.forEach((optionEl) => {
if (optionEl.selected) {
selectedOptionsArr.push(optionEl.value);
}
});
return selectedOptionsArr;
};
/**
* Process the selected option(s) from a buildings & data points dropdown list.
*
* @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected ` ` elements
* @returns {Array} An array of string(s) representing the processed value(s) of the selected buildings & data points option(s)
*/
const processSelectionsFromBuildingDataPointOptions = function (
selectedOptionsArr
) {
// Array to store our final result
const selectedOptionsBuildingDataPointArr = [];
selectedOptionsArr.forEach((optionStr) => {
// Case 1: element's value CONTAINS a "/" character
// We wish to create a string like this `Bau 101/VL`
if (optionStr.includes("/")) {
// Split the element's value into two substrings
const optionsStrPartOne = optionStr.slice(0, 3);
const optionsStrPartTwo = optionStr.slice(3);
// Create a new string for the first substring
const optionsStrPartOneNew = `Bau ${optionsStrPartOne}`;
// Create a new combined string
const optionsStrNew = optionsStrPartOneNew + optionsStrPartTwo;
selectedOptionsBuildingDataPointArr.push(optionsStrNew);
}
// Case 2: element's value DOES NOT CONTAIN a "/" character
// We wish to create a string like this `Other/Außentemp`
else {
selectedOptionsBuildingDataPointArr.push(`Other/${optionStr}`);
}
});
return selectedOptionsBuildingDataPointArr;
};
/**
* Split an option element's value (a string) using a forward slash character ("/") as the delimiter
*
* @param {String} selectedOptionsStr A string representing the value of the selected ` ` element
* @returns {String} Resulting strings after splitting
*/
const splitOptionsTextDelimitedBySlash = function (selectedOptionsStr) {
return selectedOptionsStr.split("/");
};
/**
* Split an array of option element's values (strings) which have a forward slash character ("/") as the delimiter
*
* @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected ` ` elements
* @returns {Array} An array made up of resulting strings after splitting
*/
const splitMultipleOptionsTextDelimitedBySlash = function (selectedOptionsArr) {
return selectedOptionsArr.map((selectedOption) =>
splitOptionsTextDelimitedBySlash(selectedOption)
);
};
/**
* Get the values from the currently selected options in ALL the drop down lists
* @returns {Array} An array containing four arrays, where each array contains the values of the selected options
*/
const getSelectedOptionsFromAllDropDownLists = function () {
const selectedBuildingDataPointOptionsArr =
processSelectionsFromBuildingDataPointOptions(
getSelectedOptionsFromDropDownList("#drop-down--bldg-data-point")
);
// Separate the building ID from the data point
const selectedBuildingDataPointOptionsSplitArr =
splitMultipleOptionsTextDelimitedBySlash(
selectedBuildingDataPointOptionsArr
);
const selectedAggregationOptionsArr = getSelectedOptionsFromDropDownList(
"#drop-down--aggregation-type"
);
const selectedSamplingRateArr = getSelectedOptionsFromDropDownList(
"#drop-down--sampling-rate"
);
const selectedChartTypeArr = getSelectedOptionsFromDropDownList(
"#drop-down--chart-type"
);
// Ensure that all the options have at least one selection
if (selectedChartTypeArr.length === 0) {
throw new Error("Please ensure that the chart type is selected");
} else if (selectedBuildingDataPointOptionsSplitArr.length === 0) {
throw new Error("Please ensure that at least one data point is selected");
} else if (selectedAggregationOptionsArr.length === 0) {
throw new Error("Please ensure that the aggregation type is selected");
} else if (selectedSamplingRateArr.length === 0) {
throw new Error("Please ensure that the sampling rate is selected");
} else {
return [
selectedChartTypeArr,
selectedBuildingDataPointOptionsSplitArr,
selectedAggregationOptionsArr,
selectedSamplingRateArr,
];
}
};
/**
* Check whether the abbreviated buildings + data points + sampling rate strings
* contain the temperature difference [dT] between Vorlauf temperature [VL] and
* Rücklauf temperature [RL] (i.e., dT = VL - RL). Unlike all the other data points,
* this data point is computed in a separate step
*
* @param {Array} buildingDataPointSamplingRateAbbrevArr An array that contains nested array(s) made up of strings representing the abbreviated building + data point + sampling rate values
* @returns {Boolean} true if the selected options contain the string `dT`, false otherwise
*/
const checkIfSelectedOptionsContainTemperatureDifference = function (
buildingDataPointSamplingRateAbbrevArr
) {
// Create a flattened copy of our input array,
// then check if it contains the string `dT`
return buildingDataPointSamplingRateAbbrevArr.flat().includes("dT");
};
/**
* Get the index(es) of the the abbreviated buildings + data points + sampling rate string(s)
* that contains the temperature difference (dT)
*
* @param {Array} buildingDataPointSamplingRateAbbrevArr An array that contains nested array(s) made up of abbreviated building + data point + sampling rate string(s)
* @returns {Array} An array that contains an integer(s) whose value(s) are the index(es) of the abbreviated building + data point + sampling rate string(s) containing the temperature difference (dT)
*/
const getIndexesOfTemperatureDifferenceOptions = function (
buildingDataPointSamplingRateAbbrevArr
) {
// An array to store the final result
const foundIndexesArr = [];
// Use the index, i, provided by `forEach` array method
buildingDataPointSamplingRateAbbrevArr.forEach(
(bldgDataPntSamplingRateAbbrvArr, i) => {
if (bldgDataPntSamplingRateAbbrvArr.includes("dT")) {
foundIndexesArr.push(i);
}
}
);
return foundIndexesArr;
};
/**
* Delete the abbreviated building + data point + sampling rate string(s) that contains the temperature difference (dT)
*
* @param {Array} buildingDataPointSamplingRateAbbrevArr An array that contains nested array(s) made up of abbreviated building + data point + sampling rate string(s)
* @returns {Array} An array that contains nested array(s) made up of abbreviated building + data point + sampling rate string(s)
*/
const deleteTemperatureDifferenceOptions = function (
buildingDataPointSamplingRateAbbrevArr
) {
// Create a copy of the input array, will be modified in place
const buildingDataPointSamplingRateAbbrevCopyArr = [
...buildingDataPointSamplingRateAbbrevArr,
];
// Calculate the index(es) that we wish to delete
const foundIndexesArr = getIndexesOfTemperatureDifferenceOptions(
buildingDataPointSamplingRateAbbrevCopyArr
);
// Delete the index(es) of `dT`, modifies the array in place
// Note: The resulting array is sparse
foundIndexesArr.forEach(
(foundIndex) =>
delete buildingDataPointSamplingRateAbbrevCopyArr[foundIndex]
);
// Remove the empty sub array(s) that makes entire array sparse
// Note: `empty` does not mean `undefined` or `null`
return buildingDataPointSamplingRateAbbrevCopyArr.filter(
(bldgDataPntSamplingRate) => typeof bldgDataPntSamplingRate === "object"
);
};
/**
* Extract the abbreviated building + data point + sampling rate string(s) that contains the temperature difference (dT)
*
* @param {Array} buildingDataPointSamplingRateAbbrevArr An array that contains nested array(s) made up of abbreviated building + data point + sampling rate string(s)
* @returns {Array} An array that contains nested array(s) made up of abbreviated building + data point + sampling rate string(s)
*/
const extractTemperatureDifferenceOptions = function (
buildingDataPointSamplingRateAbbrevArr
) {
// Array to store final result
const temperatureDifferenceOptionsAbbrevArr = [];
// Calculate the index(es) that we wish to extract
const foundIndexesArr = getIndexesOfTemperatureDifferenceOptions(
buildingDataPointSamplingRateAbbrevArr
);
foundIndexesArr.forEach((foundIndex) => {
// Extracted array for a single found index
const bldgDataPntSamplingRateAbbrvArr =
buildingDataPointSamplingRateAbbrevArr[foundIndex];
temperatureDifferenceOptionsAbbrevArr.push(bldgDataPntSamplingRateAbbrvArr);
});
return temperatureDifferenceOptionsAbbrevArr;
};
/**
* Extract the abbreviated building + sampling rate string(s) for use in calculating the temperature difference (dT = VL - RL)
*
* @param {Array} buildingDataPointSamplingRateAbbrevArr An array that contains nested array(s) made up of abbreviated building + data point + sampling rate string(s)
* @returns {Array} An array that contains nested array(s) made up of abbreviated building + sampling rate string(s)
*/
const extractBuildingPlusSamplingRate = function (
buildingDataPointSamplingRateAbbrevArr
) {
// Array to store final result
const temperatureDifferenceOptionsAbbrevArr = [];
// Calculate the index(es) that we wish to extract
const foundIndexesArr = getIndexesOfTemperatureDifferenceOptions(
buildingDataPointSamplingRateAbbrevArr
);
foundIndexesArr.forEach((foundIndex) => {
// Extracted array for a single found index
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(bldgSamplingRateAbbrvArr);
});
return temperatureDifferenceOptionsAbbrevArr;
};
/**
* Determine if a chart requires raw observations instead of aggregated observations
*
* @param {String} selectedAggregationType The selected aggregation type
* @param {String} selectedAggregationDuration The selected aggregation duration
* @returns {Boolean} true if the chart requires raw observations, false if not
*/
const checkIfChartRequiresRawObservations = function (
selectedAggregationType,
selectedAggregationDuration
) {
if (
selectedAggregationType === "None (raw data)" &&
selectedAggregationDuration === undefined
) {
return true;
} else {
return false;
}
};
/**
* Check if the selected building(s) + data point(s) options are valid for
* drawing either a heatmp or scatter plot. If these options are
* invalid, throw an error
*
* @param {Array} selectedChartTypeArr An array of string(s) representing the selected chart type(s)
* @param {Array} selectedBuildingsDataPointsSamplingRateAbbrevNestedArr An array that is made up of sub array(s) whose elements are strings representing the selected buildings, data points and sampling rates
* @returns {Boolean} true, if there are no errors thrown
*/
const checkIfSelectedBuildingDataPointsOptionsAreValid = function (
selectedChartTypeArr,
selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
) {
for (const selectedChartType of selectedChartTypeArr) {
// A heatmap can only visualize one data point
if (selectedChartType === "Heatmap") {
if (selectedBuildingsDataPointsSamplingRateAbbrevNestedArr.length === 1)
return true;
else if (
selectedBuildingsDataPointsSamplingRateAbbrevNestedArr.length > 1
) {
throw new Error("A heatmap can only display one data point at a time");
}
}
// A scatter plot requires at least two data points
else if (selectedChartType === "Scatter Plot") {
if (selectedBuildingsDataPointsSamplingRateAbbrevNestedArr.length >= 2)
return true;
else if (
selectedBuildingsDataPointsSamplingRateAbbrevNestedArr.length < 2
) {
throw new Error("A scatter plot requires at least two data points");
}
}
}
};
/**
* Check if the selected aggregation type options are valid for
* drawing either a heatmp or scatter plot. If these options are
* invalid, throw an error
*
* @param {Array} selectedChartTypeArr An array of string(s) representing the selected chart type(s)
* @param {String} selectedAggregationType The selected aggregation type
* @param {String} selectedAggregationDuration The selected aggregation duration
* @returns {Boolean} true, if there are no errors thrown
*/
const checkIfSelectedAggregationOptionsAreValid = function (
selectedChartTypeArr,
selectedAggregationType,
selectedAggregationDuration
) {
for (const selectedChartType of selectedChartTypeArr) {
if (
selectedChartType === "Heatmap" ||
selectedChartType === "Scatter Plot"
) {
// For both chart types, we are interested in raw observations
if (
checkIfChartRequiresRawObservations(
selectedAggregationType,
selectedAggregationDuration
)
)
return true;
// Throw error if we attempt to use aggregated observations
else {
throw new Error(
"The selected chart type does not support aggregated results"
);
}
}
}
};
/**
* Get the abbreviated form of building IDs, phenomenon names and sensor sampling rates
* @param {String} buildingFullForm A string representation of the full form of a building ID
* @param {String} phenomenonFullForm A string representation of the full form of a phenomenon name
* @param {String} samplingRateFullForm A string representation of the full form of a sensor's sampling rate
* @returns {Array} An array of abbreviated strings
*/
const getBuildingSensorSamplingRateAbbreviation = function (
buildingFullForm,
phenomenonFullForm,
samplingRateFullForm
) {
const fullFormToAbbreviationMapping = {
buildings: {
"Bau 101": "101",
"Bau 102": "102",
"Bau 107": "107",
"Bau 112": "112, 118",
"Bau 125": "125",
"Bau 225": "225",
"Other": "weather_station_521",
},
phenomenon: {
VL: "vl",
RL: "rl",
dT: "dT",
Durchfluss: "flow",
Leistung: "power",
Energie: "energy",
Energie_VERBR: "energy_verb",
Außentemp: "outside_temp",
},
samplingRate: {
"15 min": "15min",
"60 min": "60min",
},
};
const buildingAbbrev =
fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm];
const phenomenonAbbrev =
fullFormToAbbreviationMapping["phenomenon"]?.[phenomenonFullForm];
const samplingRateAbbrev =
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];
}
};
/**
* Get the abbreviated form for the currently selected options in ALL the drop down lists
*
* @param {Array} allSelectedOptionsArr An array containing four arrays, where each array contains the values of the selected options
* @returns {Array} An array which contains one or more nested arrays of abbreviations of building(s), data point(s) and sampling rate(s)
*/
const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function (
allSelectedOptionsArr
) {
// Note: The buildings + data points array is the second element and
// the sampling rate array is the fourth element, therefore we skip the first and third elementa
const [
,
selectedBuildingDataPointOptionsSplitArr,
,
selectedSamplingRateArr,
] = allSelectedOptionsArr;
// The building is the first element
const selectedBuildingsArr = selectedBuildingDataPointOptionsSplitArr.map(
(selectedBuildingDataPoint) => selectedBuildingDataPoint[0]
);
// The data point is the second element
const selectedDataPointsArr = selectedBuildingDataPointOptionsSplitArr.map(
(selectedBuildingDataPoint) => selectedBuildingDataPoint[1]
);
// Assume that the buildings and data points arrays have equal length
// use one of the arrays for looping
if (selectedBuildingsArr.length !== selectedDataPointsArr.length) {
throw new Error(
"The buildings array and data points array have different lengths"
);
} else {
return selectedBuildingsArr.map((selectedBuilding, i) =>
getBuildingSensorSamplingRateAbbreviation(
selectedBuilding,
selectedDataPointsArr[i],
...selectedSamplingRateArr
)
);
}
};
export {
splitMultipleOptionsTextDelimitedBySlash,
getSelectedOptionsFromAllDropDownLists,
checkIfSelectedOptionsContainTemperatureDifference,
deleteTemperatureDifferenceOptions,
extractTemperatureDifferenceOptions,
extractBuildingPlusSamplingRate,
checkIfSelectedBuildingDataPointsOptionsAreValid,
checkIfSelectedAggregationOptionsAreValid,
getAbbreviationsForSelectedOptionsFromAllDropDownLists,
};