From 0c48cb3632a727e4151cbd5cb5d4724edb1bc0e2 Mon Sep 17 00:00:00 2001 From: Pithon Kabiro Date: Mon, 11 Oct 2021 16:16:22 +0200 Subject: [PATCH] Check selected options validity before fetch data Confirm that the selected drop-dowwn options are valid before fetching the observations --- public/js/appChart.js | 105 +++++++++-------- public/js/src_modules/chartColumn.mjs | 7 +- public/js/src_modules/chartHeatmap.mjs | 14 ++- public/js/src_modules/chartHelpers.mjs | 94 ++++++++++++--- public/js/src_modules/chartLine.mjs | 6 +- public/js/src_modules/chartScatterPlot.mjs | 6 +- public/js/src_modules/dropDownListHelpers.mjs | 110 ++++++++++++++++-- .../js/src_modules/dropDownListProcessing.mjs | 79 +++++-------- 8 files changed, 281 insertions(+), 140 deletions(-) diff --git a/public/js/appChart.js b/public/js/appChart.js index 638884c..f78f42e 100644 --- a/public/js/appChart.js +++ b/public/js/appChart.js @@ -40,7 +40,8 @@ import { deleteTemperatureDifferenceOptions, extractTemperatureDifferenceOptions, extractBuildingPlusSamplingRate, - checkIfChartRequiresRawObservations, + checkIfSelectedBuildingDataPointsOptionsAreValid, + checkIfSelectedAggregationOptionsAreValid, getAbbreviationsForSelectedOptionsFromAllDropDownLists, } from "./src_modules/dropDownListHelpers.mjs"; @@ -137,9 +138,9 @@ const drawChartUsingSelectedOptions = async function () { const selectedOptionsAllDropDownLists = getSelectedOptionsFromAllDropDownLists(); - // Note: The aggregation type + duration and chart type are the second and - // fourth elements respectively, we have ignored the first and third elements - const [, selectedAggregationTypeDurationArr, , selectedChartTypeArr] = + // Note: The aggregation type + duration and chart type are the first and + // third elements respectively, we have ignored the second and fourth elements + const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] = selectedOptionsAllDropDownLists; // Create an array of aggregation type and duration @@ -152,41 +153,56 @@ const drawChartUsingSelectedOptions = async function () { const [selectedAggregationTypeDurationSplitArr] = selectedAggregationTypeDurationSplitNestedArr; - const [selectedAggregationTypeArr, selectedAggregationDuration] = + const [selectedAggregationType, selectedAggregationDuration] = selectedAggregationTypeDurationSplitArr; // Array of building(s) + data point(s) + sampling rate - const selectedBuildingsDataPointsSamplingRateAbbrev = + const selectedBuildingsDataPointsSamplingRateAbbrevNestedArr = getAbbreviationsForSelectedOptionsFromAllDropDownLists( selectedOptionsAllDropDownLists ); + // The values of these references is either equal to `true` or an error will be thrown + // We would like the errors to be thrown at this point before we begin performing any asynchronous tasks + const selectedAggregationOptionsAreValid = + checkIfSelectedAggregationOptionsAreValid( + selectedChartTypeArr, + selectedAggregationType, + selectedAggregationDuration + ); + + const selectedBuildingDataPointsOptionsAreValid = + checkIfSelectedBuildingDataPointsOptionsAreValid( + selectedChartTypeArr, + selectedBuildingsDataPointsSamplingRateAbbrevNestedArr + ); + // Create copies of the arrays of building(s) + data point(s) + sampling rate - const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy = [ - ...selectedBuildingsDataPointsSamplingRateAbbrev, + const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr = [ + ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr, ]; - const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy = [ - ...selectedBuildingsDataPointsSamplingRateAbbrev, + const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr = [ + ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr, ]; // Check if we have non-computed - const selectedBuildingsDataPointsSamplingRateAbbrevNonComputed = + const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr = checkIfSelectedOptionsContainTemperatureDifference( - selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy + selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr ) ? deleteTemperatureDifferenceOptions( - selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy + selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr ) - : selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy; + : selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr; // Check if we have computed / dT - const selectedBuildingsDataPointsSamplingRateAbbrevComputed = + const selectedBuildingsDataPointsSamplingRateAbbrevComputedArr = checkIfSelectedOptionsContainTemperatureDifference( - selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy + selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr ) ? extractTemperatureDifferenceOptions( - selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy + selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr ) : []; @@ -194,34 +210,34 @@ const drawChartUsingSelectedOptions = async function () { showLoadingSpinner(); // Fetch the observations + metadata / non-computed - const observationsPlusMetadataNonComputed = - selectedBuildingsDataPointsSamplingRateAbbrevNonComputed.length === 0 + const observationsPlusMetadataNonComputedArr = + selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr.length === 0 ? [[], []] : await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( BASE_URL, QUERY_PARAMS_COMBINED, - selectedBuildingsDataPointsSamplingRateAbbrevNonComputed + selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr ); // Fetch the observations + metadata / computed (dT) - const observationsPlusMetadataComputed = - selectedBuildingsDataPointsSamplingRateAbbrevComputed.length === 0 + const observationsPlusMetadataComputedArr = + selectedBuildingsDataPointsSamplingRateAbbrevComputedArr.length === 0 ? [[], []] : await calculateVorlaufMinusRuecklaufTemperature( BASE_URL, QUERY_PARAMS_COMBINED, extractBuildingPlusSamplingRate( - selectedBuildingsDataPointsSamplingRateAbbrevComputed + selectedBuildingsDataPointsSamplingRateAbbrevComputedArr ) ); // Extract the combined arrays for observations and metadata / non-computed const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] = - observationsPlusMetadataNonComputed; + observationsPlusMetadataNonComputedArr; // Extract the combined arrays for observations and metadata / computed (dT) const [observationsNestedComputedArr, metadataNestedComputedArr] = - observationsPlusMetadataComputed; + observationsPlusMetadataComputedArr; // Create a combined array of observations and metadata const observationsPlusMetadataCombined = [ @@ -246,7 +262,7 @@ const drawChartUsingSelectedOptions = async function () { // The formatted abbreviations array is nested const [selectedBuildingsDataPointsSamplingRateAbbrevArr] = - selectedBuildingsDataPointsSamplingRateAbbrev; + selectedBuildingsDataPointsSamplingRateAbbrevNestedArr; // Extract the formatted sampling rate string - used by ALL chart types const [, , selectedSamplingRateAbbrev] = @@ -273,50 +289,38 @@ const drawChartUsingSelectedOptions = async function () { extractUniqueCalendarDatesFromTimestamp(observationsArr) ); - selectedChartTypeArr.forEach((selectedChartType) => { + for (const selectedChartType of selectedChartTypeArr) { if (selectedChartType === "Heatmap") { // We are interested in raw observations if ( - checkIfChartRequiresRawObservations( - selectedAggregationTypeArr, - selectedAggregationDuration - ) + selectedAggregationOptionsAreValid && + selectedBuildingDataPointsOptionsAreValid ) { drawHeatmapBasedOnSelectedOptions( - selectedBuildingsDataPointsSamplingRateAbbrev, observationsComboNestedArr, extractedFormattedDatastreamProperties ); - } else { - throw new Error( - "This type of chart (Heatmap) does not support aggregated results" - ); } } + if (selectedChartType === "Scatter Plot") { // We are interested in raw observations if ( - checkIfChartRequiresRawObservations( - selectedAggregationTypeArr, - selectedAggregationDuration - ) + selectedAggregationOptionsAreValid && + selectedBuildingDataPointsOptionsAreValid ) { drawScatterPlotFromChartSelection( - selectedBuildingsDataPointsSamplingRateAbbrev, observationsComboNestedArr, extractedFormattedDatastreamProperties ); - } else { - throw new Error( - "This type of chart (Scatter Plot) does not support aggregated results" - ); } } + if (selectedChartType === "Line") { // We are interested in raw observations or aggregated observations // Raw observations - if (selectedAggregationTypeArr === "None (raw data)") { + if (selectedAggregationType === "None (raw data)") { // Create formatted array(s) for observations const formattedRawObservationsLineChartNestedArr = observationsComboNestedArr.map((observationsArr) => @@ -331,7 +335,7 @@ const drawChartUsingSelectedOptions = async function () { // Aggregated observations else { drawLineChartBasedOnSelectedAggregationOptions( - selectedAggregationTypeArr, + selectedAggregationType, selectedAggregationDuration, observationsAggregationNestedArr, selectedSamplingRateAbbrev, @@ -340,11 +344,12 @@ const drawChartUsingSelectedOptions = async function () { ); } } + if (selectedChartType === "Column") { // We are interested in raw observations or aggregated observations // Raw observations - if (selectedAggregationTypeArr === "None (raw data)") { + if (selectedAggregationType === "None (raw data)") { // Create formatted array(s) for observations const formattedRawObservationsColumnChartNestedArr = observationsComboNestedArr.map((observationsArr) => @@ -359,7 +364,7 @@ const drawChartUsingSelectedOptions = async function () { // Aggregated observations else { drawColumnChartBasedOnSelectedAggregationOptions( - selectedAggregationTypeArr, + selectedAggregationType, selectedAggregationDuration, observationsAggregationNestedArr, selectedSamplingRateAbbrev, @@ -368,7 +373,7 @@ const drawChartUsingSelectedOptions = async function () { ); } } - }); + } } catch (err) { console.error(err); } finally { diff --git a/public/js/src_modules/chartColumn.mjs b/public/js/src_modules/chartColumn.mjs index c54566d..8c6de14 100644 --- a/public/js/src_modules/chartColumn.mjs +++ b/public/js/src_modules/chartColumn.mjs @@ -2,10 +2,9 @@ import { chartExportOptions, - createCombinedTextDelimitedByComma, createFullTitleForLineOrColumnChart, + createSubtitleForChart, createTooltipDateString, - extractSamplingRateFromDatastreamName, } from "./chartHelpers.mjs"; /** @@ -122,9 +121,7 @@ const drawColumnChartHighcharts = function ( aggregationType ); - const textChartSubtitle = `Sampling rate(s): ${createCombinedTextDelimitedByComma( - extractSamplingRateFromDatastreamName(datastreamNamesArr) - )}`; + const textChartSubtitle = createSubtitleForChart(datastreamNamesArr); Highcharts.chart("chart-column", { chart: { diff --git a/public/js/src_modules/chartHeatmap.mjs b/public/js/src_modules/chartHeatmap.mjs index a48c5bd..69d77f5 100644 --- a/public/js/src_modules/chartHeatmap.mjs +++ b/public/js/src_modules/chartHeatmap.mjs @@ -1,6 +1,10 @@ "use strict"; -import { chartExportOptions } from "./chartHelpers.mjs"; +import { + chartExportOptions, + createTitleForHeatmap, + createSubtitleForHeatmap, +} from "./chartHelpers.mjs"; /** * Format the response from SensorThings API to make it suitable for use in a heatmap @@ -73,6 +77,10 @@ const drawHeatMapHighcharts = function ( const [PHENOMENON_NAME] = phenomenonNamesArr; const [PHENOMENON_SYMBOL] = unitOfMeasurementSymbolsArr; + const TEXT_CHART_TITLE = createTitleForHeatmap(phenomenonNamesArr); + + const TEXT_CHART_SUBTITLE = createSubtitleForHeatmap(datastreamNamesArr); + const { minObsValue: MINIMUM_VALUE_COLOR_AXIS, maxObsValue: MAXIMUM_VALUE_COLOR_AXIS, @@ -89,13 +97,13 @@ const drawHeatMapHighcharts = function ( }, title: { - text: DATASTREAM_DESCRIPTION, + text: TEXT_CHART_TITLE, align: "left", x: 40, }, subtitle: { - text: DATASTREAM_NAME, + text: TEXT_CHART_SUBTITLE, align: "left", x: 40, }, diff --git a/public/js/src_modules/chartHelpers.mjs b/public/js/src_modules/chartHelpers.mjs index fb471f4..b079ae3 100644 --- a/public/js/src_modules/chartHelpers.mjs +++ b/public/js/src_modules/chartHelpers.mjs @@ -224,6 +224,34 @@ const createCombinedTextDelimitedByComma = function (metadataPropertiesArr) { return metadataPropertiesArr.join(", "); }; +/** + * Extracts the sampling rate substring from a datastream name string + * @param {Array} datastreamNamesArr An array of datastream name(s) + * @returns {Array} An array containing the sampling rate substring(s) + */ +const extractSamplingRateFromDatastreamName = function (datastreamNamesArr) { + // First split the Datastream name string based on a single space (" "). + // The sampling rate string is the last word in the resulting string. + // We then split the resulting string using the ':' character. + // Our interest is also in the last word in the resulting string + return datastreamNamesArr.map((datastreamName) => + datastreamName.split(" ").pop().split(":").pop() + ); +}; + +/** + * Extract the building ID substring from a datastream name string + * + * @param {Array} datastreamNamesArr An array of datastream name(s) + * @returns {Array} An array containing the building ID substring(s) + */ +const extractBuildingIdFromDatastreamName = function (datastreamNamesArr) { + // The building ID string is the first word in the Datastream name string + return datastreamNamesArr.map((datastreamName) => + datastreamName.split(" ").shift() + ); +}; + /** * Create a partial string for a line chart or column chart title * @param {String} aggregationInterval The aggregation interval as a string, either "daily" or "monthly" @@ -245,7 +273,7 @@ const createPartialTitleForLineOrColumnChart = function ( /** * Create a full string for a line chart or column chart title - * @param {Array} datastreamNamesArr An array of datastream names as strings + * @param {Array} phenomenonNamesArr An array of phenomenon names as strings * @param {String} aggregationInterval The aggregation interval as a string, either "daily" or "monthly" * @param {String} aggregationType The aggregation type as a string, either "sum" or "average" * @returns {String} Full string for chart title @@ -269,6 +297,54 @@ const createFullTitleForLineOrColumnChart = function ( )}: ${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; }; +/** + * Create a title for a heatmap + * + * @param {Array} phenomenonNamesArr An array of phenomenon names as strings + * @returns {String} A string that represents the heatmap title + */ +const createTitleForHeatmap = function (phenomenonNamesArr) { + return createCombinedTextDelimitedByComma(phenomenonNamesArr); +}; + +/** + * Create a subtitle for the following charts: column chart, line chart and scatter plot + * + * @param {Array} datastreamNamesArr An array of datastream name(s) + * @returns {String} A subtitle string + */ +const createSubtitleForChart = function (datastreamNamesArr) { + // Case 1: We only have one sampling rate string + if (datastreamNamesArr.length === 1) { + return `Sampling rate: ${createCombinedTextDelimitedByComma( + extractSamplingRateFromDatastreamName(datastreamNamesArr) + )}`; + } + // Case 2: We have more than one sampling rate string + else if (datastreamNamesArr.length > 1) { + return `Sampling rate(s): ${createCombinedTextDelimitedByComma( + extractSamplingRateFromDatastreamName(datastreamNamesArr) + )} respectively`; + } +}; + +/** + * Create a subtitle for a heatmap which is different from the subtitles used for the other + * types of charts, i.e. column charts, line charts and scatter plots + * + * @param {Array} datastreamNamesArr An array of datastream name(s) + * @returns {String} A subtitle string + */ +const createSubtitleForHeatmap = function (datastreamNamesArr) { + // Note: the `datastreamNamesArr` here contains only one element + // We use the `createCombinedTextDelimitedByComma` function to "spread" the resulting arrays + return `Building, Sampling rate: ${createCombinedTextDelimitedByComma( + extractBuildingIdFromDatastreamName(datastreamNamesArr) + )}, ${createCombinedTextDelimitedByComma( + extractSamplingRateFromDatastreamName(datastreamNamesArr) + )}`; +}; + /** * Creates a date string that is used in a shared tooltip for a line or column chart * @param {Number} pointXAxisValue The x-axis value (Unix timestamp) which is common for a set of data points @@ -286,18 +362,6 @@ const createTooltipDateString = function ( return `${Highcharts.dateFormat("%b %Y", pointXAxisValue)}`; }; -/** - * Extracts the sampling rate substring from a datastream name string - * @param {Array} datastreamNamesArr An array of datastream name(s) - * @returns {Array} An array containing the sampling rate substring(s) - */ -const extractSamplingRateFromDatastreamName = function (datastreamNamesArr) { - // The sampling rate string is the last word in the Datastream name string - return datastreamNamesArr.map((datastreamName) => - datastreamName.split(" ").pop() - ); -}; - /** * Remove the transparency (alpha channel) from a color * @param {String} rgbaColor A color expressed in RGBA format @@ -313,8 +377,10 @@ export { createCombinedTextDelimitedByAmpersand, createCombinedTextDelimitedByComma, createFullTitleForLineOrColumnChart, + createTitleForHeatmap, + createSubtitleForChart, + createSubtitleForHeatmap, createTooltipDateString, convertHexColorToRGBColor, - extractSamplingRateFromDatastreamName, removeTransparencyFromColor, }; diff --git a/public/js/src_modules/chartLine.mjs b/public/js/src_modules/chartLine.mjs index 26af913..ba9a2e1 100644 --- a/public/js/src_modules/chartLine.mjs +++ b/public/js/src_modules/chartLine.mjs @@ -3,8 +3,8 @@ import { chartExportOptions, createFullTitleForLineOrColumnChart, + createSubtitleForChart, createTooltipDateString, - extractSamplingRateFromDatastreamName, } from "./chartHelpers.mjs"; /** @@ -114,9 +114,7 @@ const drawLineChartHighcharts = function ( aggregationType ); - const textChartSubtitle = `Sampling rate(s): ${createCombinedTextForLineChartTitles( - extractSamplingRateFromDatastreamName(datastreamNamesArr) - )}`; + const textChartSubtitle = createSubtitleForChart(datastreamNamesArr); // Create the array of series options object(s) const seriesOptionsArr = createSeriesOptionsForLineChart( diff --git a/public/js/src_modules/chartScatterPlot.mjs b/public/js/src_modules/chartScatterPlot.mjs index e1bd214..0cc2bdb 100644 --- a/public/js/src_modules/chartScatterPlot.mjs +++ b/public/js/src_modules/chartScatterPlot.mjs @@ -6,7 +6,7 @@ import { convertHexColorToRGBColor, createCombinedTextDelimitedByAmpersand, createCombinedTextDelimitedByComma, - extractSamplingRateFromDatastreamName, + createSubtitleForChart, removeTransparencyFromColor, } from "./chartHelpers.mjs"; @@ -200,9 +200,7 @@ const drawScatterPlotHighcharts = function ( const CHART_TITLE = createCombinedTextForScatterPlotTitles(phenomenonNamesArr); - const CHART_SUBTITLE = `Sampling rate(s): ${createCombinedTextDelimitedByComma( - extractSamplingRateFromDatastreamName(datastreamNamesArr) - )}`; + const CHART_SUBTITLE = createSubtitleForChart(datastreamNamesArr); const X_AXIS_TITLE = createXAxisTitleTextScatterPlot( phenomenonNamesArr, diff --git a/public/js/src_modules/dropDownListHelpers.mjs b/public/js/src_modules/dropDownListHelpers.mjs index d70d714..f087cdd 100644 --- a/public/js/src_modules/dropDownListHelpers.mjs +++ b/public/js/src_modules/dropDownListHelpers.mjs @@ -109,19 +109,23 @@ const getSelectedOptionsFromAllDropDownLists = function () { ); // Ensure that all the options have at least one selection - if ( - selectedBuildingDataPointOptionsSplitArr.length === 0 || - selectedAggregationOptionsArr.length === 0 || - selectedSamplingRateArr.length === 0 || - selectedChartTypeArr.length === 0 - ) - return; + if (selectedChartTypeArr.length === 0) + throw new Error("Please ensure that the chart type is selected"); + + if (selectedBuildingDataPointOptionsSplitArr.length === 0) + throw new Error("Please ensure that at least one data point is selected"); + + if (selectedSamplingRateArr.length === 0) + throw new Error("Please ensure that the aggregation type is selected"); + + if (selectedSamplingRateArr.length === 0) + throw new Error("Please ensure that the sampling rate is selected"); return [ + selectedChartTypeArr, selectedBuildingDataPointOptionsSplitArr, selectedAggregationOptionsArr, selectedSamplingRateArr, - selectedChartTypeArr, ]; }; @@ -296,6 +300,82 @@ const checkIfChartRequiresRawObservations = function ( } }; +/** + * 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 + 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 @@ -380,9 +460,14 @@ const getBuildingSensorSamplingRateAbbreviation = function ( const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function ( allSelectedOptionsArr ) { - // Note: The sampling rate array is the third array, therefore we skip one element - const [selectedBuildingDataPointOptionsSplitArr, , selectedSamplingRateArr] = - 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( @@ -417,6 +502,7 @@ export { deleteTemperatureDifferenceOptions, extractTemperatureDifferenceOptions, extractBuildingPlusSamplingRate, - checkIfChartRequiresRawObservations, + checkIfSelectedBuildingDataPointsOptionsAreValid, + checkIfSelectedAggregationOptionsAreValid, getAbbreviationsForSelectedOptionsFromAllDropDownLists, }; diff --git a/public/js/src_modules/dropDownListProcessing.mjs b/public/js/src_modules/dropDownListProcessing.mjs index 28d0ead..47715ca 100644 --- a/public/js/src_modules/dropDownListProcessing.mjs +++ b/public/js/src_modules/dropDownListProcessing.mjs @@ -29,75 +29,58 @@ import { extractPropertiesFromFormattedDatastreamMetadata } from "./fetchedDataP /** * Draw a heatmap based on the selected options from a drop-down list * - * @param {Array} selectedBuildingsDataPointsSamplingRateAbbrevArr An array which contains one or more nested arrays of abbreviations of building(s), data point(s) and sampling rate(s) * @param {Array} observationsComboNestedArr An array that contains non-computed (raw) observations and computed (temperature difference, dT) observations * @param {Object} extractedFormattedDatastreamProperties An object that contains array(s) of formatted Datastream properties * @returns {undefined} undefined */ const drawHeatmapBasedOnSelectedOptions = function ( - selectedBuildingsDataPointsSamplingRateAbbrevArr, observationsComboNestedArr, extractedFormattedDatastreamProperties ) { - if (selectedBuildingsDataPointsSamplingRateAbbrevArr.length === 1) { - // Create formatted array(s) for observations - const formattedObservationsHeatMapNestedArr = - observationsComboNestedArr.map((observationsArr) => - formatSensorThingsApiResponseForHeatMap(observationsArr) - ); - - // Note: The resulting array is nested and is not suitable for heatmap, - // extract the nested array - const [formattedObservationsHeatMapArr] = - formattedObservationsHeatMapNestedArr; - - drawHeatMapHighcharts( - formattedObservationsHeatMapArr, - extractedFormattedDatastreamProperties - ); - } else if (selectedBuildingsDataPointsSamplingRateAbbrevArr.length < 1) { - throw new Error("Please select at least one data point"); - } else { - throw new Error( - "This type of chart (Heatmap) can only display one data point at a time" - ); - } + // Create formatted array(s) for observations + const formattedObservationsHeatMapNestedArr = observationsComboNestedArr.map( + (observationsArr) => + formatSensorThingsApiResponseForHeatMap(observationsArr) + ); + + // Note: The resulting array is nested and is not suitable for heatmap, + // extract the nested array + const [formattedObservationsHeatMapArr] = + formattedObservationsHeatMapNestedArr; + + drawHeatMapHighcharts( + formattedObservationsHeatMapArr, + extractedFormattedDatastreamProperties + ); }; /** * Draw a scatter plot based on the selected options from a drop-down list * - * @param {Array} selectedBuildingsDataPointsSamplingRateAbbrevArr An array which contains one or more nested arrays of abbreviations of building(s), data point(s) and sampling rate(s) * @param {Array} observationsComboNestedArr An array that contains non-computed (raw) observations and computed (temperature difference, dT) observations * @param {Object} extractedFormattedDatastreamProperties An object that contains array(s) of formatted Datastream properties * @returns {undefined} undefined */ const drawScatterPlotFromChartSelection = function ( - selectedBuildingsDataPointsSamplingRateAbbrevArr, observationsComboNestedArr, extractedFormattedDatastreamProperties ) { - // Check the length of buildings + data points + sampling rate array - if (selectedBuildingsDataPointsSamplingRateAbbrevArr.length >= 2) { - // Extract values for x-axis and y-axis - // x-axis values are first element of nested observations array - const [obsXAxisArr] = observationsComboNestedArr.slice(0, 1); - // y-axis values are rest of elements of nested observations array - const obsYAxisNestedArr = observationsComboNestedArr.slice(1); - - // Create formatted array(s) for observations - const formattedObservationsScatterPlotArr = obsYAxisNestedArr.map( - (obsYAxisArr) => - formatSensorThingsApiResponseForScatterPlot(obsXAxisArr, obsYAxisArr) - ); - - drawScatterPlotHighcharts( - formattedObservationsScatterPlotArr, - extractedFormattedDatastreamProperties - ); - } else { - throw new Error("A scatter plot chart requires at least two data points"); - } + // Extract values for x-axis and y-axis + // x-axis values are first element of nested observations array + const [obsXAxisArr] = observationsComboNestedArr.slice(0, 1); + // y-axis values are rest of elements of nested observations array + const obsYAxisNestedArr = observationsComboNestedArr.slice(1); + + // Create formatted array(s) for observations + const formattedObservationsScatterPlotArr = obsYAxisNestedArr.map( + (obsYAxisArr) => + formatSensorThingsApiResponseForScatterPlot(obsXAxisArr, obsYAxisArr) + ); + + drawScatterPlotHighcharts( + formattedObservationsScatterPlotArr, + extractedFormattedDatastreamProperties + ); }; /** -- GitLab