diff --git a/index.html b/index.html index 2af58b68af47dc13535d17f6c733b2ada68df8b0..46c5dc07042715c9ca6dcea2ecad9e439c3e45b9 100644 --- a/index.html +++ b/index.html @@ -16,27 +16,39 @@ rel="stylesheet" crossorigin="anonymous" /> + + <!-- Font Awesome icons --> <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js" crossorigin="anonymous" ></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 --> - <script src="https://code.highcharts.com/stock/highstock.js"></script> + <script src="https://code.highcharts.com/highcharts.js"></script> <script src="https://code.highcharts.com/stock/modules/data.js"></script> <script src="https://code.highcharts.com/stock/modules/exporting.js"></script> <script src="https://code.highcharts.com/stock/modules/export-data.js"></script> + <script src="https://code.highcharts.com/modules/heatmap.js"></script> + <script src="https://code.highcharts.com/modules/data.js"></script> + <script src="https://code.highcharts.com/modules/boost-canvas.js"></script> + <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 --> <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" rel="stylesheet" /> + <!-- Bootstrap dashboard template --> <script defer @@ -49,6 +61,7 @@ crossorigin="anonymous" ></script> <script defer src="js/thirdparty/scripts.js"></script> + <!-- Custom JS --> <script defer src="js/appCesium.js"></script> @@ -97,7 +110,11 @@ 3D Visualization </div> <div class="card-body"> - <div id="myCesiumContainer" width="100%" height="40"></div> + <div + id="cesiumGlobeContainer" + width="100%" + height="40" + ></div> </div> </div> </div> @@ -110,7 +127,7 @@ Line Chart Example </div> <div class="card-body"> - <div id="chart-apex-line" width="100%" height="40"></div> + <div id="chart-line" width="100%" height="40"></div> </div> </div> </div> @@ -121,7 +138,7 @@ Area Chart Example </div> <div class="card-body"> - <div id="chart-apex-heatmap" width="100%" height="40"></div> + <div id="chart-heatmap" width="100%" height="40"></div> </div> </div> </div> diff --git a/public/js/appCesium.js b/public/js/appCesium.js index 6dcdde64682d92fc681fea7439a589e25a009b36..6df780922c57583f3f6c36d3ac25d7fdd2ae86af 100644 --- a/public/js/appCesium.js +++ b/public/js/appCesium.js @@ -8,7 +8,7 @@ Cesium.Ion.defaultAccessToken = const LOAD_DETAILED_BLDG225 = false; // Global variable -const viewer = new Cesium.Viewer("myCesiumContainer", { +const viewer = new Cesium.Viewer("cesiumGlobeContainer", { // terrainProvider: Cesium.createWorldTerrain(), }); // viewer.scene.globe.depthTestAgainstTerrain = true; diff --git a/public/js/appChart.js b/public/js/appChart.js index 41225ff9c1077b442baa53fa5dfa1e4fddc2c35a..924d98cdc802cc15caec6729d7f7582bf9dd8e8b 100644 --- a/public/js/appChart.js +++ b/public/js/appChart.js @@ -1,16 +1,23 @@ "use strict"; // Request parameters + +// Observations WITHOUT data gap - Bau 225 const BASE_URL = "http://193.196.39.91:8080/frost-icity-tp31/v1.1/Datastreams(80)/Observations"; -const BASE_URL2 = - "http://193.196.39.91:8080/frost-icity-tp31-v2/v1.1/Datastreams(41)/Observations"; + const PARAM_RESULT_FORMAT = "dataArray"; const PARAM_ORDER_BY = "phenomenonTime asc"; const PARAM_FILTER = - "resultTime ge 2020-01-01T00:00:00.000Z and resultTime le 2020-07-01T00:00:00.000Z"; + "resultTime ge 2020-01-01T00:00:00.000Z and resultTime le 2021-01-01T00:00:00.000Z"; const PARAM_SELECT = "result,phenomenonTime"; +// Observations WITH data gap - Bau 112 +const BASE_URL2 = + "http://193.196.39.91:8080/frost-icity-tp31-v2/v1.1/Datastreams(78)/Observations"; +const PARAM_FILTER2 = + "resultTime ge 2020-06-01T00:00:00.000Z and resultTime le 2021-01-01T00:00:00.000Z"; + /** * Draw an EMPTY chart using Apexcharts library * @param {HTMLElement} htmlElement - HTML element where chart will be drawn @@ -95,7 +102,7 @@ const drawEmptyLineChartAC = function ( }; // Line chart 1 constants -const chart1LineHTML = document.querySelector("#chart-apex-line"); +const chart1LineHTML = document.querySelector("#chart-line"); const chart1LineTitle = "Inlet flow (Vorlauf)"; const chart1LineYAxisTitle = "Temperature (°C)"; @@ -369,12 +376,132 @@ const drawHeatMapAC2 = function (obsArray) { }; const chart = new ApexCharts( - document.querySelector("#chart-apex-heatmap"), + document.querySelector("#chart-heatmap"), options ); chart.render(); }; +/** + * Draw a heatmap using Highcharts library + * @param {*} obsArray - Response from SensorThings API as array + * @returns {void} + */ +const drawHeatMapHC = function (obsArray) { + /** + * Format the response from SensorThings API to make it suitable for heatmap + * @returns {Array} + */ + const formatSTAResponseForHeatMap = function () { + const dataSTAFormatted = []; + obsArray.forEach((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]; + dataSTAFormatted.push([timestamp, hourOfDay, value]); + }); + return dataSTAFormatted; + }; + + Highcharts.chart("chart-heatmap", { + chart: { + type: "heatmap", + zoomType: "x", + }, + + boost: { + useGPUTranslations: true, + }, + + title: { + text: "Inlet flow (Vorlauf)", + align: "left", + x: 40, + }, + + subtitle: { + text: "Temperature variation by day and hour in 2020", + 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, 6, 12, 18, 24], + 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: 60, + max: 85, + startOnTick: false, + endOnTick: false, + labels: { + format: "{value}℃", + }, + }, + + series: [ + { + data: formatSTAResponseForHeatMap(), + boostThreshold: 100, + borderWidth: 0, + nullColor: "#525252", + colsize: 24 * 36e5, // one day + tooltip: { + headerFormat: "Temperature<br/>", + pointFormat: + "{point.x:%e %b, %Y} {point.y}:00: <b>{point.value} ℃</b>", + }, + 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 @@ -437,6 +564,7 @@ followNextLink( console.log(err); }) .then((observationArr) => { - updateLineChartAC(chart1LineTitle, observationArr); - drawHeatMapAC2(observationArr); + // updateLineChartAC(chart1LineTitle, observationArr); + // drawHeatMapAC2(observationArr); + drawHeatMapHC(observationArr); });