diff --git a/index.html b/index.html index c90ef6807d1c3f48a6acad9b4f327bf55df86278..caf70127f79b058c7bb59f149ba8dddf3b297b5d 100644 --- a/index.html +++ b/index.html @@ -28,12 +28,11 @@ > - - - + + + @@ -44,11 +43,7 @@ - - - - - +
-
+
diff --git a/package-lock.json b/package-lock.json index 78f7a1eb5834ebc6bc376e41483b8b33388f62f5..1602dd41137606aa799aaf5b7cb791a21a5af877 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,14 +18,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "requires": { - "follow-redirects": "^1.10.0" - } - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -160,11 +152,6 @@ "unpipe": "~1.0.0" } }, - "follow-redirects": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz", - "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==" - }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", diff --git a/package.json b/package.json index a8ba0bd4cc36be9770e25a3ef00ada2aea1d0099..d724ae7e1d7d9bfa13623f3b1c200ee6e7f9dba7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "author": "", "license": "ISC", "dependencies": { - "axios": "^0.21.1", "express": "^4.17.1" } } diff --git a/public/js/appChart.js b/public/js/appChart.js index 5c233ef06232be5b26b237c7a514d233d012d3cf..85c3007991fa5037d51ea0471bf02804e49d3f77 100644 --- a/public/js/appChart.js +++ b/public/js/appChart.js @@ -17,8 +17,14 @@ import { drawScatterPlotHighcharts, } from "./src_modules/chartScatterPlot.js"; +import { + formatAggregationResultForColumnChart, + drawColumnChartHighcharts, +} from "./src_modules/chartColumn.js"; + import { formatDatastreamMetadataForChart, + extractPropertiesFromFormattedDatastreamMetadata, getMetadataPlusObservationsFromSingleOrMultipleDatastreams, calculateVorlaufMinusRuecklaufTemperature, } from "./src_modules/fetchData.js"; @@ -34,7 +40,7 @@ import { * Test plotting of temp difference (dT) using heatmap */ const drawHeatmapHCUsingTempDifference = async function () { - const [tempDifferenceObsArrBau225, tempDifferenceMetadataBau225] = + const [observationsTemperatureDiff225Arr, metadataTemperatureDiff225Arr] = await calculateVorlaufMinusRuecklaufTemperature( BASE_URL, QUERY_PARAMS_COMBINED, @@ -42,9 +48,36 @@ const drawHeatmapHCUsingTempDifference = async function () { "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]; + + // Format the observations + const formattedTempDiff225NestedArr = + observationsTemperatureDiff225NestedArr.map((obsArr) => + formatSensorThingsApiResponseForHeatMap(obsArr) + ); + + // Format the metadata + const formattedTempDiff225MetadataNestedArr = + metadataTemperatureDiff225NestedArr.map((metadataObj) => + formatDatastreamMetadataForChart(metadataObj) + ); + + // Extract the formatted metadata properties + const extractedFormattedTempDiff225Properties = + extractPropertiesFromFormattedDatastreamMetadata( + formattedTempDiff225MetadataNestedArr + ); + + // First need to extract the formatted observations from the nested array + // Heatmap only needs one set of formatted observation values drawHeatMapHighcharts( - formatSensorThingsApiResponseForHeatMap(tempDifferenceObsArrBau225), - formatDatastreamMetadataForChart(tempDifferenceMetadataBau225) + ...formattedTempDiff225NestedArr, + extractedFormattedTempDiff225Properties ); }; @@ -52,7 +85,7 @@ const drawHeatmapHCUsingTempDifference = async function () { * Test drawing of scatter plot chart */ const drawScatterPlotHCTest2 = async function () { - const sensorsOfInterestArr = [ + const sensorsOfInterestNestedArr = [ ["225", "vl", "60min"], // ["125", "rl", "60min"], ["weather_station_521", "outside_temp", "60min"], @@ -62,26 +95,32 @@ const drawScatterPlotHCTest2 = async function () { await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( BASE_URL, QUERY_PARAMS_COMBINED, - sensorsOfInterestArr + sensorsOfInterestNestedArr ); // Extract the combined arrays for observations and metadata - const [observationsArr, metadataArr] = observationsPlusMetadata; + const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata; // Create formatted array(s) for observations // This function expects two arguments, these are unpacked using the spread operator - const formattedObsScatterPlotArr = - formatSensorThingsApiResponseForScatterPlot(...observationsArr); + const formattedObservationsArr = formatSensorThingsApiResponseForScatterPlot( + ...observationsNestedArr + ); // Create formatted array(s) for metadata - const formattedMetadataArr = metadataArr.map((metadata) => - formatDatastreamMetadataForChart(metadata) + const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) => + formatDatastreamMetadataForChart(metadataObj) ); - // This function expects three arguments, the second and third are unpacked using the spread operator + // Extract the formatted metadata properties + const extractedFormattedDatastreamProperties = + extractPropertiesFromFormattedDatastreamMetadata( + formattedMetadataNestedArr + ); + drawScatterPlotHighcharts( - formattedObsScatterPlotArr, - ...formattedMetadataArr + formattedObservationsArr, + extractedFormattedDatastreamProperties ); }; @@ -89,84 +128,132 @@ const drawScatterPlotHCTest2 = async function () { * Test drawing of line chart with multiple series */ const testLineChartMultipleSeries = async function () { - const sensorsOfInterestArr = [ + const sensorsOfInterestNestedArr = [ ["225", "vl", "60min"], ["125", "rl", "60min"], ["weather_station_521", "outside_temp", "60min"], ]; - const observationsPlusMetadata = + const observationsPlusMetadataArr = await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( BASE_URL, QUERY_PARAMS_COMBINED, - sensorsOfInterestArr + sensorsOfInterestNestedArr ); - // Extract the observations and metadata arrays - const [observationsArr, metadataArr] = observationsPlusMetadata; + // Extract the observations and metadata arrays of arrays + const [observationsNestedArr, metadataNestedArr] = + observationsPlusMetadataArr; - // Format the observations and metadata - const formattedObservationsArr = observationsArr.map((observations) => - formatSensorThingsApiResponseForLineChart(observations) + // Format the observations + const formattedObservationsNestedArr = observationsNestedArr.map( + (observationsArr) => + formatSensorThingsApiResponseForLineChart(observationsArr) ); - const formattedMetadataArr = metadataArr.map((metadata) => - formatDatastreamMetadataForChart(metadata) + // Format the metadata + const formattedMetadataNestedArr = metadataNestedArr.map((metadataArr) => + formatDatastreamMetadataForChart(metadataArr) ); - drawLineChartHighcharts(formattedObservationsArr, formattedMetadataArr); + // Extract the formatted metadata properties + const extractedFormattedDatastreamProperties = + extractPropertiesFromFormattedDatastreamMetadata( + formattedMetadataNestedArr + ); + + drawLineChartHighcharts( + formattedObservationsNestedArr, + extractedFormattedDatastreamProperties + ); }; /** - * Test aggregation of observations from a single datastream + * Test drawing of column chart using aggregation result */ -const testAggregationSum = async function () { - const sensorOfInterestNestedArr = [["225", "vl", "60min"]]; +const drawColumnChartMonthlySumTest = async function () { + const sensorsOfInterestNestedArr = [ + ["125", "vl", "60min"], + ["225", "vl", "60min"], + ]; const observationsPlusMetadata = await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( BASE_URL, QUERY_PARAMS_COMBINED, - sensorOfInterestNestedArr + sensorsOfInterestNestedArr ); // Extract the observations and metadata for each sensor // Array elements in same order as input array - const [[obsSensorOneArr], [metadataSensorOne]] = observationsPlusMetadata; + const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata; // Unique calendar dates - const uniqueCalendarDates = - extractUniqueCalendarDatesFromTimestamp(obsSensorOneArr); + const uniqueCalendarDatesNestedArr = observationsNestedArr.map( + (observationsArr) => + extractUniqueCalendarDatesFromTimestamp(observationsArr) + ); // Unique calendar months - const uniqueCalendarMonths = - extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDates); + const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map( + (uniqueCalendarDatesArr) => + extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr) + ); // Calculate sum of values of observations - daily - const observationsBau225VLSumDaily = uniqueCalendarDates.map((calendarDate) => - calculateSumOfObservationValuesWithinDatesInterval( - obsSensorOneArr, - "60 min", - calendarDate, - calendarDate - ) + // Note the two nested `map` methods + const observationsSumDailyNestedArr = uniqueCalendarDatesNestedArr.map( + (uniqueCalendarDatesArr, i) => + uniqueCalendarDatesArr.map((uniqueCalendarDate) => + calculateSumOfObservationValuesWithinDatesInterval( + observationsNestedArr[i], + "60 min", + uniqueCalendarDate, + uniqueCalendarDate + ) + ) ); // Calculate sum of values of observations - monthly - const observationsBau225VLSumMonthly = uniqueCalendarMonths.map( - (calendarMonth) => - calculateSumOfObservationValuesWithinMonthInterval( - obsSensorOneArr, - "60 min", - calendarMonth + // Note the two nested `map` methods + const observationsSumMonthlyNestedArr = uniqueCalendarMonthsNestedArr.map( + (uniqueCalendarMonthsArr, i) => + uniqueCalendarMonthsArr.map((uniqueCalendarMonth) => + calculateSumOfObservationValuesWithinMonthInterval( + observationsNestedArr[i], + "60 min", + uniqueCalendarMonth + ) ) ); - console.log(observationsBau225VLSumDaily); - console.log(observationsBau225VLSumMonthly); + // Format the observations + const formattedObservationsNestedArr = 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 + ); + + drawColumnChartHighcharts( + formattedObservationsNestedArr, + extractedFormattedDatastreamProperties + ); }; // drawScatterPlotHCTest2(); // drawHeatmapHCUsingTempDifference(); -// testLineChartMultipleSeries() -// testAggregationSum(); +// testLineChartMultipleSeries(); +// drawColumnChartMonthlySumTest(); diff --git a/public/js/dropDownList.js b/public/js/dropDownList.js index 2addbdedc479e248f396cae29220d1498a74e7bf..9bda35e9b3684d4faf9fe30b57a50a2d459636f3 100644 --- a/public/js/dropDownList.js +++ b/public/js/dropDownList.js @@ -4,6 +4,7 @@ import { BASE_URL, QUERY_PARAMS_COMBINED } from "./src_modules/createUrl.js"; import { formatDatastreamMetadataForChart, + extractPropertiesFromFormattedDatastreamMetadata, getMetadataPlusObservationsFromSingleOrMultipleDatastreams, } from "./src_modules/fetchData.js"; @@ -272,28 +273,41 @@ const selectChartTypeFromDropDown = async function () { ); // Extract the combined arrays for observations and metadata - const [observationsArr, metadataArr] = observationsPlusMetadata; + const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata; // Create formatted array(s) for observations - line chart - const formattedObsLineChartArr = observationsArr.map((observations) => - formatSensorThingsApiResponseForLineChart(observations) + const formattedObsLineChartArr = observationsNestedArr.map( + (observationsArr) => + formatSensorThingsApiResponseForLineChart(observationsArr) ); // Create formatted array(s) for observations - heatmap - const formattedObsHeatMapArr = observationsArr.map((observations) => - formatSensorThingsApiResponseForHeatMap(observations) + const formattedObsHeatMapArr = observationsNestedArr.map( + (observationsArr) => + formatSensorThingsApiResponseForHeatMap(observationsArr) ); // Create formatted array(s) for metadata - same for both chart types - const formattedMetadataArr = metadataArr.map((metadata) => - formatDatastreamMetadataForChart(metadata) + const formattedMetadataArr = metadataNestedArr.map((metadataObj) => + formatDatastreamMetadataForChart(metadataObj) ); + // Extract the formatted metadata properties + const extractedFormattedDatastreamProperties = + extractPropertiesFromFormattedDatastreamMetadata(formattedMetadataArr); + if (selectedChartType === "Line") { - drawLineChartHighcharts(formattedObsLineChartArr, formattedMetadataArr); + drawLineChartHighcharts( + formattedObsLineChartArr, + extractedFormattedDatastreamProperties + ); } else if (selectedChartType === "Heatmap") { - // First need to extract the nested arrays for the formatted observations and metadata - drawHeatMapHighcharts(...formattedObsHeatMapArr, ...formattedMetadataArr); + // First need to extract the formatted observations from the nested array + // Heatmap only needs one set of formatted observation values + drawHeatMapHighcharts( + ...formattedObsHeatMapArr, + extractedFormattedDatastreamProperties + ); } } catch (err) { console.error(err); diff --git a/public/js/src_modules/chartColumn.js b/public/js/src_modules/chartColumn.js new file mode 100644 index 0000000000000000000000000000000000000000..51f322424902803cbfbf8ad2bf3dd66d427e3bb2 --- /dev/null +++ b/public/js/src_modules/chartColumn.js @@ -0,0 +1,130 @@ +/** + * Format a computed aggregation result to make it suitable for a column chart + * @param {Array} calendarDatesMonthsStrArr An array of unique calendar dates strings (in "YYYY-MM-DD" fromat) or unique calendar months strings (in "YYYY-MM" format) + * @param {Array} aggregatedValuesArr An array of aggregated values + * @returns {Array} An array of formatted aggregation values suitable for use in a column chart + */ +const formatAggregationResultForColumnChart = function ( + calendarDatesMonthsStrArr, + aggregatedValuesArr +) { + if (!calendarDatesMonthsStrArr || !aggregatedValuesArr) return; + + // Create an array of Unix timestamp strings + const timestampsArr = calendarDatesMonthsStrArr.map((calendarStr) => + new Date(calendarStr).getTime() + ); + + // Combine timestamp and value pairs + // The timestamps array and values array have same lengths, use one for looping + return timestampsArr.map((timestamp, i) => [ + timestamp, + aggregatedValuesArr[i], + ]); +}; + +/** + * Creates an options object for each series drawn in a column chart + * @param {Array} formattedAggregatedResultForColumnChart An array of formatted aggregated result array(s) from one or more datastreams + * @param {Array} phenomenonNamesArr An array of phenomenon name(s) + * @param {Array} phenomenonSymbolsArr An array of phenomenon symbol(s) + * @returns {Array} An array made up of series options object(s) + */ +const createSeriesOptionsForColumnChart = function ( + formattedAggregatedResultForColumnChart, + phenomenonNamesArr, + phenomenonSymbolsArr +) { + // Create an array of seriesOptions objects + // Assumes that the observation array of arrays, phenomenon names array and phenomenon symbols array are of equal length + // Use one of the arrays for looping + return formattedAggregatedResultForColumnChart.map( + (formattedAggResArray, i) => { + return { + name: `${phenomenonNamesArr[i]} (${phenomenonSymbolsArr[i]})`, + data: formattedAggResArray, + turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release + }; + } + ); +}; + +/** + * Draw a column chart using Highcharts library + * @param {Array} formattedAggResultArraysForColumnChart An array made up of formatted aggregated result array(s) suitable for use in a column chart + * @param {Object} extractedFormattedDatastreamProperties An object that contains arrays of formatted Datastream properties + * @returns {undefined} + */ +const drawColumnChartHighcharts = function ( + formattedAggResultArraysForColumnChart, + extractedFormattedDatastreamProperties +) { + // Arrays of datastream properties + const { + datastreamNamesArr, + phenomenonNamesArr, + unitOfMeasurementSymbolsArr, + } = extractedFormattedDatastreamProperties; + + const seriesOptionsArr = createSeriesOptionsForColumnChart( + formattedAggResultArraysForColumnChart, + phenomenonNamesArr, + unitOfMeasurementSymbolsArr + ); + + Highcharts.chart("chart-column", { + chart: { + type: "column", + zoomType: "x", + }, + + title: { + text: "Monthly Average Rainfall", + }, + + subtitle: { + text: "Source: WorldClimate.com", + }, + + xAxis: { + type: "datetime", + crosshair: true, + }, + + yAxis: { + min: 0, + title: { + text: "Rainfall (mm)", + }, + }, + + tooltip: { + headerFormat: ` + {point.key} + + `, + pointFormat: ` + + + + + `, + footerFormat: ` +
{series.name}: {point.y:.1f} mm
+ `, + shared: true, + useHTML: true, + }, + + plotOptions: { + column: { + pointPadding: 0.2, + borderWidth: 0, + }, + }, + + series: seriesOptionsArr, + }); +}; + +export { formatAggregationResultForColumnChart, drawColumnChartHighcharts }; diff --git a/public/js/src_modules/chartHeatmap.js b/public/js/src_modules/chartHeatmap.js index c1891121641946180a72179421c28541118c657f..5d15d4f6a7e7a0c85ca9d32dd109cd1b8b7b8b04 100644 --- a/public/js/src_modules/chartHeatmap.js +++ b/public/js/src_modules/chartHeatmap.js @@ -8,7 +8,7 @@ const formatSensorThingsApiResponseForHeatMap = function (obsArray) { if (!obsArray) return; - const dataSTAFormatted = obsArray.map((obs) => { + return obsArray.map((obs) => { // Get the date/time string; first element in input array; remove trailing "Z" const obsDateTimeInput = obs[0].slice(0, -1); // Get the "date" part of an observation @@ -24,8 +24,6 @@ const formatSensorThingsApiResponseForHeatMap = function (obsArray) { const value = obs[1]; return [timestamp, hourOfDay, value]; }); - - return dataSTAFormatted; }; /** @@ -53,19 +51,25 @@ const calculateMinMaxValuesForHeatmapColorAxis = function ( /** * Draw a heatmap using Highcharts library * @param {Array} formattedObsArrayForHeatmap Response from SensorThings API formatted for use in a heatmap - * @param {Object} formattedDatastreamMetadata Object containing Datastream metadata + * @param {Object} extractedFormattedDatastreamProperties An object that contains arrays of formatted Datastream properties * @returns {undefined} undefined */ const drawHeatMapHighcharts = function ( formattedObsArrayForHeatmap, - formattedDatastreamMetadata + extractedFormattedDatastreamProperties ) { + // Arrays of datastream properties const { - datastreamDescription: DATASTREAM_DESCRIPTION, - datastreamName: DATASTREAM_NAME, - phenomenonName: PHENOMENON_NAME, - unitOfMeasurementSymbol: PHENOMENON_SYMBOL, - } = formattedDatastreamMetadata; + datastreamDescriptionsArr, + datastreamNamesArr, + phenomenonNamesArr, + unitOfMeasurementSymbolsArr, + } = extractedFormattedDatastreamProperties; + + const [DATASTREAM_DESCRIPTION] = datastreamDescriptionsArr; + const [DATASTREAM_NAME] = datastreamNamesArr; + const [PHENOMENON_NAME] = phenomenonNamesArr; + const [PHENOMENON_SYMBOL] = unitOfMeasurementSymbolsArr; const { minObsValue: MINIMUM_VALUE_COLOR_AXIS, diff --git a/public/js/src_modules/chartLine.js b/public/js/src_modules/chartLine.js index 8a3d3ba93358c227818f1119173c6ddb1b12c59b..8c8e5bfc0e80d5217ffc56870fc1b4e71517316e 100644 --- a/public/js/src_modules/chartLine.js +++ b/public/js/src_modules/chartLine.js @@ -8,46 +8,11 @@ const formatSensorThingsApiResponseForLineChart = function (obsArray) { if (!obsArray) return; - const dataSTAFormatted = obsArray.map((result) => { + return obsArray.map((result) => { const timestampObs = new Date(result[0].slice(0, -1)).getTime(); // slice() removes trailing "Z" character in timestamp const valueObs = result[1]; return [timestampObs, valueObs]; }); - - return dataSTAFormatted; -}; - -/** - * Extract the properties that make up the formatted datastream metadata object(s) - * @param {Array} formattedDatastreamsMetadataArr An array of formatted metadata object(s) from one or more datastreams - * @returns {Object} An object that contains array(s) of formatted datastream metadata properties - */ -const extractPropertiesFromDatastreamMetadata = function ( - formattedDatastreamsMetadataArr -) { - // Create arrays from the properties of the formatted datastream metadata - const datastreamDescriptionsArr = formattedDatastreamsMetadataArr.map( - (datastreamMetadata) => datastreamMetadata.datastreamDescription - ); - - const datastreamNamesArr = formattedDatastreamsMetadataArr.map( - (datastreamMetadata) => datastreamMetadata.datastreamName - ); - - const phenomenonNamesArr = formattedDatastreamsMetadataArr.map( - (datastreamMetadata) => datastreamMetadata.phenomenonName - ); - - const unitOfMeasurementSymbolsArr = formattedDatastreamsMetadataArr.map( - (datastreamMetadata) => datastreamMetadata.unitOfMeasurementSymbol - ); - - return { - datastreamDescriptionsArr, - datastreamNamesArr, - phenomenonNamesArr, - unitOfMeasurementSymbolsArr, - }; }; /** @@ -104,19 +69,19 @@ const createSeriesOptionsForLineChart = function ( /** * Draw a line chart using Highcharts library * @param {Array} formattedObsArraysForLineChart An array made up of formatted observation array(s) suitable for use in a line chart - * @param {Object} formattedDatastreamMetadataArr An array made up of object(s) containing Datastream metadata + * @param {Object} extractedFormattedDatastreamPropertiesArr An object that contains arrays of formatted Datastream properties * @returns {undefined} undefined */ const drawLineChartHighcharts = function ( formattedObsArraysForLineChart, - formattedDatastreamMetadataArr + extractedFormattedDatastreamProperties ) { // Arrays of datastream properties const { datastreamNamesArr, phenomenonNamesArr, unitOfMeasurementSymbolsArr, - } = extractPropertiesFromDatastreamMetadata(formattedDatastreamMetadataArr); + } = extractedFormattedDatastreamProperties; // Create the array of series options object(s) const seriesOptionsArr = createSeriesOptionsForLineChart( diff --git a/public/js/src_modules/chartScatterPlot.js b/public/js/src_modules/chartScatterPlot.js index 00874c9caddbb20db087344e61a3cbe4659e29f1..eb88b9488b31c6d81924cb5e2eb08e734a3cb59a 100644 --- a/public/js/src_modules/chartScatterPlot.js +++ b/public/js/src_modules/chartScatterPlot.js @@ -183,11 +183,7 @@ const createCombinedObservationValues = function (obsArrayOne, obsArrayTwo) { const obsValuesTwo = obsArrayTwo.map((result) => result[1]); // Since the arrays are of equal length, we need only use one of the arrays for looping - const obsValuesOnePlusTwo = obsValuesOne.map((obsValOne, i) => { - return [obsValOne, obsValuesTwo[i]]; - }); - - return obsValuesOnePlusTwo; + return obsValuesOne.map((obsValOne, i) => [obsValOne, obsValuesTwo[i]]); }; /** @@ -218,28 +214,29 @@ const formatSensorThingsApiResponseForScatterPlot = function ( /** * Draw a scatter plot using Highcharts library * @param {Array} formattedObsArrayForSeriesOnePlusSeriesTwo Response from SensorThings API formatted for use in a scatter plot - * @param {Object} formattedDatastreamMetadataSeriesOne Object containing Datastream metadata for the first chart series - * @param {Object} formattedDatastreamMetadataSeriesTwo Object containing Datastream metadata for the second chart series + * @param {Object} extractedFormattedDatastreamProperties An object that contains arrays of formatted Datastream properties * @returns {undefined} */ const drawScatterPlotHighcharts = function ( formattedObsArrayForSeriesOnePlusSeriesTwo, - formattedDatastreamMetadataSeriesOne, - formattedDatastreamMetadataSeriesTwo + extractedFormattedDatastreamProperties ) { + // Arrays of datastream properties const { - datastreamDescription: DATASTREAM_DESCRIPTION_SERIES_1, - datastreamName: DATASTREAM_NAME_SERIES_1, - phenomenonName: PHENOMENON_NAME_SERIES_1, - unitOfMeasurementSymbol: PHENOMENON_SYMBOL_SERIES_1, - } = formattedDatastreamMetadataSeriesOne; - - const { - datastreamDescription: DATASTREAM_DESCRIPTION_SERIES_2, - datastreamName: DATASTREAM_NAME_SERIES_2, - phenomenonName: PHENOMENON_NAME_SERIES_2, - unitOfMeasurementSymbol: PHENOMENON_SYMBOL_SERIES_2, - } = formattedDatastreamMetadataSeriesTwo; + datastreamDescriptionsArr, + datastreamNamesArr, + phenomenonNamesArr, + unitOfMeasurementSymbolsArr, + } = extractedFormattedDatastreamProperties; + + const [DATASTREAM_DESCRIPTION_SERIES_1, DATASTREAM_DESCRIPTION_SERIES_2] = + datastreamDescriptionsArr; + const [DATASTREAM_NAME_SERIES_1, DATASTREAM_NAME_SERIES_2] = + datastreamNamesArr; + const [PHENOMENON_NAME_SERIES_1, PHENOMENON_NAME_SERIES_2] = + phenomenonNamesArr; + const [PHENOMENON_SYMBOL_SERIES_1, PHENOMENON_SYMBOL_SERIES_2] = + unitOfMeasurementSymbolsArr; // Order of axes // Y-Axis -- Series 2 diff --git a/public/js/src_modules/fetchData.js b/public/js/src_modules/fetchData.js index 5fedf89d90ca5c4b5d026c7ed6e6dd181306f69d..64ba329a1e43a7fb60b4099eeb315705f13287ca 100644 --- a/public/js/src_modules/fetchData.js +++ b/public/js/src_modules/fetchData.js @@ -233,6 +233,39 @@ const formatDatastreamMetadataForChart = function (datastreamMetadata) { }; }; +/** + * Extract the properties that make up the formatted datastream metadata object(s) + * @param {Array} formattedDatastreamsMetadataArr An array of formatted metadata object(s) from one or more datastreams + * @returns {Object} An object that contains array(s) of formatted datastream metadata properties + */ +const extractPropertiesFromFormattedDatastreamMetadata = function ( + formattedDatastreamsMetadataArr +) { + // Create arrays from the properties of the formatted datastream metadata + const datastreamDescriptionsArr = formattedDatastreamsMetadataArr.map( + (datastreamMetadata) => datastreamMetadata.datastreamDescription + ); + + const datastreamNamesArr = formattedDatastreamsMetadataArr.map( + (datastreamMetadata) => datastreamMetadata.datastreamName + ); + + const phenomenonNamesArr = formattedDatastreamsMetadataArr.map( + (datastreamMetadata) => datastreamMetadata.phenomenonName + ); + + const unitOfMeasurementSymbolsArr = formattedDatastreamsMetadataArr.map( + (datastreamMetadata) => datastreamMetadata.unitOfMeasurementSymbol + ); + + return { + datastreamDescriptionsArr, + datastreamNamesArr, + phenomenonNamesArr, + unitOfMeasurementSymbolsArr, + }; +}; + /** * Traverses all the pages that make up the response from a SensorThingsAPI instance. The link to the next page, if present, is denoted by the presence of a "@iot.nextLink" property in the response object. This function concatenates all the values so that the complete results are returned in one array. * @async @@ -332,18 +365,18 @@ const getObservationsFromMultipleDatastreams = async function ( * Retrieve the metadata from a single Datastream or multiple Datastreams and the Observations corresponding to the Datastream(s) * @param {String} baseUrl Base URL of the STA server * @param {Object} urlParamObj The URL parameters to be sent together with the GET request - * @param {Array} bldgSensorSamplingRateArr A N*1 array (where N >= 1) containing a nested array of buildings, sensors & sampling rates as strings, i.e. [["101", "rl", "15min"]] or [["101", "rl", "15min"], ["102", "vl", "60min"]] or [["101", "rl", "15min"], ["102", "vl", "60min"], ["225", "vl", "60min"]], etc + * @param {Array} bldgSensorSamplingRateNestedArr A N*1 array (where N >= 1) containing a nested array of buildings, sensors & sampling rates as strings, i.e. [["101", "rl", "15min"]] or [["101", "rl", "15min"], ["102", "vl", "60min"]] or [["101", "rl", "15min"], ["102", "vl", "60min"], ["225", "vl", "60min"]], etc * @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 */ const getMetadataPlusObservationsFromSingleOrMultipleDatastreams = - async function (baseUrl, urlParamObj, bldgSensorSamplingRateArr) { + async function (baseUrl, urlParamObj, bldgSensorSamplingRateNestedArr) { try { - if (!bldgSensorSamplingRateArr) return; + if (!bldgSensorSamplingRateNestedArr) return; // Datastreams IDs - const datastreamsIdsArr = bldgSensorSamplingRateArr.map( - (bldgSensorSamplingRate) => - getDatastreamIdFromBuildingNumber(...bldgSensorSamplingRate) + const datastreamsIdsArr = bldgSensorSamplingRateNestedArr.map( + (bldgSensorSamplingRateArr) => + getDatastreamIdFromBuildingNumber(...bldgSensorSamplingRateArr) ); // Observations URLs @@ -394,7 +427,7 @@ const calculateVorlaufMinusRuecklaufTemperature = async function ( samplingRate ) { try { - const bldgSensorSamplingRateArr = [ + const bldgSensorSamplingRateNestedArr = [ [buildingId, "vl", samplingRate], [buildingId, "rl", samplingRate], ]; @@ -406,23 +439,31 @@ const calculateVorlaufMinusRuecklaufTemperature = async function ( await getMetadataPlusObservationsFromSingleOrMultipleDatastreams( baseUrl, urlParams, - bldgSensorSamplingRateArr + bldgSensorSamplingRateNestedArr ); // Extract Vorlauf temperature, Ruecklauf temperature and metadata - const [[vorlaufTemp, ruecklaufTemp], [metadataVorlauf, metadataRuecklauf]] = - observationsPlusMetadata; + const [ + [vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr], + [metadataVorlauf, metadataRuecklauf], + ] = observationsPlusMetadata; // Extract the temperature values - const vorlaufTempValues = vorlaufTemp.map((obs) => obs[1]); - const ruecklaufTempValues = ruecklaufTemp.map((obs) => obs[1]); + const vorlaufTemperatureValues = vorlaufTemperatureObsArr.map( + (vlTempObs) => vlTempObs[1] + ); + const ruecklaufTemperatureValues = ruecklaufTemperatureObsArr.map( + (rlTempObs) => rlTempObs[1] + ); // The arrays have equal length, we need only use one of them for looping // Resulting array contains the following pairs (timestamp + dT) - const vorlaufMinusRuecklaufTemp = vorlaufTemp.map((obs, i) => [ - obs[0], - vorlaufTempValues[i] - ruecklaufTempValues[i], - ]); + const vorlaufMinusRuecklaufTemperatureObs = vorlaufTemperatureObsArr.map( + (vlTempObs, i) => [ + vlTempObs[0], // timestamp + vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i], + ] + ); // From Vorlauf metadata, extract `name` and `unitOfMeasurement` const { @@ -459,7 +500,7 @@ const calculateVorlaufMinusRuecklaufTemperature = async function ( const unitOfMeasurement = unitOfMeasurementVorlauf; return [ - vorlaufMinusRuecklaufTemp, + vorlaufMinusRuecklaufTemperatureObs, { description, name, @@ -473,6 +514,7 @@ const calculateVorlaufMinusRuecklaufTemperature = async function ( export { formatDatastreamMetadataForChart, + extractPropertiesFromFormattedDatastreamMetadata, getMetadataPlusObservationsFromSingleOrMultipleDatastreams, calculateVorlaufMinusRuecklaufTemperature, };