Commit 04eb3a11 authored by Pithon Kabiro's avatar Pithon Kabiro
Browse files

Merge branch 'wip_select-sensors-dropdown-list-6' into 'master'

Update logic for drop-down list

Tweak logic so that custom error messages are thrown before performing 
async tasks i.e. fetching observations and metadata

See merge request !17
parents 6838b0dd 0c48cb36
Showing with 281 additions and 140 deletions
+281 -140
...@@ -40,7 +40,8 @@ import { ...@@ -40,7 +40,8 @@ import {
deleteTemperatureDifferenceOptions, deleteTemperatureDifferenceOptions,
extractTemperatureDifferenceOptions, extractTemperatureDifferenceOptions,
extractBuildingPlusSamplingRate, extractBuildingPlusSamplingRate,
checkIfChartRequiresRawObservations, checkIfSelectedBuildingDataPointsOptionsAreValid,
checkIfSelectedAggregationOptionsAreValid,
getAbbreviationsForSelectedOptionsFromAllDropDownLists, getAbbreviationsForSelectedOptionsFromAllDropDownLists,
} from "./src_modules/dropDownListHelpers.mjs"; } from "./src_modules/dropDownListHelpers.mjs";
...@@ -137,9 +138,9 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -137,9 +138,9 @@ const drawChartUsingSelectedOptions = async function () {
const selectedOptionsAllDropDownLists = const selectedOptionsAllDropDownLists =
getSelectedOptionsFromAllDropDownLists(); getSelectedOptionsFromAllDropDownLists();
// Note: The aggregation type + duration and chart type are the second and // Note: The aggregation type + duration and chart type are the first and
// fourth elements respectively, we have ignored the first and third elements // third elements respectively, we have ignored the second and fourth elements
const [, selectedAggregationTypeDurationArr, , selectedChartTypeArr] = const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] =
selectedOptionsAllDropDownLists; selectedOptionsAllDropDownLists;
// Create an array of aggregation type and duration // Create an array of aggregation type and duration
...@@ -152,41 +153,56 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -152,41 +153,56 @@ const drawChartUsingSelectedOptions = async function () {
const [selectedAggregationTypeDurationSplitArr] = const [selectedAggregationTypeDurationSplitArr] =
selectedAggregationTypeDurationSplitNestedArr; selectedAggregationTypeDurationSplitNestedArr;
const [selectedAggregationTypeArr, selectedAggregationDuration] = const [selectedAggregationType, selectedAggregationDuration] =
selectedAggregationTypeDurationSplitArr; selectedAggregationTypeDurationSplitArr;
// Array of building(s) + data point(s) + sampling rate // Array of building(s) + data point(s) + sampling rate
const selectedBuildingsDataPointsSamplingRateAbbrev = const selectedBuildingsDataPointsSamplingRateAbbrevNestedArr =
getAbbreviationsForSelectedOptionsFromAllDropDownLists( getAbbreviationsForSelectedOptionsFromAllDropDownLists(
selectedOptionsAllDropDownLists 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 // Create copies of the arrays of building(s) + data point(s) + sampling rate
const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy = [ const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr = [
...selectedBuildingsDataPointsSamplingRateAbbrev, ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
]; ];
const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy = [ const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr = [
...selectedBuildingsDataPointsSamplingRateAbbrev, ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
]; ];
// Check if we have non-computed // Check if we have non-computed
const selectedBuildingsDataPointsSamplingRateAbbrevNonComputed = const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr =
checkIfSelectedOptionsContainTemperatureDifference( checkIfSelectedOptionsContainTemperatureDifference(
selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
) )
? deleteTemperatureDifferenceOptions( ? deleteTemperatureDifferenceOptions(
selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
) )
: selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy; : selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr;
// Check if we have computed / dT // Check if we have computed / dT
const selectedBuildingsDataPointsSamplingRateAbbrevComputed = const selectedBuildingsDataPointsSamplingRateAbbrevComputedArr =
checkIfSelectedOptionsContainTemperatureDifference( checkIfSelectedOptionsContainTemperatureDifference(
selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
) )
? extractTemperatureDifferenceOptions( ? extractTemperatureDifferenceOptions(
selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
) )
: []; : [];
...@@ -194,34 +210,34 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -194,34 +210,34 @@ const drawChartUsingSelectedOptions = async function () {
showLoadingSpinner(); showLoadingSpinner();
// Fetch the observations + metadata / non-computed // Fetch the observations + metadata / non-computed
const observationsPlusMetadataNonComputed = const observationsPlusMetadataNonComputedArr =
selectedBuildingsDataPointsSamplingRateAbbrevNonComputed.length === 0 selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr.length === 0
? [[], []] ? [[], []]
: await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( : await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL, BASE_URL,
QUERY_PARAMS_COMBINED, QUERY_PARAMS_COMBINED,
selectedBuildingsDataPointsSamplingRateAbbrevNonComputed selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr
); );
// Fetch the observations + metadata / computed (dT) // Fetch the observations + metadata / computed (dT)
const observationsPlusMetadataComputed = const observationsPlusMetadataComputedArr =
selectedBuildingsDataPointsSamplingRateAbbrevComputed.length === 0 selectedBuildingsDataPointsSamplingRateAbbrevComputedArr.length === 0
? [[], []] ? [[], []]
: await calculateVorlaufMinusRuecklaufTemperature( : await calculateVorlaufMinusRuecklaufTemperature(
BASE_URL, BASE_URL,
QUERY_PARAMS_COMBINED, QUERY_PARAMS_COMBINED,
extractBuildingPlusSamplingRate( extractBuildingPlusSamplingRate(
selectedBuildingsDataPointsSamplingRateAbbrevComputed selectedBuildingsDataPointsSamplingRateAbbrevComputedArr
) )
); );
// Extract the combined arrays for observations and metadata / non-computed // Extract the combined arrays for observations and metadata / non-computed
const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] = const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] =
observationsPlusMetadataNonComputed; observationsPlusMetadataNonComputedArr;
// Extract the combined arrays for observations and metadata / computed (dT) // Extract the combined arrays for observations and metadata / computed (dT)
const [observationsNestedComputedArr, metadataNestedComputedArr] = const [observationsNestedComputedArr, metadataNestedComputedArr] =
observationsPlusMetadataComputed; observationsPlusMetadataComputedArr;
// Create a combined array of observations and metadata // Create a combined array of observations and metadata
const observationsPlusMetadataCombined = [ const observationsPlusMetadataCombined = [
...@@ -246,7 +262,7 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -246,7 +262,7 @@ const drawChartUsingSelectedOptions = async function () {
// The formatted abbreviations array is nested // The formatted abbreviations array is nested
const [selectedBuildingsDataPointsSamplingRateAbbrevArr] = const [selectedBuildingsDataPointsSamplingRateAbbrevArr] =
selectedBuildingsDataPointsSamplingRateAbbrev; selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;
// Extract the formatted sampling rate string - used by ALL chart types // Extract the formatted sampling rate string - used by ALL chart types
const [, , selectedSamplingRateAbbrev] = const [, , selectedSamplingRateAbbrev] =
...@@ -273,50 +289,38 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -273,50 +289,38 @@ const drawChartUsingSelectedOptions = async function () {
extractUniqueCalendarDatesFromTimestamp(observationsArr) extractUniqueCalendarDatesFromTimestamp(observationsArr)
); );
selectedChartTypeArr.forEach((selectedChartType) => { for (const selectedChartType of selectedChartTypeArr) {
if (selectedChartType === "Heatmap") { if (selectedChartType === "Heatmap") {
// We are interested in raw observations // We are interested in raw observations
if ( if (
checkIfChartRequiresRawObservations( selectedAggregationOptionsAreValid &&
selectedAggregationTypeArr, selectedBuildingDataPointsOptionsAreValid
selectedAggregationDuration
)
) { ) {
drawHeatmapBasedOnSelectedOptions( drawHeatmapBasedOnSelectedOptions(
selectedBuildingsDataPointsSamplingRateAbbrev,
observationsComboNestedArr, observationsComboNestedArr,
extractedFormattedDatastreamProperties extractedFormattedDatastreamProperties
); );
} else {
throw new Error(
"This type of chart (Heatmap) does not support aggregated results"
);
} }
} }
if (selectedChartType === "Scatter Plot") { if (selectedChartType === "Scatter Plot") {
// We are interested in raw observations // We are interested in raw observations
if ( if (
checkIfChartRequiresRawObservations( selectedAggregationOptionsAreValid &&
selectedAggregationTypeArr, selectedBuildingDataPointsOptionsAreValid
selectedAggregationDuration
)
) { ) {
drawScatterPlotFromChartSelection( drawScatterPlotFromChartSelection(
selectedBuildingsDataPointsSamplingRateAbbrev,
observationsComboNestedArr, observationsComboNestedArr,
extractedFormattedDatastreamProperties extractedFormattedDatastreamProperties
); );
} else {
throw new Error(
"This type of chart (Scatter Plot) does not support aggregated results"
);
} }
} }
if (selectedChartType === "Line") { if (selectedChartType === "Line") {
// We are interested in raw observations or aggregated observations // We are interested in raw observations or aggregated observations
// Raw observations // Raw observations
if (selectedAggregationTypeArr === "None (raw data)") { if (selectedAggregationType === "None (raw data)") {
// Create formatted array(s) for observations // Create formatted array(s) for observations
const formattedRawObservationsLineChartNestedArr = const formattedRawObservationsLineChartNestedArr =
observationsComboNestedArr.map((observationsArr) => observationsComboNestedArr.map((observationsArr) =>
...@@ -331,7 +335,7 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -331,7 +335,7 @@ const drawChartUsingSelectedOptions = async function () {
// Aggregated observations // Aggregated observations
else { else {
drawLineChartBasedOnSelectedAggregationOptions( drawLineChartBasedOnSelectedAggregationOptions(
selectedAggregationTypeArr, selectedAggregationType,
selectedAggregationDuration, selectedAggregationDuration,
observationsAggregationNestedArr, observationsAggregationNestedArr,
selectedSamplingRateAbbrev, selectedSamplingRateAbbrev,
...@@ -340,11 +344,12 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -340,11 +344,12 @@ const drawChartUsingSelectedOptions = async function () {
); );
} }
} }
if (selectedChartType === "Column") { if (selectedChartType === "Column") {
// We are interested in raw observations or aggregated observations // We are interested in raw observations or aggregated observations
// Raw observations // Raw observations
if (selectedAggregationTypeArr === "None (raw data)") { if (selectedAggregationType === "None (raw data)") {
// Create formatted array(s) for observations // Create formatted array(s) for observations
const formattedRawObservationsColumnChartNestedArr = const formattedRawObservationsColumnChartNestedArr =
observationsComboNestedArr.map((observationsArr) => observationsComboNestedArr.map((observationsArr) =>
...@@ -359,7 +364,7 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -359,7 +364,7 @@ const drawChartUsingSelectedOptions = async function () {
// Aggregated observations // Aggregated observations
else { else {
drawColumnChartBasedOnSelectedAggregationOptions( drawColumnChartBasedOnSelectedAggregationOptions(
selectedAggregationTypeArr, selectedAggregationType,
selectedAggregationDuration, selectedAggregationDuration,
observationsAggregationNestedArr, observationsAggregationNestedArr,
selectedSamplingRateAbbrev, selectedSamplingRateAbbrev,
...@@ -368,7 +373,7 @@ const drawChartUsingSelectedOptions = async function () { ...@@ -368,7 +373,7 @@ const drawChartUsingSelectedOptions = async function () {
); );
} }
} }
}); }
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} finally { } finally {
......
...@@ -2,10 +2,9 @@ ...@@ -2,10 +2,9 @@
import { import {
chartExportOptions, chartExportOptions,
createCombinedTextDelimitedByComma,
createFullTitleForLineOrColumnChart, createFullTitleForLineOrColumnChart,
createSubtitleForChart,
createTooltipDateString, createTooltipDateString,
extractSamplingRateFromDatastreamName,
} from "./chartHelpers.mjs"; } from "./chartHelpers.mjs";
/** /**
...@@ -122,9 +121,7 @@ const drawColumnChartHighcharts = function ( ...@@ -122,9 +121,7 @@ const drawColumnChartHighcharts = function (
aggregationType aggregationType
); );
const textChartSubtitle = `Sampling rate(s): ${createCombinedTextDelimitedByComma( const textChartSubtitle = createSubtitleForChart(datastreamNamesArr);
extractSamplingRateFromDatastreamName(datastreamNamesArr)
)}`;
Highcharts.chart("chart-column", { Highcharts.chart("chart-column", {
chart: { chart: {
......
"use strict"; "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 * Format the response from SensorThings API to make it suitable for use in a heatmap
...@@ -73,6 +77,10 @@ const drawHeatMapHighcharts = function ( ...@@ -73,6 +77,10 @@ const drawHeatMapHighcharts = function (
const [PHENOMENON_NAME] = phenomenonNamesArr; const [PHENOMENON_NAME] = phenomenonNamesArr;
const [PHENOMENON_SYMBOL] = unitOfMeasurementSymbolsArr; const [PHENOMENON_SYMBOL] = unitOfMeasurementSymbolsArr;
const TEXT_CHART_TITLE = createTitleForHeatmap(phenomenonNamesArr);
const TEXT_CHART_SUBTITLE = createSubtitleForHeatmap(datastreamNamesArr);
const { const {
minObsValue: MINIMUM_VALUE_COLOR_AXIS, minObsValue: MINIMUM_VALUE_COLOR_AXIS,
maxObsValue: MAXIMUM_VALUE_COLOR_AXIS, maxObsValue: MAXIMUM_VALUE_COLOR_AXIS,
...@@ -89,13 +97,13 @@ const drawHeatMapHighcharts = function ( ...@@ -89,13 +97,13 @@ const drawHeatMapHighcharts = function (
}, },
title: { title: {
text: DATASTREAM_DESCRIPTION, text: TEXT_CHART_TITLE,
align: "left", align: "left",
x: 40, x: 40,
}, },
subtitle: { subtitle: {
text: DATASTREAM_NAME, text: TEXT_CHART_SUBTITLE,
align: "left", align: "left",
x: 40, x: 40,
}, },
......
...@@ -224,6 +224,34 @@ const createCombinedTextDelimitedByComma = function (metadataPropertiesArr) { ...@@ -224,6 +224,34 @@ const createCombinedTextDelimitedByComma = function (metadataPropertiesArr) {
return metadataPropertiesArr.join(", "); 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 * 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" * @param {String} aggregationInterval The aggregation interval as a string, either "daily" or "monthly"
...@@ -245,7 +273,7 @@ const createPartialTitleForLineOrColumnChart = function ( ...@@ -245,7 +273,7 @@ const createPartialTitleForLineOrColumnChart = function (
/** /**
* Create a full string for a line chart or column chart title * 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} aggregationInterval The aggregation interval as a string, either "daily" or "monthly"
* @param {String} aggregationType The aggregation type as a string, either "sum" or "average" * @param {String} aggregationType The aggregation type as a string, either "sum" or "average"
* @returns {String} Full string for chart title * @returns {String} Full string for chart title
...@@ -269,6 +297,54 @@ const createFullTitleForLineOrColumnChart = function ( ...@@ -269,6 +297,54 @@ const createFullTitleForLineOrColumnChart = function (
)}: ${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`; )}: ${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 * 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 * @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 ( ...@@ -286,18 +362,6 @@ const createTooltipDateString = function (
return `${Highcharts.dateFormat("%b %Y", pointXAxisValue)}`; 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 * Remove the transparency (alpha channel) from a color
* @param {String} rgbaColor A color expressed in RGBA format * @param {String} rgbaColor A color expressed in RGBA format
...@@ -313,8 +377,10 @@ export { ...@@ -313,8 +377,10 @@ export {
createCombinedTextDelimitedByAmpersand, createCombinedTextDelimitedByAmpersand,
createCombinedTextDelimitedByComma, createCombinedTextDelimitedByComma,
createFullTitleForLineOrColumnChart, createFullTitleForLineOrColumnChart,
createTitleForHeatmap,
createSubtitleForChart,
createSubtitleForHeatmap,
createTooltipDateString, createTooltipDateString,
convertHexColorToRGBColor, convertHexColorToRGBColor,
extractSamplingRateFromDatastreamName,
removeTransparencyFromColor, removeTransparencyFromColor,
}; };
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
import { import {
chartExportOptions, chartExportOptions,
createFullTitleForLineOrColumnChart, createFullTitleForLineOrColumnChart,
createSubtitleForChart,
createTooltipDateString, createTooltipDateString,
extractSamplingRateFromDatastreamName,
} from "./chartHelpers.mjs"; } from "./chartHelpers.mjs";
/** /**
...@@ -114,9 +114,7 @@ const drawLineChartHighcharts = function ( ...@@ -114,9 +114,7 @@ const drawLineChartHighcharts = function (
aggregationType aggregationType
); );
const textChartSubtitle = `Sampling rate(s): ${createCombinedTextForLineChartTitles( const textChartSubtitle = createSubtitleForChart(datastreamNamesArr);
extractSamplingRateFromDatastreamName(datastreamNamesArr)
)}`;
// Create the array of series options object(s) // Create the array of series options object(s)
const seriesOptionsArr = createSeriesOptionsForLineChart( const seriesOptionsArr = createSeriesOptionsForLineChart(
......
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
convertHexColorToRGBColor, convertHexColorToRGBColor,
createCombinedTextDelimitedByAmpersand, createCombinedTextDelimitedByAmpersand,
createCombinedTextDelimitedByComma, createCombinedTextDelimitedByComma,
extractSamplingRateFromDatastreamName, createSubtitleForChart,
removeTransparencyFromColor, removeTransparencyFromColor,
} from "./chartHelpers.mjs"; } from "./chartHelpers.mjs";
...@@ -200,9 +200,7 @@ const drawScatterPlotHighcharts = function ( ...@@ -200,9 +200,7 @@ const drawScatterPlotHighcharts = function (
const CHART_TITLE = const CHART_TITLE =
createCombinedTextForScatterPlotTitles(phenomenonNamesArr); createCombinedTextForScatterPlotTitles(phenomenonNamesArr);
const CHART_SUBTITLE = `Sampling rate(s): ${createCombinedTextDelimitedByComma( const CHART_SUBTITLE = createSubtitleForChart(datastreamNamesArr);
extractSamplingRateFromDatastreamName(datastreamNamesArr)
)}`;
const X_AXIS_TITLE = createXAxisTitleTextScatterPlot( const X_AXIS_TITLE = createXAxisTitleTextScatterPlot(
phenomenonNamesArr, phenomenonNamesArr,
......
...@@ -109,19 +109,23 @@ const getSelectedOptionsFromAllDropDownLists = function () { ...@@ -109,19 +109,23 @@ const getSelectedOptionsFromAllDropDownLists = function () {
); );
// Ensure that all the options have at least one selection // Ensure that all the options have at least one selection
if ( if (selectedChartTypeArr.length === 0)
selectedBuildingDataPointOptionsSplitArr.length === 0 || throw new Error("Please ensure that the chart type is selected");
selectedAggregationOptionsArr.length === 0 ||
selectedSamplingRateArr.length === 0 || if (selectedBuildingDataPointOptionsSplitArr.length === 0)
selectedChartTypeArr.length === 0 throw new Error("Please ensure that at least one data point is selected");
)
return; 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 [ return [
selectedChartTypeArr,
selectedBuildingDataPointOptionsSplitArr, selectedBuildingDataPointOptionsSplitArr,
selectedAggregationOptionsArr, selectedAggregationOptionsArr,
selectedSamplingRateArr, selectedSamplingRateArr,
selectedChartTypeArr,
]; ];
}; };
...@@ -296,6 +300,82 @@ const checkIfChartRequiresRawObservations = function ( ...@@ -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 * 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} buildingFullForm A string representation of the full form of a building ID
...@@ -380,9 +460,14 @@ const getBuildingSensorSamplingRateAbbreviation = function ( ...@@ -380,9 +460,14 @@ const getBuildingSensorSamplingRateAbbreviation = function (
const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function ( const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function (
allSelectedOptionsArr allSelectedOptionsArr
) { ) {
// Note: The sampling rate array is the third array, therefore we skip one element // Note: The buildings + data points array is the second element and
const [selectedBuildingDataPointOptionsSplitArr, , selectedSamplingRateArr] = // the sampling rate array is the fourth element, therefore we skip the first and third elementa
allSelectedOptionsArr; const [
,
selectedBuildingDataPointOptionsSplitArr,
,
selectedSamplingRateArr,
] = allSelectedOptionsArr;
// The building is the first element // The building is the first element
const selectedBuildingsArr = selectedBuildingDataPointOptionsSplitArr.map( const selectedBuildingsArr = selectedBuildingDataPointOptionsSplitArr.map(
...@@ -417,6 +502,7 @@ export { ...@@ -417,6 +502,7 @@ export {
deleteTemperatureDifferenceOptions, deleteTemperatureDifferenceOptions,
extractTemperatureDifferenceOptions, extractTemperatureDifferenceOptions,
extractBuildingPlusSamplingRate, extractBuildingPlusSamplingRate,
checkIfChartRequiresRawObservations, checkIfSelectedBuildingDataPointsOptionsAreValid,
checkIfSelectedAggregationOptionsAreValid,
getAbbreviationsForSelectedOptionsFromAllDropDownLists, getAbbreviationsForSelectedOptionsFromAllDropDownLists,
}; };
...@@ -29,75 +29,58 @@ import { extractPropertiesFromFormattedDatastreamMetadata } from "./fetchedDataP ...@@ -29,75 +29,58 @@ import { extractPropertiesFromFormattedDatastreamMetadata } from "./fetchedDataP
/** /**
* Draw a heatmap based on the selected options from a drop-down list * 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 {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 * @param {Object} extractedFormattedDatastreamProperties An object that contains array(s) of formatted Datastream properties
* @returns {undefined} undefined * @returns {undefined} undefined
*/ */
const drawHeatmapBasedOnSelectedOptions = function ( const drawHeatmapBasedOnSelectedOptions = function (
selectedBuildingsDataPointsSamplingRateAbbrevArr,
observationsComboNestedArr, observationsComboNestedArr,
extractedFormattedDatastreamProperties extractedFormattedDatastreamProperties
) { ) {
if (selectedBuildingsDataPointsSamplingRateAbbrevArr.length === 1) { // Create formatted array(s) for observations
// Create formatted array(s) for observations const formattedObservationsHeatMapNestedArr = observationsComboNestedArr.map(
const formattedObservationsHeatMapNestedArr = (observationsArr) =>
observationsComboNestedArr.map((observationsArr) => formatSensorThingsApiResponseForHeatMap(observationsArr)
formatSensorThingsApiResponseForHeatMap(observationsArr) );
);
// Note: The resulting array is nested and is not suitable for heatmap,
// Note: The resulting array is nested and is not suitable for heatmap, // extract the nested array
// extract the nested array const [formattedObservationsHeatMapArr] =
const [formattedObservationsHeatMapArr] = formattedObservationsHeatMapNestedArr;
formattedObservationsHeatMapNestedArr;
drawHeatMapHighcharts(
drawHeatMapHighcharts( formattedObservationsHeatMapArr,
formattedObservationsHeatMapArr, extractedFormattedDatastreamProperties
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"
);
}
}; };
/** /**
* Draw a scatter plot based on the selected options from a drop-down list * 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 {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 * @param {Object} extractedFormattedDatastreamProperties An object that contains array(s) of formatted Datastream properties
* @returns {undefined} undefined * @returns {undefined} undefined
*/ */
const drawScatterPlotFromChartSelection = function ( const drawScatterPlotFromChartSelection = function (
selectedBuildingsDataPointsSamplingRateAbbrevArr,
observationsComboNestedArr, observationsComboNestedArr,
extractedFormattedDatastreamProperties extractedFormattedDatastreamProperties
) { ) {
// Check the length of buildings + data points + sampling rate array // Extract values for x-axis and y-axis
if (selectedBuildingsDataPointsSamplingRateAbbrevArr.length >= 2) { // x-axis values are first element of nested observations array
// Extract values for x-axis and y-axis const [obsXAxisArr] = observationsComboNestedArr.slice(0, 1);
// x-axis values are first element of nested observations array // y-axis values are rest of elements of nested observations array
const [obsXAxisArr] = observationsComboNestedArr.slice(0, 1); const obsYAxisNestedArr = observationsComboNestedArr.slice(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(
// Create formatted array(s) for observations (obsYAxisArr) =>
const formattedObservationsScatterPlotArr = obsYAxisNestedArr.map( formatSensorThingsApiResponseForScatterPlot(obsXAxisArr, obsYAxisArr)
(obsYAxisArr) => );
formatSensorThingsApiResponseForScatterPlot(obsXAxisArr, obsYAxisArr)
); drawScatterPlotHighcharts(
formattedObservationsScatterPlotArr,
drawScatterPlotHighcharts( extractedFormattedDatastreamProperties
formattedObservationsScatterPlotArr, );
extractedFormattedDatastreamProperties
);
} else {
throw new Error("A scatter plot chart requires at least two data points");
}
}; };
/** /**
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment