"use strict";
const BASE_URL = "http://193.196.39.91:8080/frost-icity-tp31/v1.1";
/**
* Retrieve the datastream ID that corresponds to a particular building
* @param {Number | String} buildingNumber Integer representing the building ID
* @param {String} phenomenon String representing the phenomenon of interest
* @param {String} samplingRate String representing the sampling rate of the observations
* @returns {Number} Datastream corresponding to the input building
*/
const getDatastreamIdFromBuildingNumber = function (
buildingNumber,
phenomenon,
samplingRate
) {
const buildingToDatastreamMapping = {
101: {
vl: { "15min": "69", "60min": "75" },
rl: { "15min": "81", "60min": "87" },
// These Datastreams do not yet have Observations
// flow: { "15min": "93", "60min": "99" },
// power: { "15min": "105", "60min": "111" },
// energy: { "15min": "117", "60min": "123" },
// energy_verb: { "15min": "129", "60min": "135" },
},
102: {
vl: { "15min": "70", "60min": "76" },
rl: { "15min": "82", "60min": "88" },
// These Datastreams do not yet have Observations
// flow: { "15min": "94", "60min": "100" },
// power: { "15min": "106", "60min": "112" },
// energy: { "15min": "118", "60min": "124" },
// energy_verb: { "15min": "130", "60min": "136" },
},
107: {
vl: { "15min": "71", "60min": "77" },
rl: { "15min": "83", "60min": "89" },
// These Datastreams do not yet have Observations
// flow: { "15min": "95", "60min": "101" },
// power: { "15min": "107", "60min": "113" },
// energy: { "15min": "119", "60min": "125" },
// energy_verb: { "15min": "131", "60min": "137" },
},
"112, 118": {
vl: { "15min": "72", "60min": "78" },
rl: { "15min": "84", "60min": "90" },
// These Datastreams do not yet have Observations
// flow: { "15min": "96", "60min": "102" },
// power: { "15min": "108", "60min": "114" },
// energy: { "15min": "120", "60min": "126" },
// energy_verb: { "15min": "132", "60min": "138" },
},
125: {
vl: { "15min": "73", "60min": "79" },
rl: { "15min": "85", "60min": "91" },
// These Datastreams do not yet have Observations
// flow: { "15min": "97", "60min": "103" },
// power: { "15min": "109", "60min": "115" },
// energy: { "15min": "121", "60min": "127" },
// energy_verb: { "15min": "133", "60min": "139" },
},
225: {
vl: { "15min": "74", "60min": "80" },
rl: { "15min": "86", "60min": "92" },
flow: { "15min": "98", "60min": "104" },
power: { "15min": "110", "60min": "116" },
energy: { "15min": "122", "60min": "128" },
energy_verb: { "15min": "134", "60min": "140" },
},
};
if (
buildingToDatastreamMapping?.[buildingNumber]?.[phenomenon]?.[
samplingRate
] === undefined
)
return;
const datastreamIdMatched = Number(
buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate]
);
return datastreamIdMatched;
};
/**
* Create URL to fetch the details of single Datastream
* @param {String} baseUrl Base URL of the STA server
* @param {Number} datastreamID Integer representing the Datastream ID
* @returns {String} URL string for fetching a single Datastream
*/
const getDatastreamUrl = function (baseUrl, datastreamID) {
if (!datastreamID) return;
const fullDatastreamURL = `${baseUrl}/Datastreams(${datastreamID})`;
return fullDatastreamURL;
};
/**
* Create URL to fetch Observations
* @param {String} baseUrl Base URL of the STA server
* @param {Number} datastreamID Integer representing the Datastream ID
* @returns {String} URL string for fetching Observations
*/
const getObservationsUrl = function (baseUrl, datastreamID) {
if (!datastreamID) return;
const fullObservationsURL = `${baseUrl}/Datastreams(${datastreamID})/Observations`;
return fullObservationsURL;
};
/**
* Create a temporal filter string for the fetched Observations
* @param {String} dateStart Start date in YYYY-MM-DD format
* @param {String} dateStop Stop date in YYYY-MM-DD format
* @returns {String} Temporal filter string
*/
const createTemporalFilterString = function (dateStart, dateStop) {
if (!dateStart || !dateStop) return;
const filterString = `resultTime ge ${dateStart}T00:00:00.000Z and resultTime le ${dateStop}T00:00:00.000Z`;
return filterString;
};
// const BASE_URL_OBSERVATIONS = getObservationsUrl(80);
const QUERY_PARAM_RESULT_FORMAT = "dataArray";
const QUERY_PARAM_ORDER_BY = "phenomenonTime asc";
const QUERY_PARAM_FILTER = createTemporalFilterString(
"2020-01-01",
"2021-01-01"
);
const QUERY_PARAM_SELECT = "result,phenomenonTime";
const QUERY_PARAMS_COMBINED = {
"$resultFormat": QUERY_PARAM_RESULT_FORMAT,
"$orderBy": QUERY_PARAM_ORDER_BY,
"$filter": QUERY_PARAM_FILTER,
"$select": QUERY_PARAM_SELECT,
};
/**
* Perform a GET request using the Axios library
* @param {String} urlObservations A URL that fetches Observations from an STA instance
* @param {Object} urlParamObj The URL parameters to be sent together with the GET request
* @returns {Promise} A promise that contains the first page of results when fulfilled
*/
const axiosGetRequest = function (urlObservations, urlParamObj) {
return axios.get(urlObservations, {
params: urlParamObj,
});
};
/**
* Retrieve the metadata for a single datastream
* @async
* @param {String} urlDatastream A URL that fetches a Datastream from an STA instance
* @returns {Promise} A promise that contains a metadata object for a Datastream when fulfilled
*/
const getDatastreamMetadata = async function (urlDatastream) {
try {
// Extract properties of interest
const {
data: { description, name, unitOfMeasurement },
} = await axiosGetRequest(urlDatastream);
return { description, name, unitOfMeasurement };
} catch (err) {
console.error(err);
}
};
/**
* Format the response containing a Datastream's metadata from Sensorthings API
* @param {Object} datastreamMetadata An object containing a Datastream's metadata
* @returns {Object} An object containing the formatted metadata that is suitable for use in a line chart or heatmap
*/
const formatDatastreamMetadataForChart = function (datastreamMetadata) {
const {
description: datastreamDescription,
name: datastreamName,
unitOfMeasurement,
} = datastreamMetadata;
// Extract phenomenon name from Datastream name
const regex = /\/ (.*) DS/;
const phenomenonName = datastreamName.match(regex)[1]; // use second element in array
// Match the unitOfMeasurement's string representation of a symbol
// to an actual symbol, where necessary
const unitOfMeasurementSymbol = (() => {
if (unitOfMeasurement.symbol === "degC") {
return "℃";
} else if (unitOfMeasurement.symbol === "m3/h") {
return "m3/h";
} else {
return unitOfMeasurement.symbol;
}
})();
return {
datastreamDescription,
datastreamName,
phenomenonName,
unitOfMeasurementSymbol,
};
};
/**
* Format the response from SensorThings API to make it suitable for heatmap
* @param {Array} obsArray Response from SensorThings API as array
* @returns {Array} Array of formatted observations suitable for use in a heatmap
*/
const formatSTAResponseForHeatMap = function (obsArray) {
if (!obsArray) return;
const dataSTAFormatted = 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
const obsDateInput = obs[0].slice(0, 10);
// Create Date objects
const obsDateTime = new Date(obsDateTimeInput);
const obsDate = new Date(obsDateInput);
// x-axis -> timestamp; will be the same for observations from the same date
const timestamp = Date.parse(obsDate);
// y-axis -> hourOfDay
const hourOfDay = obsDateTime.getHours();
// value -> the observation's value; second element in input array
const value = obs[1];
return [timestamp, hourOfDay, value];
});
return dataSTAFormatted;
};
/**
* 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
* @returns {undefined} undefined
*/
const drawHeatMapHC = function (
formattedObsArrayForHeatmap,
formattedDatastreamMetadata
) {
const {
datastreamDescription: DATASTREAM_DESCRIPTION,
datastreamName: DATASTREAM_NAME,
phenomenonName: PHENOMENON_NAME,
unitOfMeasurementSymbol: PHENOMENON_SYMBOL,
} = formattedDatastreamMetadata;
// Function returns the min and max observation values
const {
minObsValue: MINIMUM_VALUE_COLOR_AXIS,
maxObsValue: MAXIMUM_VALUE_COLOR_AXIS,
} = (() => {
// The observation value is the third element in array
const obsValueArr = formattedObsArrayForHeatmap.map((obs) => obs[2]);
// Extract integer part
const minValue = Math.trunc(Math.min(...obsValueArr));
const maxValue = Math.trunc(Math.max(...obsValueArr));
// Calculate the closest multiple of 5
const minObsValue = minValue - (minValue % 5);
const maxObsValue = maxValue + (5 - (maxValue % 5));
return { minObsValue, maxObsValue };
})();
Highcharts.chart("chart-heatmap", {
chart: {
type: "heatmap",
zoomType: "x",
},
boost: {
useGPUTranslations: true,
},
title: {
text: DATASTREAM_DESCRIPTION,
align: "left",
x: 40,
},
subtitle: {
text: DATASTREAM_NAME,
align: "left",
x: 40,
},
xAxis: {
type: "datetime",
// min: Date.UTC(2017, 0, 1),
// max: Date.UTC(2017, 11, 31, 23, 59, 59),
labels: {
align: "left",
x: 5,
y: 14,
format: "{value:%B}", // long month
},
showLastLabel: false,
tickLength: 16,
},
yAxis: {
title: {
text: null,
},
labels: {
format: "{value}:00",
},
minPadding: 0,
maxPadding: 0,
startOnTick: false,
endOnTick: false,
tickPositions: [0, 3, 6, 9, 12, 15, 18, 21, 24],
tickWidth: 1,
min: 0,
max: 23,
reversed: true,
},
colorAxis: {
stops: [
[0, "#3060cf"],
[0.5, "#fffbbc"],
[0.9, "#c4463a"],
[1, "#c4463a"],
],
min: MINIMUM_VALUE_COLOR_AXIS,
max: MAXIMUM_VALUE_COLOR_AXIS,
startOnTick: false,
endOnTick: false,
labels: {
// format: "{value}℃",
format: `{value}${PHENOMENON_SYMBOL}`,
},
},
series: [
{
data: formattedObsArrayForHeatmap,
boostThreshold: 100,
borderWidth: 0,
nullColor: "#525252",
colsize: 24 * 36e5, // one day
tooltip: {
headerFormat: `${PHENOMENON_NAME}
`,
valueDecimals: 2,
pointFormat:
// "{point.x:%e %b, %Y} {point.y}:00: {point.value} ℃",
`{point.x:%e %b, %Y} {point.y}:00: {point.value} ${PHENOMENON_SYMBOL}`,
nullFormat: `{point.x:%e %b, %Y} {point.y}:00: null`,
},
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
},
],
});
};
/**
* Convert the observations' phenomenonTime from an ISO 8601 string to Unix epoch
* @param {Array} obsArray Response from SensorThings API as array
* @returns {Array} Array of formatted observations suitable for use in a line chart
*/
const formatSTAResponseForLineChart = function (obsArray) {
if (!obsArray) return;
const dataSTAFormatted = 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;
};
/**
* Draw a line chart using Highcharts library
* @param {Array} formattedObsArrayForLineChart Response from SensorThings API formatted for use in a line chart
* @param {Object} formattedDatastreamMetadata Object containing Datastream metadata
* @returns {undefined} undefined
*/
const drawLineChartHC = function (
formattedObsArrayForLineChart,
formattedDatastreamMetadata
) {
const {
datastreamDescription: DATASTREAM_DESCRIPTION,
datastreamName: DATASTREAM_NAME,
phenomenonName: PHENOMENON_NAME,
unitOfMeasurementSymbol: PHENOMENON_SYMBOL,
} = formattedDatastreamMetadata;
Highcharts.stockChart("chart-line", {
chart: {
zoomType: "x",
},
rangeSelector: {
selected: 5,
},
title: {
text: DATASTREAM_DESCRIPTION,
"align": "left",
},
subtitle: {
text: DATASTREAM_NAME,
align: "left",
},
series: [
{
name: `${PHENOMENON_NAME} (${PHENOMENON_SYMBOL})`,
data: formattedObsArrayForLineChart,
tooltip: {
valueDecimals: 2,
},
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
},
],
});
};
/**
* Follows "@iot.nextLink" links in SensorThingsAPI's response
* Appends new results to existing results
* @async
* @param {Promise} responsePromise Promise object resulting from an Axios GET request
* @returns {Object} Object containing results from all the "@iot.nextLink" links
*/
const followNextLink = function (responsePromise) {
if (!responsePromise) return;
return responsePromise
.then((lastSuccess) => {
if (lastSuccess.data["@iot.nextLink"]) {
return followNextLink(
axios.get(lastSuccess.data["@iot.nextLink"])
).then((nextLinkSuccess) => {
nextLinkSuccess.data.value = lastSuccess.data.value.concat(
nextLinkSuccess.data.value
);
return nextLinkSuccess;
});
} else {
return lastSuccess;
}
})
.catch((err) => {
console.error(err);
});
};
/**
* Retrieve all the Observations from a Datastream after traversing all the "@iot.nextLink" links
* @async
* @param {Promise} httpGetRequestPromise Promise object resulting from an Axios GET request
* @returns {Promise} A promise that contains an array of Observations from a single Datastream when fulfilled
*/
const getCombinedObservationsFromAllNextLinks = function (
httpGetRequestPromise
) {
return followNextLink(httpGetRequestPromise)
.then((success) => {
const successValue = success.data.value;
// Array that will hold the combined observations
const combinedObservations = [];
successValue.forEach((dataObj) => {
// Each page of results will have a dataArray that holds the observations
const dataArrays = dataObj.dataArray;
combinedObservations.push(...dataArrays);
});
return new Promise((resolve, reject) => {
resolve(combinedObservations);
});
})
.catch((err) => {
console.error(err);
});
};
/**
* Retrieve the metadata for a Datastream as well as the Observations corresponding to it
* @async
* @param {Promise} metadataPlusObsPromiseArray An array that contains two promises, one for datastream metadata, the other for observations
* @returns {Promise} A promise that contains two arrays when fulfilled, one for datastream metadata and the other for observations
*/
const getMetadataPlusObservationsForChart = async function (
metadataPlusObsPromiseArray
) {
// Array to store our final result
const combinedResolvedPromises = [];
// Use for/of loop - we need to maintain the order of execution of the async operations
for (const promise of metadataPlusObsPromiseArray) {
try {
// Resolved value of a single promise
const resolvedPromise = await promise;
combinedResolvedPromises.push(resolvedPromise);
} catch (err) {
console.error(err);
}
}
return combinedResolvedPromises;
};
/**
* Retrieve all the Observations from an array of Observations promises
* @async
* @param {Promise} observationPromiseArray An array that contains N observation promises
* @returns {Promise} A promise that contains an array of Observations from multiple Datastreams when fulfilled
*/
const getObservationsFromMultipleDatastreams = async function (
observationPromiseArray
) {
// Array to store our final result
const observationsAllDatastreamsArr = [];
// Use for/of loop - we need to maintain the order of execution of the async operations
for (const observationPromise of observationPromiseArray) {
try {
// Observations from a single Datastream
const observations = await observationPromise;
observationsAllDatastreamsArr.push(observations);
} catch (err) {
console.error(err);
}
}
return observationsAllDatastreamsArr;
};
// Building + phenomenon + sampling rate
const buildingsSensorSamplingRateRLArr = [
["101", "rl", "60min"],
["102", "rl", "60min"],
["107", "rl", "60min"],
["112, 118", "rl", "60min"],
["125", "rl", "60min"],
["225", "rl", "60min"],
];
// Datastreams IDs
const datastreamsRLArr = buildingsSensorSamplingRateRLArr.map((bldg) =>
getDatastreamIdFromBuildingNumber(...bldg)
);
// Datastreams URLs
const datastreamsUrlRLArr = datastreamsRLArr.map((datastreamId) =>
getObservationsUrl(BASE_URL, datastreamId)
);
// Promise objects - Observations / RL
const observationsPromisesRLArr = datastreamsUrlRLArr.map((obsUrl) =>
getCombinedObservationsFromAllNextLinks(
axiosGetRequest(obsUrl, QUERY_PARAMS_COMBINED)
)
);
// getObservationsFromMultipleDatastreams(observationsPromisesRLArr).then((x) =>
// console.log(x)
// );
const drawScatterPlotHC = function () {
// Ruecklauf
const ruecklaufArr = [
[1578193200000, 69.1999969482422],
[1578196800000, 69.1999969482422],
[1578200400000, 69.1999969482422],
[1578204000000, 69.1999969482422],
[1578207600000, 69.1999969482422],
[1578211200000, 69.1999969482422],
[1578214800000, 69.1999969482422],
[1578218400000, 69.1999969482422],
[1578222000000, 69.1999969482422],
[1578225600000, 69.1999969482422],
[1578229200000, 69.1999969482422],
[1578232800000, 69.1999969482422],
[1578236400000, 69.1999969482422],
[1578240000000, 69.1999969482422],
[1578243600000, 69.1999969482422],
[1578247200000, 69.1999969482422],
[1578250800000, 69.1999969482422],
[1578254400000, 69.1999969482422],
[1578258000000, 69.1999969482422],
[1578261600000, 69.1999969482422],
[1578265200000, 69.1999969482422],
[1578268800000, 69.1999969482422],
[1578272400000, 69.1999969482422],
[1578276000000, 69.1999969482422],
[1578279600000, 69.1999969482422],
[1578283200000, 69.1999969482422],
[1578286800000, 69.1999969482422],
[1578290400000, 69.1999969482422],
[1578294000000, 69.1999969482422],
[1578297600000, 69.1999969482422],
[1578301200000, 69.1999969482422],
[1578304800000, 69.1999969482422],
[1578308400000, 69.1999969482422],
[1578312000000, 69.1999969482422],
[1578315600000, 69.1999969482422],
[1578319200000, 69.1999969482422],
[1578322800000, 69.1999969482422],
[1578326400000, 69.1999969482422],
[1578330000000, 69.1999969482422],
[1578333600000, 69.1999969482422],
[1578337200000, 69.1999969482422],
[1578340800000, 69.1999969482422],
[1578344400000, 69.1999969482422],
[1578348000000, 69.1999969482422],
[1578351600000, 70.3556109079997],
[1578355200000, 76.920997634146],
[1578358800000, 79.4292947098697],
[1578362400000, 79.2650916245309],
[1578366000000, 79.6125674172757],
[1578369600000, 79.0597236964905],
[1578373200000, 77.7484098868052],
[1578376800000, 77.1226613864899],
[1578380400000, 76.9194480415149],
[1578384000000, 78.0028471359237],
[1578387600000, 77.1535494270819],
[1578391200000, 75.0470741498029],
[1578394800000, 74.679502580818],
[1578398400000, 73.6077361986314],
[1578402000000, 72.5580677314758],
[1578405600000, 72.3134755830553],
[1578409200000, 73.2778338997311],
[1578412800000, 73.7656394293467],
[1578416400000, 74.4736907299466],
[1578420000000, 74.046935040758],
[1578423600000, 73.4957105572807],
[1578427200000, 73.9627712815163],
[1578430800000, 74.0438044241729],
[1578434400000, 73.3727496036106],
[1578438000000, 73.1666655679279],
[1578441600000, 73.3418058388816],
[1578445200000, 73.6250001589457],
[1578448800000, 73.829112378629],
[1578452400000, 74.564083528116],
[1578456000000, 75.5183061171072],
[1578459600000, 77.3372781058983],
[1578463200000, 78.0196371225993],
[1578466800000, 77.6398578971368],
[1578470400000, 78.5464104081542],
[1578474000000, 78.7977605936686],
[1578477600000, 76.0006624035588],
[1578481200000, 74.818987728345],
[1578484800000, 72.7776491559135],
[1578488400000, 71.1266380795161],
[1578492000000, 71.3866485616896],
[1578495600000, 72.1584558128357],
[1578499200000, 72.7288795283423],
[1578502800000, 73.2401491424669],
[1578506400000, 72.613320930343],
[1578510000000, 71.7903886201647],
[1578513600000, 71.4483344078064],
[1578517200000, 71.8162703686467],
[1578520800000, 71.3690680013303],
[1578524400000, 70.6132688085203],
[1578528000000, 69.9669739277875],
[1578531600000, 69.2502318650422],
[1578535200000, 68.8407318482576],
[1578538800000, 71.4223982252898],
[1578542400000, 68.8941290716666],
[1578546000000, 71.8311421724037],
[1578549600000, 72.5245706435945],
];
// Power
const powerArr = [
[1578193200000, 0],
[1578196800000, 0],
[1578200400000, 0],
[1578204000000, 0],
[1578207600000, 0],
[1578211200000, 0],
[1578214800000, 0],
[1578218400000, 0],
[1578222000000, 0],
[1578225600000, 0],
[1578229200000, 0],
[1578232800000, 0],
[1578236400000, 0],
[1578240000000, 0],
[1578243600000, 0],
[1578247200000, 0],
[1578250800000, 0],
[1578254400000, 0],
[1578258000000, 0],
[1578261600000, 0],
[1578265200000, 0],
[1578268800000, 0],
[1578272400000, 0],
[1578276000000, 0],
[1578279600000, 0],
[1578283200000, 0],
[1578286800000, 0],
[1578290400000, 0],
[1578294000000, 0],
[1578297600000, 0],
[1578301200000, 0],
[1578304800000, 0],
[1578308400000, 0],
[1578312000000, 0],
[1578315600000, 0],
[1578319200000, 0],
[1578322800000, 0],
[1578326400000, 0],
[1578330000000, 0],
[1578333600000, 0],
[1578337200000, 0],
[1578340800000, 0],
[1578344400000, 0],
[1578348000000, 0],
[1578351600000, 0.831280025800069],
[1578355200000, 27.4361266860337],
[1578358800000, 4.02296011930285],
[1578362400000, 5.46578637448993],
[1578366000000, 189.045738115567],
[1578369600000, 262.879154692536],
[1578373200000, 182.996291840137],
[1578376800000, 253.720326864073],
[1578380400000, 266.71791350888],
[1578384000000, 258.650130305165],
[1578387600000, 256.817462126146],
[1578391200000, 251.198874591439],
[1578394800000, 245.782954276794],
[1578398400000, 225.835229413786],
[1578402000000, 191.164833256192],
[1578405600000, 189.317473084174],
[1578409200000, 160.866751228135],
[1578412800000, 165.104705085896],
[1578416400000, 185.380724406267],
[1578420000000, 6.318082232318],
[1578423600000, 22.6244981930396],
[1578427200000, 0.125080846609247],
[1578430800000, 0.858333364129066],
[1578434400000, 3.15562303745482],
[1578438000000, 1.73965485449897],
[1578441600000, 3.73938900530338],
[1578445200000, 0.641666680574417],
[1578448800000, 1.64397225697835],
[1578452400000, 14.7165156847371],
[1578456000000, 6.7406491904815],
[1578459600000, 257.018884414906],
[1578463200000, 282.409075120573],
[1578466800000, 284.999958205159],
[1578470400000, 291.20836768991],
[1578474000000, 285.753944205729],
[1578477600000, 248.43810322171],
[1578481200000, 227.135268204399],
[1578484800000, 182.10778157076],
[1578488400000, 169.076414526325],
[1578492000000, 160.098294117384],
[1578495600000, 149.832191919638],
[1578499200000, 195.966023142751],
[1578502800000, 159.01891281008],
[1578506400000, 3.94323859943668],
[1578510000000, 9.29238140483663],
[1578513600000, 4.09348021179692],
[1578517200000, 0],
[1578520800000, 0],
[1578524400000, 0],
[1578528000000, 0],
[1578531600000, 0],
[1578535200000, 0],
[1578538800000, 154.315364068476],
[1578542400000, 193.405831769548],
[1578546000000, 136.484141248209],
[1578549600000, 209.041194383494],
];
const CHART_TITLE = "Height Versus Weight of 507 Individuals by Gender";
const CHART_SUBTITLE = "Source: Heinz 2003";
const X_AXIS_TITLE = "Height (cm)";
const SERIES_1_NAME = "Rücklauftemp";
const SERIES_1_SYMBOL_COLOR = "rgba(119, 152, 191, .5)";
const SERIES_1_TEXT_COLOR = "rgb(119, 152, 191)"; // remove transparency from symbol color for a more "intense" color
const SERIES_1_SYMBOL = "°C";
const SERIES_2_NAME = "Power";
const SERIES_2_SYMBOL_COLOR = "rgba(223, 83, 83, .5)";
const SERIES_2_TEXT_COLOR = "rgb(223, 83, 83)"; // remove transparency from symbol color for a more "intense" color
const SERIES_2_SYMBOL = "kW";
Highcharts.chart("chart-scatter-plot", {
chart: {
type: "scatter",
zoomType: "xy",
},
title: {
text: CHART_TITLE,
},
subtitle: {
text: CHART_SUBTITLE,
},
xAxis: {
title: {
enabled: true,
text: X_AXIS_TITLE,
},
type: "datetime",
startOnTick: true,
endOnTick: true,
showLastLabel: true,
},
yAxis: [
{
// Primary yAxis
labels: {
format: `{value} ${SERIES_1_SYMBOL}`,
style: {
color: SERIES_1_TEXT_COLOR,
},
},
title: {
text: SERIES_1_NAME,
style: {
color: SERIES_1_TEXT_COLOR,
},
},
},
{
// Secondary yAxis
title: {
text: SERIES_2_NAME,
style: {
color: SERIES_2_TEXT_COLOR,
},
},
labels: {
format: `{value} ${SERIES_2_SYMBOL}`,
style: {
color: SERIES_2_TEXT_COLOR,
},
},
opposite: true,
},
],
legend: {
layout: "vertical",
align: "left",
verticalAlign: "top",
x: 100,
y: 70,
floating: true,
backgroundColor: Highcharts.defaultOptions.chart.backgroundColor,
borderWidth: 1,
},
plotOptions: {
scatter: {
marker: {
radius: 5,
states: {
hover: {
enabled: true,
lineColor: "rgb(100,100,100)",
},
},
},
states: {
hover: {
marker: {
enabled: false,
},
},
},
tooltip: {
headerFormat: "{point.x:%e %b, %Y %H:%M:%S}: {point.y}",
pointFormat: "{point.x} cm, {point.y} kg",
valueDecimals: 2,
},
},
},
series: [
{
name: SERIES_1_NAME,
color: SERIES_1_SYMBOL_COLOR,
data: ruecklaufArr,
tooltip: {
valueSuffix: ` ${SERIES_1_SYMBOL}`,
},
},
{
name: SERIES_2_NAME,
color: SERIES_2_SYMBOL_COLOR,
data: powerArr,
// need this property for the dual y-axes to work
// defines the y-axis that this series refers to
yAxis: 1,
tooltip: {
valueSuffix: ` ${SERIES_2_SYMBOL}`,
},
},
],
});
};
drawScatterPlotHC();
export {
BASE_URL,
QUERY_PARAMS_COMBINED,
getDatastreamIdFromBuildingNumber,
getDatastreamUrl,
getObservationsUrl,
createTemporalFilterString,
axiosGetRequest,
getDatastreamMetadata,
formatDatastreamMetadataForChart,
formatSTAResponseForHeatMap,
drawHeatMapHC,
formatSTAResponseForLineChart,
drawLineChartHC,
getCombinedObservationsFromAllNextLinks,
getMetadataPlusObservationsForChart,
};