Commit 786a5e18 authored by Pithon Kabiro's avatar Pithon Kabiro
Browse files

Draw column chart

It is assumed that this chart type will be used for visualizing
aggregated results
parent 1c71824b
Showing with 391 additions and 171 deletions
+391 -171
......@@ -28,12 +28,11 @@
></script>
<!-- Axios -->
<!-- <script src="./node_modules/axios/dist/axios.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- Higcharts lib -->
<!-- Does not play well with `Highstock`; see: https://www.highcharts.com/errors/16/
<script src="https://code.highcharts.com/highcharts.js"></script> -->
<!-- Higcharts -->
<!-- `highcharts.js` does not play well with `highstock.js`; see: https://www.highcharts.com/errors/16/-->
<!-- <script src="https://code.highcharts.com/highcharts.js"></script> -->
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/data.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
......@@ -44,11 +43,7 @@
<script src="https://code.highcharts.com/modules/boost.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
<!-- Apexcharts lib -->
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<link rel="stylesheet" href="css/styles.css" />
<!-- Cesium lib -->
<!-- Cesium -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.48/Build/Cesium/Cesium.js"></script>
<link
href="https://cesium.com/downloads/cesiumjs/releases/1.48/Build/Cesium/Widgets/widgets.css"
......@@ -230,7 +225,7 @@
Bar Chart Example
</div>
<div class="card-body">
<div id="chart-bar" width="100%" height="40"></div>
<div id="chart-column" width="100%" height="40"></div>
</div>
</div>
</div>
......
......@@ -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",
......
......@@ -9,7 +9,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.21.1",
"express": "^4.17.1"
}
}
......@@ -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();
......@@ -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);
......
/**
* 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: `
<span style="font-size:10px">{point.key}</span>
<table>
`,
pointFormat: `
<tr>
<td style="color:{series.color};padding:0">{series.name}: </td>
<td style="padding:0"><b>{point.y:.1f} mm</b></td>
</tr>
`,
footerFormat: `
</table>
`,
shared: true,
useHTML: true,
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0,
},
},
series: seriesOptionsArr,
});
};
export { formatAggregationResultForColumnChart, drawColumnChartHighcharts };
......@@ -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,
......
......@@ -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(
......
......@@ -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
......
......@@ -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,
};
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment