Commit 4c983cf6 authored by Pithon Kabiro's avatar Pithon Kabiro
Browse files

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

Update logic for drop-down list

Improve processing logic for drop-down list (draw the appropriate 
chart based on selected options)

See merge request !16
parents 59f58680 35dc06f4
...@@ -69,7 +69,6 @@ ...@@ -69,7 +69,6 @@
Custom JS --> Custom JS -->
<script defer type="module" src="js/appCesium.js"></script> <script defer type="module" src="js/appCesium.js"></script>
<script defer type="module" src="js/appChart.js"></script> <script defer type="module" src="js/appChart.js"></script>
<script defer type="module" src="js/dropDownList.js"></script>
</head> </head>
<body class="sb-nav-fixed"> <body class="sb-nav-fixed">
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark"> <nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
...@@ -131,6 +130,21 @@ ...@@ -131,6 +130,21 @@
<label for="droneMode">Drone View</label> <label for="droneMode">Drone View</label>
</div> </div>
<br /> --> <br /> -->
<div id="drop-down--chart-type-parent">
<span><strong>Chart type</strong></span>
<div class="nowrap">
<!-- We need the `multiple` attribute for the dropdowns even if
we do not need to support multiple selections. This seems to
be a quirk of the `vanillaSelectBox` library -->
<select id="drop-down--chart-type" multiple>
<option>Column</option>
<option>Line</option>
<option>Heatmap</option>
<option>Scatter Plot</option>
</select>
</div>
</div>
<br />
<div id="drop-down--bldg-data-point-parent"> <div id="drop-down--bldg-data-point-parent">
<span><strong>Building(s), Data Point(s)</strong></span> <span><strong>Building(s), Data Point(s)</strong></span>
<div class="nowrap"> <div class="nowrap">
...@@ -140,6 +154,9 @@ ...@@ -140,6 +154,9 @@
size="5" size="5"
> >
<!-- Note: The values of the option elements have to be unique --> <!-- Note: The values of the option elements have to be unique -->
<optgroup label="Other">
<option>Außentemp</option>
</optgroup>
<optgroup label="Bau 101"> <optgroup label="Bau 101">
<option>101/VL</option> <option>101/VL</option>
<option>101/RL</option> <option>101/RL</option>
...@@ -174,9 +191,6 @@ ...@@ -174,9 +191,6 @@
<option>225/Energie</option> <option>225/Energie</option>
<option>225/Energie_VERBR</option> <option>225/Energie_VERBR</option>
</optgroup> </optgroup>
<optgroup label="Other">
<option>Außentemp</option>
</optgroup>
</select> </select>
</div> </div>
</div> </div>
...@@ -216,21 +230,6 @@ ...@@ -216,21 +230,6 @@
</div> </div>
</div> </div>
<br /> <br />
<div id="drop-down--chart-type-parent">
<span><strong>Chart type</strong></span>
<div class="nowrap">
<!-- We need the `multiple` attribute for the dropdowns even if
we do not need to support multiple selections. This seems to
be a quirk of the `vanillaSelectBox` library -->
<select id="drop-down--chart-type" multiple>
<option>Column</option>
<option>Line</option>
<option>Heatmap</option>
<option>Scatter Plot</option>
</select>
</div>
</div>
<br />
<button id="btn-draw-chart">Draw Chart</button> <button id="btn-draw-chart">Draw Chart</button>
<!-- <span><strong>Display Options</strong></span> <!-- <span><strong>Display Options</strong></span>
<div class="nowrap"> <div class="nowrap">
......
...@@ -5,765 +5,380 @@ import { ...@@ -5,765 +5,380 @@ import {
QUERY_PARAMS_COMBINED, QUERY_PARAMS_COMBINED,
} from "./src_modules/baseUrlPlusQueryParams.mjs"; } from "./src_modules/baseUrlPlusQueryParams.mjs";
import { import { calculateVorlaufMinusRuecklaufTemperature } from "./src_modules/calculateTemperatureDiff.mjs";
formatSensorThingsApiResponseForLineOrColumnChart,
drawLineChartHighcharts,
} from "./src_modules/chartLine.mjs";
import { import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./src_modules/fetchData.mjs";
formatSensorThingsApiResponseForHeatMap,
drawHeatMapHighcharts,
} from "./src_modules/chartHeatmap.mjs";
import { import {
formatSensorThingsApiResponseForScatterPlot, formatDatastreamMetadataForChart,
drawScatterPlotHighcharts, extractPropertiesFromFormattedDatastreamMetadata,
} from "./src_modules/chartScatterPlot.mjs"; } from "./src_modules/fetchedDataProcessing.mjs";
import { import {
formatAggregationResultForColumnChart, formatSensorThingsApiResponseForLineOrColumnChart,
drawColumnChartHighcharts, drawLineChartHighcharts,
} from "./src_modules/chartColumn.mjs"; } from "./src_modules/chartLine.mjs";
import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./src_modules/fetchData.mjs"; import { drawColumnChartHighcharts } from "./src_modules/chartColumn.mjs";
import { import {
formatDatastreamMetadataForChart, showLoadingSpinner,
extractPropertiesFromFormattedDatastreamMetadata, hideLoadingSpinner,
} from "./src_modules/fetchedDataProcessing.mjs"; } from "./src_modules/loadingIndicator.mjs";
import { calculateVorlaufMinusRuecklaufTemperature } from "./src_modules/calculateTemperatureDiff.mjs"; import { vanillaSelectBox } from "./thirdparty/vanillaSelectBox.mjs";
import { import {
extractObservationsWithinDatesInterval, extractObservationsWithinDatesInterval,
extractUniqueCalendarDatesFromTimestamp, extractUniqueCalendarDatesFromTimestamp,
extractUniqueCalendarMonthsFromCalendarDates,
} from "./src_modules/aggregateHelpers.mjs"; } from "./src_modules/aggregateHelpers.mjs";
import { import {
calculateMinimumObservationValuesWithinInterval, splitMultipleOptionsTextDelimitedBySlash,
calculateMaximumObservationValuesWithinInterval, getSelectedOptionsFromAllDropDownLists,
calculateSumOfObservationValuesWithinInterval, checkIfSelectedOptionsContainTemperatureDifference,
calculateAverageOfObservationValuesWithinInterval, deleteTemperatureDifferenceOptions,
} from "./src_modules/aggregate.mjs"; extractTemperatureDifferenceOptions,
extractBuildingPlusSamplingRate,
/** checkIfChartRequiresRawObservations,
* Test plotting of temp difference (dT) using heatmap getAbbreviationsForSelectedOptionsFromAllDropDownLists,
*/ } from "./src_modules/dropDownListHelpers.mjs";
const drawHeatmapHCUsingTempDifference = async function () {
try {
const [observationsTemperatureDiff225Arr, metadataTemperatureDiff225Arr] =
await calculateVorlaufMinusRuecklaufTemperature(
BASE_URL,
QUERY_PARAMS_COMBINED,
"225",
"60min"
);
// We want to have nested arrays, so as to mimick the nested responses we get from fetching observations + metadata
const observationsTemperatureDiff225NestedArr = [
observationsTemperatureDiff225Arr,
];
const metadataTemperatureDiff225NestedArr = [metadataTemperatureDiff225Arr]; import {
drawHeatmapBasedOnSelectedOptions,
// Format the observations drawScatterPlotFromChartSelection,
const formattedTempDiff225NestedArr = drawLineChartBasedOnSelectedAggregationOptions,
observationsTemperatureDiff225NestedArr.map((obsArr) => drawColumnChartBasedOnSelectedAggregationOptions,
formatSensorThingsApiResponseForHeatMap(obsArr) } from "./src_modules/dropDownListProcessing.mjs";
);
// Format the metadata
const formattedTempDiff225MetadataNestedArr =
metadataTemperatureDiff225NestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties
const extractedFormattedTempDiff225Properties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedTempDiff225MetadataNestedArr,
false
);
// First need to extract the formatted observations from the nested array
// Heatmap only needs one set of formatted observation values
drawHeatMapHighcharts(
...formattedTempDiff225NestedArr,
extractedFormattedTempDiff225Properties
);
} catch (err) {
console.error(err);
}
};
/** /**
* Test drawing of scatter plot chart * Use the `vanillaDropDown` library to style the buildings & data points drop down list
*
* @returns {undefined}
*/ */
const drawScatterPlotHCTest2 = async function () { const styleBuildingsDataPointsDropDown = function () {
try { // Create our dropdown list using `vanillaSelectBox`; supports the selection of multiple options
const sensorsOfInterestNestedArr = [ new vanillaSelectBox("#drop-down--bldg-data-point", {
["weather_station_521", "outside_temp", "60min"], "disableSelectAll": true,
["225", "vl", "60min"], "maxSelect": 5,
["125", "rl", "60min"], "placeHolder": "--Select--",
]; "search": false,
});
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the combined arrays for observations and metadata
const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// Extract values for x-axis and y-axis
// x-axis values are first element of nested observations array
const [obsXAxisArr] = observationsNestedArr.slice(0, 1);
// y-axis values are rest of elements of nested observations array
const obsYAxisNestedArr = observationsNestedArr.slice(1);
// Create formatted array(s) for observations
const formattedObservationsArr = obsYAxisNestedArr.map((obsYAxisArr) =>
formatSensorThingsApiResponseForScatterPlot(obsXAxisArr, obsYAxisArr)
);
// Create formatted array(s) for metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj, false)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
false
);
drawScatterPlotHighcharts(
formattedObservationsArr,
extractedFormattedDatastreamProperties
);
} catch (err) {
console.error(err);
}
}; };
/** /**
* Test drawing of line chart with multiple series * Use the `vanillaDropDown` library to style the aggregation type drop down list
*
* @returns {undefined}
*/ */
const testLineChartMultipleSeries = async function () { const styleAggregationDropDown = function () {
try { // Create our dropdown list using `vanillaSelectBox`
const sensorsOfInterestNestedArr = [ new vanillaSelectBox("#drop-down--aggregation-type", {
["225", "vl", "60min"], "disableSelectAll": true,
["125", "rl", "60min"], "maxSelect": 1,
["weather_station_521", "outside_temp", "60min"], "placeHolder": "--Select--",
]; "search": false,
});
const observationsPlusMetadataArr =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata arrays of arrays
const [observationsNestedArr, metadataNestedArr] =
observationsPlusMetadataArr;
// Format the observations
const formattedObservationsNestedArr = observationsNestedArr.map(
(observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataArr) =>
formatDatastreamMetadataForChart(metadataArr)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
false
);
drawLineChartHighcharts(
formattedObservationsNestedArr,
extractedFormattedDatastreamProperties
);
} catch (err) {
console.error(err);
}
}; };
/** /**
* Test drawing of column chart using aggregation / sum result - monthly * Use the `vanillaDropDown` library to style the third sampling rate down list
*
* @returns {undefined}
*/ */
const drawColumnChartMonthlySumTest = async function () { const styleSamplingRateDropDown = function () {
try { // Create our dropdown list using `vanillaSelectBox`
const sensorsOfInterestNestedArr = [ new vanillaSelectBox("#drop-down--sampling-rate", {
["125", "vl", "60min"], "disableSelectAll": true,
["225", "vl", "60min"], "maxSelect": 1,
]; "placeHolder": "--Select--",
"search": false,
const observationsPlusMetadata = });
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// User-specified start date and end date
const startDate = "2020-02-01";
const endDate = "2020-05-31";
// Extract observations within the user-specified start and end date
const observationsNestedArr = obsNestedArr.map((obsArr) =>
extractObservationsWithinDatesInterval(
obsArr,
"60min",
startDate,
endDate
)
);
// Unique calendar dates
const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
);
// Unique calendar months
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
(uniqueCalendarDatesArr) =>
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
);
// Calculate sum of values of observations - monthly
const observationsSumMonthlyNestedArr =
calculateSumOfObservationValuesWithinInterval(
observationsNestedArr,
"60min",
uniqueCalendarMonthsNestedArr,
"monthly"
);
// Format the observations
const formattedObservationsSumMonthlyNestedArr =
observationsSumMonthlyNestedArr.map((obsSumMonthlyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i],
obsSumMonthlyArr
)
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"monthly",
"sum"
);
drawColumnChartHighcharts(
formattedObservationsSumMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
} catch (err) {
console.error(err);
}
}; };
/** /**
* Test drawing of column chart using aggregation / sum result - daily * Use the `vanillaDropDown` library to style the chart type drop down list
*
* @returns {undefined}
*/ */
const drawColumnChartDailySumTest = async function () { const styleChartTypeDropDown = function () {
try { // Create our dropdown list using `vanillaSelectBox`
const sensorsOfInterestNestedArr = [ new vanillaSelectBox("#drop-down--chart-type", {
["125", "vl", "60min"], "disableSelectAll": true,
["225", "vl", "60min"], "maxSelect": 1,
]; "placeHolder": "--Select--",
"search": false,
const observationsPlusMetadata = });
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// User-specified start date and end date
const startDate = "2020-02-01";
const endDate = "2020-05-31";
// Extract observations within the user-specified start and end date
const observationsNestedArr = obsNestedArr.map((obsArr) =>
extractObservationsWithinDatesInterval(
obsArr,
"60min",
startDate,
endDate
)
);
// Unique calendar dates
const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
);
// Calculate sum of values of observations - daily
const observationsSumDailyNestedArr =
calculateSumOfObservationValuesWithinInterval(
observationsNestedArr,
"60min",
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - daily
const formattedObservationsSumDailyNestedArr =
observationsSumDailyNestedArr.map((obsSumDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsSumDailyArr
)
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"daily",
"sum"
);
drawColumnChartHighcharts(
formattedObservationsSumDailyNestedArr,
extractedFormattedDatastreamProperties
);
} catch (err) {
console.error(err);
}
}; };
/** /**
* Test drawing of column chart using raw observations * Callback function that wraps the logic of populating the linked drop down lists.
* Will run on `DOMContentLoaded` event
*
* @returns {undefined}
*/ */
const drawColumnChartNonAggregationTest = async function () { const afterDocumentLoads = function () {
try { styleBuildingsDataPointsDropDown();
const sensorsOfInterestNestedArr = [ styleAggregationDropDown();
["125", "vl", "60min"], styleSamplingRateDropDown();
["225", "vl", "60min"], styleChartTypeDropDown();
];
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// Format the observations
const formattedObservationsNestedArr = observationsNestedArr.map(
(observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataArr) =>
formatDatastreamMetadataForChart(metadataArr)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
false
);
drawColumnChartHighcharts(
formattedObservationsNestedArr,
extractedFormattedDatastreamProperties
);
} catch (err) {
console.error(err);
}
}; };
/** /**
* Test drawing of line chart using aggregation / average result - monthly * Callback function that draws a chart using options from the drop-down list that
* have been selected by a user.
* Will be run when the user clicks a button
*
* @async
* @returns {undefined} undefined
*/ */
const drawLineChartMonthlyAverageTest = async function () { const drawChartUsingSelectedOptions = async function () {
try { try {
const sensorsOfInterestNestedArr = [ const selectedOptionsAllDropDownLists =
["125", "vl", "60min"], getSelectedOptionsFromAllDropDownLists();
["225", "vl", "60min"],
];
const observationsPlusMetadata = // Note: The aggregation type + duration and chart type are the second and
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( // fourth elements respectively, we have ignored the first and third elements
BASE_URL, const [, selectedAggregationTypeDurationArr, , selectedChartTypeArr] =
QUERY_PARAMS_COMBINED, selectedOptionsAllDropDownLists;
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor // Create an array of aggregation type and duration
// Array elements in same order as input array const selectedAggregationTypeDurationSplitNestedArr =
const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata; splitMultipleOptionsTextDelimitedBySlash(
selectedAggregationTypeDurationArr
// User-specified start date and end date
const startDate = "2020-02-01";
const endDate = "2020-05-31";
// Extract observations within the user-specified start and end date
const observationsNestedArr = obsNestedArr.map((obsArr) =>
extractObservationsWithinDatesInterval(
obsArr,
"60min",
startDate,
endDate
)
);
// Unique calendar dates
const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
);
// Unique calendar months
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
(uniqueCalendarDatesArr) =>
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
);
// Calculate average of values of observations - monthly
const observationsAverageMonthlyNestedArr =
calculateAverageOfObservationValuesWithinInterval(
observationsNestedArr,
"60min",
uniqueCalendarMonthsNestedArr,
"monthly"
); );
// Format the observations // Separate the aggregation type and the aggregation duration strings
const formattedObservationsAverageMonthlyNestedArr = const [selectedAggregationTypeDurationSplitArr] =
observationsAverageMonthlyNestedArr.map((obsAverageMonthlyArr, i) => selectedAggregationTypeDurationSplitNestedArr;
formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i],
obsAverageMonthlyArr
)
);
// Format the metadata const [selectedAggregationTypeArr, selectedAggregationDuration] =
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) => selectedAggregationTypeDurationSplitArr;
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties // Array of building(s) + data point(s) + sampling rate
const extractedFormattedDatastreamProperties = const selectedBuildingsDataPointsSamplingRateAbbrev =
extractPropertiesFromFormattedDatastreamMetadata( getAbbreviationsForSelectedOptionsFromAllDropDownLists(
formattedMetadataNestedArr, selectedOptionsAllDropDownLists
true,
"monthly",
"average"
); );
drawLineChartHighcharts( // Create copies of the arrays of building(s) + data point(s) + sampling rate
formattedObservationsAverageMonthlyNestedArr, const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy = [
extractedFormattedDatastreamProperties ...selectedBuildingsDataPointsSamplingRateAbbrev,
);
} catch (err) {
console.error(err);
}
};
/**
* Test drawing of line chart using aggregation / average result - daily
*/
const drawLineChartDailyAverageTest = async function () {
try {
const sensorsOfInterestNestedArr = [
["125", "vl", "60min"],
["225", "vl", "60min"],
]; ];
const observationsPlusMetadata = const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy = [
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( ...selectedBuildingsDataPointsSamplingRateAbbrev,
BASE_URL,
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// User-specified start date and end date
const startDate = "2020-02-01";
const endDate = "2020-05-31";
// Extract observations within the user-specified start and end date
const observationsNestedArr = obsNestedArr.map((obsArr) =>
extractObservationsWithinDatesInterval(
obsArr,
"60min",
startDate,
endDate
)
);
// Unique calendar dates
const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
);
// Calculate average of values of observations - daily
const observationsAverageDailyNestedArr =
calculateAverageOfObservationValuesWithinInterval(
observationsNestedArr,
"60min",
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - daily
const formattedObservationsAverageDailyNestedArr =
observationsAverageDailyNestedArr.map((obsAverageDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsAverageDailyArr
)
);
// Format the metadata
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"daily",
"average"
);
drawLineChartHighcharts(
formattedObservationsAverageDailyNestedArr,
extractedFormattedDatastreamProperties
);
} catch (err) {
console.error(err);
}
};
/**
* Test drawing of line chart using aggregation / minimum result - daily
*/
const drawLineChartDailyMinTest = async function () {
try {
const sensorsOfInterestNestedArr = [
["125", "vl", "60min"],
["225", "vl", "60min"],
]; ];
const observationsPlusMetadata = // Check if we have non-computed
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( const selectedBuildingsDataPointsSamplingRateAbbrevNonComputed =
BASE_URL, checkIfSelectedOptionsContainTemperatureDifference(
QUERY_PARAMS_COMBINED, selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor
// Array elements in same order as input array
const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// User-specified start date and end date
const startDate = "2020-02-01";
const endDate = "2020-05-31";
// Extract observations within the user-specified start and end date
const observationsNestedArr = obsNestedArr.map((obsArr) =>
extractObservationsWithinDatesInterval(
obsArr,
"60min",
startDate,
endDate
) )
); ? deleteTemperatureDifferenceOptions(
selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy
// Unique calendar dates )
const uniqueCalendarDatesNestedArr = observationsNestedArr.map( : selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopy;
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr) // Check if we have computed / dT
); const selectedBuildingsDataPointsSamplingRateAbbrevComputed =
checkIfSelectedOptionsContainTemperatureDifference(
// Calculate average of values of observations - daily selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy
const observationsMinimumDailyNestedArr = )
calculateMinimumObservationValuesWithinInterval( ? extractTemperatureDifferenceOptions(
observationsNestedArr, selectedBuildingsDataPointsSamplingRateAbbrevComputedCopy
"60min", )
uniqueCalendarDatesNestedArr, : [];
"daily"
); // Display the loading indicator
showLoadingSpinner();
// Fetch the observations + metadata / non-computed
const observationsPlusMetadataNonComputed =
selectedBuildingsDataPointsSamplingRateAbbrevNonComputed.length === 0
? [[], []]
: await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
selectedBuildingsDataPointsSamplingRateAbbrevNonComputed
);
// Fetch the observations + metadata / computed (dT)
const observationsPlusMetadataComputed =
selectedBuildingsDataPointsSamplingRateAbbrevComputed.length === 0
? [[], []]
: await calculateVorlaufMinusRuecklaufTemperature(
BASE_URL,
QUERY_PARAMS_COMBINED,
extractBuildingPlusSamplingRate(
selectedBuildingsDataPointsSamplingRateAbbrevComputed
)
);
// Extract the combined arrays for observations and metadata / non-computed
const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] =
observationsPlusMetadataNonComputed;
// Extract the combined arrays for observations and metadata / computed (dT)
const [observationsNestedComputedArr, metadataNestedComputedArr] =
observationsPlusMetadataComputed;
// Create a combined array of observations and metadata
const observationsPlusMetadataCombined = [
[...observationsNestedNonComputedArr, ...observationsNestedComputedArr],
[...metadataNestedNonComputedArr, ...metadataNestedComputedArr],
];
// Format the observations - daily const [observationsComboNestedArr, metadataComboNestedArr] =
const formattedObservationsMinimumDailyNestedArr = observationsPlusMetadataCombined;
observationsMinimumDailyNestedArr.map((obsMinDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsMinDailyArr
)
);
// Format the metadata // Create formatted array(s) for metadata - used by ALL chart types
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) => const formattedMetadataNestedArr = metadataComboNestedArr.map(
formatDatastreamMetadataForChart(metadataObj) (metadataObj) => formatDatastreamMetadataForChart(metadataObj)
); );
// Extract the formatted metadata properties // Extract the formatted metadata properties - used by ALL chart types
const extractedFormattedDatastreamProperties = const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata( extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr, formattedMetadataNestedArr,
true, false
"daily",
"minimum"
); );
drawLineChartHighcharts( // The formatted abbreviations array is nested
formattedObservationsMinimumDailyNestedArr, const [selectedBuildingsDataPointsSamplingRateAbbrevArr] =
extractedFormattedDatastreamProperties selectedBuildingsDataPointsSamplingRateAbbrev;
);
} catch (err) {
console.error(err);
}
};
/** // Extract the formatted sampling rate string - used by ALL chart types
* Test drawing of line chart using aggregation / maximum result - daily const [, , selectedSamplingRateAbbrev] =
*/ selectedBuildingsDataPointsSamplingRateAbbrevArr;
const drawLineChartMonthlyMaxTest = async function () {
try {
const sensorsOfInterestNestedArr = [
["125", "vl", "60min"],
["225", "vl", "60min"],
];
const observationsPlusMetadata = // User-specified start date and end date for aggregation - used by MULTIPLE chart types
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( const aggregationStartDate = "2020-01-01";
BASE_URL, const aggregationEndDate = "2020-12-31";
QUERY_PARAMS_COMBINED,
sensorsOfInterestNestedArr
);
// Extract the observations and metadata for each sensor // Extract observations within the user-specified start and end date - used by MULTIPLE chart types
// Array elements in same order as input array const observationsAggregationNestedArr = observationsComboNestedArr.map(
const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata; (obsArr) =>
extractObservationsWithinDatesInterval(
// User-specified start date and end date obsArr,
const startDate = "2020-02-01"; "60min",
const endDate = "2020-12-31"; aggregationStartDate,
aggregationEndDate
// Extract observations within the user-specified start and end date )
const observationsNestedArr = obsNestedArr.map((obsArr) =>
extractObservationsWithinDatesInterval(
obsArr,
"60min",
startDate,
endDate
)
); );
// Unique calendar dates // Unique calendar dates - used by MULTIPLE chart types
const uniqueCalendarDatesNestedArr = observationsNestedArr.map( const uniqueCalendarDatesNestedArr = observationsAggregationNestedArr.map(
(observationsArr) => (observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr) extractUniqueCalendarDatesFromTimestamp(observationsArr)
); );
// Unique calendar months selectedChartTypeArr.forEach((selectedChartType) => {
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map( if (selectedChartType === "Heatmap") {
(uniqueCalendarDatesArr) => // We are interested in raw observations
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr) if (
); checkIfChartRequiresRawObservations(
selectedAggregationTypeArr,
// Calculate maximum values of observations - monthly selectedAggregationDuration
const observationsMaxMonthlyNestedArr = )
calculateMaximumObservationValuesWithinInterval( ) {
observationsNestedArr, drawHeatmapBasedOnSelectedOptions(
"60min", selectedBuildingsDataPointsSamplingRateAbbrev,
uniqueCalendarMonthsNestedArr, observationsComboNestedArr,
"monthly" extractedFormattedDatastreamProperties
); );
} else {
// Format the observations - monthly throw new Error(
const formattedObservationsMaxMonthlyNestedArr = "This type of chart (Heatmap) does not support aggregated results"
observationsMaxMonthlyNestedArr.map((obsMaxMonthlyArr, i) => );
formatAggregationResultForColumnChart( }
uniqueCalendarMonthsNestedArr[i], }
obsMaxMonthlyArr if (selectedChartType === "Scatter Plot") {
) // We are interested in raw observations
); if (
checkIfChartRequiresRawObservations(
// Format the metadata selectedAggregationTypeArr,
const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) => selectedAggregationDuration
formatDatastreamMetadataForChart(metadataObj) )
); ) {
drawScatterPlotFromChartSelection(
// Extract the formatted metadata properties selectedBuildingsDataPointsSamplingRateAbbrev,
const extractedFormattedDatastreamProperties = observationsComboNestedArr,
extractPropertiesFromFormattedDatastreamMetadata( extractedFormattedDatastreamProperties
formattedMetadataNestedArr, );
true, } else {
"monthly", throw new Error(
"maximum" "This type of chart (Scatter Plot) does not support aggregated results"
); );
}
drawLineChartHighcharts( }
formattedObservationsMaxMonthlyNestedArr, if (selectedChartType === "Line") {
extractedFormattedDatastreamProperties // We are interested in raw observations or aggregated observations
);
// Raw observations
if (selectedAggregationTypeArr === "None (raw data)") {
// Create formatted array(s) for observations
const formattedRawObservationsLineChartNestedArr =
observationsComboNestedArr.map((observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
);
drawLineChartHighcharts(
formattedRawObservationsLineChartNestedArr,
extractedFormattedDatastreamProperties
);
}
// Aggregated observations
else {
drawLineChartBasedOnSelectedAggregationOptions(
selectedAggregationTypeArr,
selectedAggregationDuration,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
formattedMetadataNestedArr
);
}
}
if (selectedChartType === "Column") {
// We are interested in raw observations or aggregated observations
// Raw observations
if (selectedAggregationTypeArr === "None (raw data)") {
// Create formatted array(s) for observations
const formattedRawObservationsColumnChartNestedArr =
observationsComboNestedArr.map((observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
);
drawColumnChartHighcharts(
formattedRawObservationsColumnChartNestedArr,
extractedFormattedDatastreamProperties
);
}
// Aggregated observations
else {
drawColumnChartBasedOnSelectedAggregationOptions(
selectedAggregationTypeArr,
selectedAggregationDuration,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
formattedMetadataNestedArr
);
}
}
});
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} finally {
// Hide the loading indicator
hideLoadingSpinner();
} }
}; };
// drawScatterPlotHCTest2(); document.addEventListener("DOMContentLoaded", afterDocumentLoads);
// drawHeatmapHCUsingTempDifference();
// testLineChartMultipleSeries(); document
// drawColumnChartMonthlySumTest(); .querySelector("#btn-draw-chart")
// drawColumnChartDailySumTest(); .addEventListener("click", drawChartUsingSelectedOptions);
// drawColumnChartNonAggregationTest();
// drawLineChartMonthlyAverageTest();
// drawLineChartDailyAverageTest();
// drawLineChartDailyMinTest();
// drawLineChartMonthlyMaxTest();
...@@ -7,127 +7,139 @@ import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./fe ...@@ -7,127 +7,139 @@ import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./fe
import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcessing.mjs"; import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcessing.mjs";
/** /**
* Calculate the temperature difference, dT, between Vorlauf temperature [VL] and Rücklauf temperature [RL] (i.e., dT = VL - RL) * Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
* Rücklauf temperature [RL] (i.e., dT = VL - RL). In addition, create synthetic metadata
* for the temperature difference
* @async * @async
* @param {String} baseUrl Base URL of the STA server * @param {String} baseUrl Base URL of the STA server
* @param {Object} urlParams The URL parameters to be sent together with the GET request * @param {Object} urlParams The URL parameters to be sent together with the GET request
* @param {String} buildingId The building ID as a string * @param {Array} buildingSamplingRateNestedArr A N*1 array (where N >= 1) containing a nested array of buildings & sampling rates as strings, i.e. [["101", "15min"]] or [["101", "15min"], ["102", "60min"]] or [["101", "15min"], ["102", "60min"], ["225", "60min"]], etc
* @param {String} samplingRate The sampling rate as a string * @returns {Promise} A promise that contains a 1*2 array (the first element is an array that contans N Observations arrays; and the second element is an array of N Datastream metadata objects) when fulfilled
* @returns {Promise} A promise that contains an array (that is made up of a temperature difference array and a metadata object) when fulfilled
*/ */
export const calculateVorlaufMinusRuecklaufTemperature = async function ( export const calculateVorlaufMinusRuecklaufTemperature = async function (
baseUrl, baseUrl,
urlParams, urlParams,
buildingId, buildingSamplingRateNestedArr
samplingRate
) { ) {
try { try {
const bldgSensorSamplingRateNestedArr = [ // Arrays to store our results
[buildingId, "vl", samplingRate], const combinedObservationsArr = [];
[buildingId, "rl", samplingRate], const combinedMetadataArr = [];
];
const buildingDataPointSamplingRateNestedTwiceArr =
const BUILDING_ID = buildingId; buildingSamplingRateNestedArr.map((bldgSmplngRate) => {
const SAMPLING_RATE = samplingRate; // The building ID is the first element, sampling rate is second element
return [
const observationsPlusMetadata = [bldgSmplngRate[0], "vl", bldgSmplngRate[1]],
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( [bldgSmplngRate[0], "rl", bldgSmplngRate[1]],
baseUrl, ];
urlParams, });
bldgSensorSamplingRateNestedArr
// 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,
urlParams,
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]
); );
// Extract Vorlauf temperature, Ruecklauf temperature and metadata // The arrays have equal length, we need only use one of them for looping
const [ // Resulting array contains the following pairs (timestamp + dT)
[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr], const vorlaufMinusRuecklaufTemperatureObs = vorlaufTemperatureObsArr.map(
[metadataVorlauf, metadataRuecklauf], (vlTempObs, i) => {
] = observationsPlusMetadata; // Use timestamp from VL, since is equal to that of RL
const timestamp = vlTempObs[0];
// Compare the lengths of the observations arrays for VL and RL,
// delete the unique observation(s), if necessary // Case 1: One of the observation values is `null`,
const [vorlaufTemperatureObsFinalArr, ruecklaufTemperatureObsFinalArr] = // no need to calculate temperature difference
vorlaufTemperatureObsArr.length === ruecklaufTemperatureObsArr.length if (
? [vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr] vorlaufTemperatureValues[i] === null ||
: checkForAndDeleteUniqueObservationsFromLargerArray( ruecklaufTemperatureValues[i] === null
vorlaufTemperatureObsArr, ) {
ruecklaufTemperatureObsArr return [timestamp, null];
); }
// Extract the temperature values // Case 2: Neither of the observation values is `null`,
const vorlaufTemperatureValues = vorlaufTemperatureObsFinalArr.map( // calculate temperature difference
(vlTempObs) => vlTempObs[1] return [
); timestamp,
const ruecklaufTemperatureValues = ruecklaufTemperatureObsFinalArr.map( vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i],
(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`, // From Vorlauf metadata, extract `name` and `unitOfMeasurement`
// calculate temperature difference const {
return [ name: datastreamNameVorlauf,
timestamp, unitOfMeasurement: unitOfMeasurementVorlauf,
vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i], } = metadataVorlauf;
];
} // From Ruecklauf metadata, extract `name`
); const { name: datastreamNameRuecklauf } = metadataRuecklauf;
// From Vorlauf metadata, extract `name` and `unitOfMeasurement` // Extract the phenomenon names from the Datastream names
const { const phenomenonNameVorlauf = extractPhenomenonNameFromDatastreamName(
name: datastreamNameVorlauf, datastreamNameVorlauf
unitOfMeasurement: unitOfMeasurementVorlauf, );
} = metadataVorlauf; const phenomenonNameRuecklauf = extractPhenomenonNameFromDatastreamName(
datastreamNameRuecklauf
// 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 our custom datastream name text // Create our custom datastream description text
const nameTempDifference = `BOSCH_${BUILDING_ID} / dT Temperature difference (VL-RL) DS:${SAMPLING_RATE}`; // The resulting datastream description string has two `temperature` substrings;
// replace the first occurence with an empty string
// The datastream object that we return needs to have these property names const descriptionTempDifference =
const description = descriptionTempDifference; `Computed dT: ${phenomenonNameVorlauf} minus ${phenomenonNameRuecklauf}`.replace(
const name = nameTempDifference; "temperature",
const unitOfMeasurement = unitOfMeasurementVorlauf; ""
);
return [
vorlaufMinusRuecklaufTemperatureObs, // Create our custom datastream name text
{ const nameTempDifference = `BOSCH_${BUILDING_ID} / dT Temperature difference (VL-RL) DS:${SAMPLING_RATE}`;
description,
name, // The datastream object that we return needs to have these property names
unitOfMeasurement, const description = descriptionTempDifference;
}, const name = nameTempDifference;
]; const unitOfMeasurement = unitOfMeasurementVorlauf;
// Add the observations and metadata to our arrays
combinedObservationsArr.push(vorlaufMinusRuecklaufTemperatureObs);
combinedMetadataArr.push({ description, name, unitOfMeasurement });
}
return [combinedObservationsArr, combinedMetadataArr];
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
......
"use strict";
import {
BASE_URL,
QUERY_PARAMS_COMBINED,
} from "./src_modules/baseUrlPlusQueryParams.mjs";
import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./src_modules/fetchData.mjs";
import {
formatDatastreamMetadataForChart,
extractPropertiesFromFormattedDatastreamMetadata,
} from "./src_modules/fetchedDataProcessing.mjs";
import {
formatSensorThingsApiResponseForLineOrColumnChart,
drawLineChartHighcharts,
} from "./src_modules/chartLine.mjs";
import {
formatSensorThingsApiResponseForHeatMap,
drawHeatMapHighcharts,
} from "./src_modules/chartHeatmap.mjs";
import {
showLoadingSpinner,
hideLoadingSpinner,
} from "./src_modules/loadingIndicator.mjs";
import { vanillaSelectBox } from "./thirdparty/vanillaSelectBox.mjs";
/**
* Use the `vanillaDropDown` library to style the first level drop down list
*
* @returns {undefined}
*/
const styleLevelOneDropDown = function () {
// Create our dropdown list using `vanillaSelectBox`; supports the selection of multiple options
new vanillaSelectBox("#drop-down--bldg-data-point", {
"disableSelectAll": true,
"maxSelect": 5,
"placeHolder": "--Select--",
"search": false,
});
};
/**
* Use the `vanillaDropDown` library to style the second level drop down list
*
* @returns {undefined}
*/
const styleLevelTwoDropDown = function () {
// Create our dropdown list using `vanillaSelectBox`
new vanillaSelectBox("#drop-down--aggregation-type", {
"disableSelectAll": true,
"maxSelect": 1,
"placeHolder": "--Select--",
"search": false,
});
};
/**
* Use the `vanillaDropDown` library to style the third level drop down list
*
* @returns {undefined}
*/
const styleLevelThreeDropDown = function () {
// Create our dropdown list using `vanillaSelectBox`
new vanillaSelectBox("#drop-down--sampling-rate", {
"disableSelectAll": true,
"maxSelect": 1,
"placeHolder": "--Select--",
"search": false,
});
};
/**
* Use the `vanillaDropDown` library to style the fourth level drop down list
*
* @returns {undefined}
*/
const styleLevelFourDropDown = function () {
// Create our dropdown list using `vanillaSelectBox`
new vanillaSelectBox("#drop-down--chart-type", {
"disableSelectAll": true,
"maxSelect": 1,
"placeHolder": "--Select--",
"search": false,
});
};
/**
* Callback function that wraps the logic of populating the linked drop down lists.
* Will run on `DOMContentLoaded` event
*
* @returns {undefined}
*/
const afterDocumentLoads = function () {
styleLevelOneDropDown();
styleLevelTwoDropDown();
styleLevelThreeDropDown();
styleLevelFourDropDown();
};
/** /**
* Get the selected option(s) from a dropdown list * Get the selected option(s) from a dropdown list
* *
...@@ -126,7 +22,6 @@ const getSelectedOptionsFromDropDownList = function (selectorStr) { ...@@ -126,7 +22,6 @@ const getSelectedOptionsFromDropDownList = function (selectorStr) {
/** /**
* Process the selected option(s) from a buildings & data points dropdown list. * Process the selected option(s) from a buildings & data points dropdown list.
* This is currently the first dropdown list in the UI.
* *
* @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected `<option>` elements * @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected `<option>` elements
* @returns {Array} An array of string(s) representing the processed value(s) of the selected buildings & data points option(s) * @returns {Array} An array of string(s) representing the processed value(s) of the selected buildings & data points option(s)
...@@ -138,10 +33,9 @@ const processSelectionsFromBuildingDataPointOptions = function ( ...@@ -138,10 +33,9 @@ const processSelectionsFromBuildingDataPointOptions = function (
const selectedOptionsBuildingDataPointArr = []; const selectedOptionsBuildingDataPointArr = [];
selectedOptionsArr.forEach((optionStr) => { selectedOptionsArr.forEach((optionStr) => {
// Case 1: <option> element's value CONTAINS a "/" character
// We wish to create a string like this `Bau 101/VL`
if (optionStr.includes("/")) { if (optionStr.includes("/")) {
// Case 1: <option> element's value CONTAINS a "/" character
// We wish to create a string like this `Bau 101/VL`
// Split the <option> element's value into two substrings // Split the <option> element's value into two substrings
const optionsStrPartOne = optionStr.slice(0, 3); const optionsStrPartOne = optionStr.slice(0, 3);
const optionsStrPartTwo = optionStr.slice(3); const optionsStrPartTwo = optionStr.slice(3);
...@@ -153,9 +47,10 @@ const processSelectionsFromBuildingDataPointOptions = function ( ...@@ -153,9 +47,10 @@ const processSelectionsFromBuildingDataPointOptions = function (
const optionsStrNew = optionsStrPartOneNew + optionsStrPartTwo; const optionsStrNew = optionsStrPartOneNew + optionsStrPartTwo;
selectedOptionsBuildingDataPointArr.push(optionsStrNew); selectedOptionsBuildingDataPointArr.push(optionsStrNew);
} else { }
// Case 2: <option> element's value DOES NOT CONTAIN a "/" character // Case 2: <option> element's value DOES NOT CONTAIN a "/" character
// We wish to create a string like this `Other/Außentemp` // We wish to create a string like this `Other/Außentemp`
else {
selectedOptionsBuildingDataPointArr.push(`Other/${optionStr}`); selectedOptionsBuildingDataPointArr.push(`Other/${optionStr}`);
} }
}); });
...@@ -166,11 +61,23 @@ const processSelectionsFromBuildingDataPointOptions = function ( ...@@ -166,11 +61,23 @@ const processSelectionsFromBuildingDataPointOptions = function (
/** /**
* Split an option element's value (a string) using a forward slash character ("/") as the delimiter * 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 `<option>` 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 `<option>` elements * @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected `<option>` elements
* @returns {Array} An array made up of resulting strings after splitting * @returns {Array} An array made up of resulting strings after splitting
*/ */
const splitOptionsTextDelimitedBySlash = function (selectedOptionsArr) { const splitMultipleOptionsTextDelimitedBySlash = function (selectedOptionsArr) {
return selectedOptionsArr.map((selectedOption) => selectedOption.split("/")); return selectedOptionsArr.map((selectedOption) =>
splitOptionsTextDelimitedBySlash(selectedOption)
);
}; };
/** /**
...@@ -185,7 +92,9 @@ const getSelectedOptionsFromAllDropDownLists = function () { ...@@ -185,7 +92,9 @@ const getSelectedOptionsFromAllDropDownLists = function () {
// Separate the building ID from the data point // Separate the building ID from the data point
const selectedBuildingDataPointOptionsSplitArr = const selectedBuildingDataPointOptionsSplitArr =
splitOptionsTextDelimitedBySlash(selectedBuildingDataPointOptionsArr); splitMultipleOptionsTextDelimitedBySlash(
selectedBuildingDataPointOptionsArr
);
const selectedAggregationOptionsArr = getSelectedOptionsFromDropDownList( const selectedAggregationOptionsArr = getSelectedOptionsFromDropDownList(
"#drop-down--aggregation-type" "#drop-down--aggregation-type"
...@@ -200,7 +109,6 @@ const getSelectedOptionsFromAllDropDownLists = function () { ...@@ -200,7 +109,6 @@ 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 (
selectedBuildingDataPointOptionsSplitArr.length === 0 || selectedBuildingDataPointOptionsSplitArr.length === 0 ||
selectedAggregationOptionsArr.length === 0 || selectedAggregationOptionsArr.length === 0 ||
...@@ -217,6 +125,177 @@ const getSelectedOptionsFromAllDropDownLists = function () { ...@@ -217,6 +125,177 @@ const getSelectedOptionsFromAllDropDownLists = function () {
]; ];
}; };
/**
* 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
) {
// Calculate the index(es) that we wish to delete
const foundIndexesArr = getIndexesOfTemperatureDifferenceOptions(
buildingDataPointSamplingRateAbbrevArr
);
// Delete the index(es) of `dT`, modifies the array in place
// Note: The resulting array is sparse
foundIndexesArr.forEach(
(foundIndex) => delete buildingDataPointSamplingRateAbbrevArr[foundIndex]
);
// Array to store our final result
const buildingDataPointFinalArr = [];
// Remove the empty sub array(s) that makes entire array sparse
// Note: `empty` does not mean `undefined` or `null`
buildingDataPointSamplingRateAbbrevArr.forEach(
(bldgDataPntSmplingRateAbbrvArr) => {
if (typeof bldgDataPntSmplingRateAbbrvArr === "object") {
buildingDataPointFinalArr.push(bldgDataPntSmplingRateAbbrvArr);
}
}
);
return buildingDataPointFinalArr;
};
/**
* 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];
// 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);
});
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;
}
};
/** /**
* 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
...@@ -239,17 +318,18 @@ const getBuildingSensorSamplingRateAbbreviation = function ( ...@@ -239,17 +318,18 @@ const getBuildingSensorSamplingRateAbbreviation = function (
"Bau 225": "225", "Bau 225": "225",
"Other": "weather_station_521", "Other": "weather_station_521",
}, },
phenomenon: { phenomenon: {
VL: "vl", VL: "vl",
RL: "rl", RL: "rl",
dT: "dT", dT: "dT",
Durchfluss: "flow", Durchfluss: "flow",
Leistung: "power", Leistung: "power",
Energie: "energy", Energie: "energy",
Energie_VERBR: "energy_verb", Energie_VERBR: "energy_verb",
Außentemp: "outside_temp", Außentemp: "outside_temp",
}, },
samplingRate: { samplingRate: {
"15 min": "15min", "15 min": "15min",
"60 min": "60min", "60 min": "60min",
...@@ -330,88 +410,13 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function ( ...@@ -330,88 +410,13 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function (
); );
}; };
/** export {
* Callback function for chart selection using drop down list splitMultipleOptionsTextDelimitedBySlash,
* @returns {undefined} getSelectedOptionsFromAllDropDownLists,
*/ checkIfSelectedOptionsContainTemperatureDifference,
const selectChartTypeFromDropDown = async function () { deleteTemperatureDifferenceOptions,
try { extractTemperatureDifferenceOptions,
const selectedOptions = getSelectedOptionsFromDropDownLists(); extractBuildingPlusSamplingRate,
checkIfChartRequiresRawObservations,
if (selectedOptions === undefined) return; getAbbreviationsForSelectedOptionsFromAllDropDownLists,
const selectedOptionsAbbreviationsArr =
getBuildingSensorSamplingRateAbbreviation(...selectedOptions);
const selectedChartType = document.querySelector(
"#drop-down--chart-type"
).value;
if (selectedChartType === "--Select--") return;
// Display the loading indicator
showLoadingSpinner();
// The `getMetadataPlusObservationsFromSingleOrMultipleDatastreams` function expects a nested array structure
const abbreviationsNestedArr = [selectedOptionsAbbreviationsArr];
const observationsPlusMetadata =
await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
abbreviationsNestedArr
);
// Extract the combined arrays for observations and metadata
const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;
// Create formatted array(s) for observations - line chart
const formattedObsLineChartNestedArr = observationsNestedArr.map(
(observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
);
// Create formatted array(s) for observations - heatmap
const formattedObsHeatMapNestedArr = observationsNestedArr.map(
(observationsArr) =>
formatSensorThingsApiResponseForHeatMap(observationsArr)
);
// Create formatted array(s) for metadata - same for both chart types
const formattedMetadataArr = metadataNestedArr.map((metadataObj) =>
formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataArr,
false
);
if (selectedChartType === "Line") {
drawLineChartHighcharts(
formattedObsLineChartNestedArr,
extractedFormattedDatastreamProperties
);
} else if (selectedChartType === "Heatmap") {
// First need to extract the formatted observations from the nested array
// Heatmap only needs one set of formatted observation values
drawHeatMapHighcharts(
...formattedObsHeatMapNestedArr,
extractedFormattedDatastreamProperties
);
}
} catch (err) {
console.error(err);
} finally {
// Hide the loading indicator
hideLoadingSpinner();
}
}; };
document.addEventListener("DOMContentLoaded", afterDocumentLoads);
document
.querySelector("#drop-down--chart-type")
.addEventListener("change", selectChartTypeFromDropDown);
import { drawLineChartHighcharts } from "./chartLine.mjs";
import {
formatSensorThingsApiResponseForHeatMap,
drawHeatMapHighcharts,
} from "./chartHeatmap.mjs";
import {
formatSensorThingsApiResponseForScatterPlot,
drawScatterPlotHighcharts,
} from "./chartScatterPlot.mjs";
import {
formatAggregationResultForColumnChart,
drawColumnChartHighcharts,
} from "./chartColumn.mjs";
import { extractUniqueCalendarMonthsFromCalendarDates } from "./aggregateHelpers.mjs";
import {
calculateMinimumObservationValuesWithinInterval,
calculateMaximumObservationValuesWithinInterval,
calculateSumOfObservationValuesWithinInterval,
calculateAverageOfObservationValuesWithinInterval,
} from "./aggregate.mjs";
import { extractPropertiesFromFormattedDatastreamMetadata } from "./fetchedDataProcessing.mjs";
/**
* 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"
);
}
};
/**
* 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");
}
};
/**
* Calculate the daily sum of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (daily sum) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatDailySumObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Calculate sum of values of observations - daily
const observationsSumDailyNestedArr =
calculateSumOfObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - sum / daily
const formattedObservationsSumDailyNestedArr =
observationsSumDailyNestedArr.map((obsSumDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsSumDailyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"daily",
"sum"
);
return [
formattedObservationsSumDailyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Calculate the monthly sum of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (monthly sum) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatMonthlySumObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Unique calendar months
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
(uniqueCalendarDatesArr) =>
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
);
// Calculate sum of values of observations - monthly
const observationsSumMonthlyNestedArr =
calculateSumOfObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarMonthsNestedArr,
"monthly"
);
// Format the observations - sum / monthly
const formattedObservationsSumMonthlyNestedArr =
observationsSumMonthlyNestedArr.map((obsSumMonthlyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i],
obsSumMonthlyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"monthly",
"sum"
);
return [
formattedObservationsSumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Calculate the daily maximum of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (daily maximum) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatDailyMaximumObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Calculate minimum of values of observations - daily
const observationsMaximumDailyNestedArr =
calculateMaximumObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - min / daily
const formattedObservationsMaximumDailyNestedArr =
observationsMaximumDailyNestedArr.map((obsMinDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsMinDailyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"daily",
"maximum"
);
return [
formattedObservationsMaximumDailyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Calculate the monthly maximum of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (monthly maximum) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatMonthlyMaximumObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Unique calendar months
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
(uniqueCalendarDatesArr) =>
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
);
// Calculate minimum of values of observations - monthly
const observationsMaximumMonthlyNestedArr =
calculateMaximumObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarMonthsNestedArr,
"monthly"
);
// Format the observations - max / monthly
const formattedObservationsMaximumMonthlyNestedArr =
observationsMaximumMonthlyNestedArr.map((obsMaxMonthlyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i],
obsMaxMonthlyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"monthly",
"maximum"
);
return [
formattedObservationsMaximumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Calculate the daily minimum of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (daily minimum) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatDailyMinimumObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Calculate minimum of values of observations - daily
const observationsMinimumDailyNestedArr =
calculateMinimumObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - min / daily
const formattedObservationsMinimumDailyNestedArr =
observationsMinimumDailyNestedArr.map((obsMinDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsMinDailyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"daily",
"minimum"
);
return [
formattedObservationsMinimumDailyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Calculate the monthly minimum of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (monthly minimum) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatMonthlyMinimumObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Unique calendar months
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
(uniqueCalendarDatesArr) =>
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
);
// Calculate minimum of values of observations - monthly
const observationsMinimumMonthlyNestedArr =
calculateMinimumObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarMonthsNestedArr,
"monthly"
);
// Format the observations - min / monthly
const formattedObservationsMinimumMonthlyNestedArr =
observationsMinimumMonthlyNestedArr.map((obsMinMonthlyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i],
obsMinMonthlyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"monthly",
"minimum"
);
return [
formattedObservationsMinimumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Calculate the daily average of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (daily average) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatDailyAverageObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Calculate average of values of observations - daily
const observationsAverageDailyNestedArr =
calculateAverageOfObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
"daily"
);
// Format the observations - average / daily
const formattedObservationsAverageDailyNestedArr =
observationsAverageDailyNestedArr.map((obsAverageDailyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarDatesNestedArr[i],
obsAverageDailyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"daily",
"average"
);
return [
formattedObservationsAverageDailyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Calculate the monthly average of observations and format these aggregated observations
*
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {Array} An array whose first element is the formatted aggregated (monthly average) observations. The second element is an object made up of extracted & formatted datastream properties
*/
const calculateAndFormatMonthlyAverageObservations = function (
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
) {
// Unique calendar months
const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
(uniqueCalendarDatesArr) =>
extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
);
// Calculate average of values of observations - monthly
const observationsAverageMonthlyNestedArr =
calculateAverageOfObservationValuesWithinInterval(
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarMonthsNestedArr,
"monthly"
);
// Format the observations - average / monthly
const formattedObservationsAverageMonthlyNestedArr =
observationsAverageMonthlyNestedArr.map((obsAverageMonthlyArr, i) =>
formatAggregationResultForColumnChart(
uniqueCalendarMonthsNestedArr[i],
obsAverageMonthlyArr
)
);
// Extract the formatted metadata properties
const extractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
true,
"monthly",
"average"
);
return [
formattedObservationsAverageMonthlyNestedArr,
extractedFormattedDatastreamProperties,
];
};
/**
* Draw a line chart based on the selected aggregation options from a drop-down list
*
* @param {String} selectedAggregationType A string representing the selected aggregation type. The currently supported strings include `Sum`, `Maximum`, `Minimum` and `Average`
* @param {String} selectedAggregationDuration A string representing the selected aggregation duration. The currently supported strings include `Daily` and `Monthly`
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {undefined} undefined
*/
const drawLineChartBasedOnSelectedAggregationOptions = function (
selectedAggregationType,
selectedAggregationDuration,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
formattedMetadataNestedArr
) {
if (
selectedAggregationType === "Sum" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / sum
const [
formattedObservationsSumDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailySumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsSumDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationType === "Sum" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / sum
const [
formattedObservationsSumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlySumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsSumMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationType === "Maximum" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / maximum
const [
formattedObservationsMaximumDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailyMaximumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsMaximumDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationType === "Maximum" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / maximum
const [
formattedObservationsMaximumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlyMaximumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsMaximumMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationType === "Minimum" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / minimum
const [
formattedObservationsMinimumDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailyMinimumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsMinimumDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationType === "Minimum" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / minimum
const [
formattedObservationsMinimumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlyMinimumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsMinimumMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationType === "Average" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / average
const [
formattedObservationsAverageDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailyAverageObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsAverageDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationType === "Average" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / average
const [
formattedObservationsAverageMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlyAverageObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawLineChartHighcharts(
formattedObservationsAverageMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
}
};
/**
* Draw a column chart based on the selected aggregation options from a drop-down list
*
* @param {String} selectedAggregationType A string representing the selected aggregation type. The currently supported strings include `Sum`, `Maximum`, `Minimum` and `Average`
* @param {String} selectedAggregationDuration A string representing the selected aggregation duration. The currently supported strings include `Daily` and `Monthly`
* @param {Array} observationsAggregationNestedArr An array made up of sub-array(s) of aggregated observations
* @param {String} selectedSamplingRateAbbrev A string representing the abbreviated form of the selected sampling rate option
* @param {Array} uniqueCalendarDatesNestedArr An array made up of sub-array(s) of unique calendar date(s) string(s)
* @param {Array} formattedMetadataNestedArr An array of sub-arrays of formatted metadata properties
* @returns {undefined} undefined
*/
const drawColumnChartBasedOnSelectedAggregationOptions = function (
selectedAggregationTypeArr,
selectedAggregationDuration,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
formattedMetadataNestedArr
) {
if (
selectedAggregationTypeArr === "Sum" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / sum
const [
formattedObservationsSumDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailySumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsSumDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationTypeArr === "Sum" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / sum
const [
formattedObservationsSumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlySumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsSumMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationTypeArr === "Maximum" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / maximum
const [
formattedObservationsMaximumDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailyMaximumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsMaximumDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationTypeArr === "Maximum" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / maximum
const [
formattedObservationsMaximumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlyMaximumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsMaximumMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationTypeArr === "Minimum" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / minimum
const [
formattedObservationsMinimumDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailyMinimumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsMinimumDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationTypeArr === "Minimum" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / minimum
const [
formattedObservationsMinimumMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlyMinimumObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsMinimumMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationTypeArr === "Average" &&
selectedAggregationDuration === "Daily"
) {
// Formatted observations and metadata for chart - daily / average
const [
formattedObservationsAverageDailyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatDailyAverageObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsAverageDailyNestedArr,
extractedFormattedDatastreamProperties
);
} else if (
selectedAggregationTypeArr === "Average" &&
selectedAggregationDuration === "Monthly"
) {
// Formatted observations and metadata for chart - monthly / average
const [
formattedObservationsAverageMonthlyNestedArr,
extractedFormattedDatastreamProperties,
] = calculateAndFormatMonthlyAverageObservations(
uniqueCalendarDatesNestedArr,
observationsAggregationNestedArr,
selectedSamplingRateAbbrev,
formattedMetadataNestedArr
);
drawColumnChartHighcharts(
formattedObservationsAverageMonthlyNestedArr,
extractedFormattedDatastreamProperties
);
}
};
export {
drawHeatmapBasedOnSelectedOptions,
drawScatterPlotFromChartSelection,
drawLineChartBasedOnSelectedAggregationOptions,
drawColumnChartBasedOnSelectedAggregationOptions,
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment