Commit 7196482d authored by Pithon Kabiro's avatar Pithon Kabiro
Browse files

Merge branch 'wip_chart-column' into 'master'

Add functionality to draw multi-series column chart

See merge request !7
parents 1c71824b 786a5e18
......@@ -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,
};
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