Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
iCity
EnergyDashboard
Commits
6c9c84ee
Commit
6c9c84ee
authored
Apr 12, 2021
by
Pithon Kabiro
Browse files
Add heatmap chart
parent
57b1db33
Changes
8
Expand all
Show whitespace changes
Inline
Side-by-side
.prettierrc
0 → 100644
View file @
6c9c84ee
{
"quoteProps": "preserve"
}
README.md
View file @
6c9c84ee
## Energy Dashboard
# iCity TP3.1 Dashboard
Energy dashboard for Bosch Schwieberdingen
Development of dashboard for Bosch Schwieberdingen
\ No newline at end of file
index.html
View file @
6c9c84ee
<!DOCTYPE html>
<!DOCTYPE html>
<html
lang=
"en"
>
<html
lang=
"en"
>
<head>
<head>
<meta
charset=
"UTF-8"
/>
<meta
charset=
"utf-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
/>
<title>
iCity Bosch Prototype
</title>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta
name=
"description"
content=
""
/>
<meta
name=
"author"
content=
""
/>
<title>
Dashboard - iCity Bosch
</title>
<link
href=
"css/styles.css"
rel=
"stylesheet"
/>
<link
href=
"https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css"
rel=
"stylesheet"
crossorigin=
"anonymous"
/>
<script
src=
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js"
crossorigin=
"anonymous"
></script>
<!-- Axios -->
<!-- Axios -->
<!-- <script src="./node_modules/axios/dist/axios.min.js"></script> -->
<!-- <script src="./node_modules/axios/dist/axios.min.js"></script> -->
<script
src=
"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"
></script>
<script
src=
"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"
></script>
...
@@ -21,12 +37,112 @@
...
@@ -21,12 +37,112 @@
href=
"https://cesium.com/downloads/cesiumjs/releases/1.48/Build/Cesium/Widgets/widgets.css"
href=
"https://cesium.com/downloads/cesiumjs/releases/1.48/Build/Cesium/Widgets/widgets.css"
rel=
"stylesheet"
rel=
"stylesheet"
/>
/>
<!-- Bootstrap dashboard template -->
<script
defer
src=
"https://code.jquery.com/jquery-3.5.1.slim.min.js"
crossorigin=
"anonymous"
></script>
<script
defer
src=
"https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"
crossorigin=
"anonymous"
></script>
<script
defer
src=
"js/thirdparty/scripts.js"
></script>
<!--
Custom JS -->
<script
defer
src=
"js/appCesium.js"
></script>
<script
defer
src=
"js/appChart.js"
></script>
</head>
</head>
<body>
<body
class=
"sb-nav-fixed"
>
<div
id=
"cesiumContainer"
></div>
<nav
class=
"sb-topnav navbar navbar-expand navbar-dark bg-dark"
>
<!-- <div id="chart-high-line"></div> -->
<a
class=
"navbar-brand"
href=
"index.html"
>
iCity Bosch Dashboard
</a>
<div
id=
"chart-apex-line"
></div>
<button
<script
src=
"js/appChart.js"
></script>
class=
"btn btn-link btn-sm order-1 order-lg-0"
<script
src=
"js/appCesium.js"
></script>
id=
"sidebarToggle"
href=
"#"
>
<i
class=
"fas fa-bars"
></i>
</button>
</nav>
<div
id=
"layoutSidenav"
>
<div
id=
"layoutSidenav_nav"
>
<nav
class=
"sb-sidenav accordion sb-sidenav-dark"
id=
"sidenavAccordion"
>
<div
class=
"sb-sidenav-menu"
>
<div
class=
"nav"
>
<div
class=
"sb-sidenav-menu-heading"
>
Core
</div>
<a
class=
"nav-link"
href=
"index.html"
>
<div
class=
"sb-nav-link-icon"
>
<i
class=
"fas fa-tachometer-alt"
></i>
</div>
Dashboard
</a>
</div>
</div>
</nav>
</div>
<div
id=
"layoutSidenav_content"
>
<main>
<div
class=
"container-fluid"
>
<h1
class=
"mt-4"
>
Dashboard
</h1>
<ol
class=
"breadcrumb mb-4"
>
<li
class=
"breadcrumb-item active"
>
Dashboard
</li>
</ol>
<div
class=
"row"
>
<div
class=
"col-xl-12"
>
<div
class=
"card mb-4"
>
<div
class=
"card-header"
>
<i
class=
"fas fa-globe mr-1"
></i>
3D Visualization
</div>
<div
class=
"card-body"
>
<div
id=
"myCesiumContainer"
width=
"100%"
height=
"40"
></div>
</div>
</div>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-xl-6"
>
<div
class=
"card mb-4"
>
<div
class=
"card-header"
>
<i
class=
"fas fa-chart-line mr-1"
></i>
Line Chart Example
</div>
<div
class=
"card-body"
>
<div
id=
"chart-apex-line"
width=
"100%"
height=
"40"
></div>
</div>
</div>
</div>
<div
class=
"col-xl-6"
>
<div
class=
"card mb-4"
>
<div
class=
"card-header"
>
<i
class=
"fas fa-chart-area mr-1"
></i>
Area Chart Example
</div>
<div
class=
"card-body"
>
<div
id=
"chart-apex-heatmap"
width=
"100%"
height=
"40"
></div>
</div>
</div>
</div>
</div>
</div>
</main>
<footer
class=
"py-4 bg-light mt-auto"
>
<div
class=
"container-fluid"
>
<div
class=
"d-flex align-items-center justify-content-between small"
>
<div
class=
"text-muted"
>
Copyright
©
HfT Stuttgart 2021
</div>
<div>
<a
href=
"#"
>
Privacy Policy
</a>
·
<a
href=
"#"
>
Terms
&
Conditions
</a>
</div>
</div>
</div>
</footer>
</div>
</div>
</body>
</body>
</html>
</html>
index.js
View file @
6c9c84ee
...
@@ -9,6 +9,10 @@ app.get("/", (req, res) => {
...
@@ -9,6 +9,10 @@ app.get("/", (req, res) => {
res
.
sendFile
(
__dirname
+
"
/index.html
"
);
res
.
sendFile
(
__dirname
+
"
/index.html
"
);
});
});
app
.
get
(
"
/index.html
"
,
(
req
,
res
)
=>
{
res
.
redirect
(
"
/
"
);
});
app
.
listen
(
port
,
()
=>
{
app
.
listen
(
port
,
()
=>
{
console
.
log
(
`App listening at localhost:
${
port
}
`
);
console
.
log
(
`App listening at localhost:
${
port
}
`
);
});
});
public/css/styles.css
View file @
6c9c84ee
This diff is collapsed.
Click to expand it.
public/js/appCesium.js
View file @
6c9c84ee
...
@@ -8,7 +8,7 @@ Cesium.Ion.defaultAccessToken =
...
@@ -8,7 +8,7 @@ Cesium.Ion.defaultAccessToken =
const
LOAD_DETAILED_BLDG225
=
false
;
const
LOAD_DETAILED_BLDG225
=
false
;
// Global variable
// Global variable
const
viewer
=
new
Cesium
.
Viewer
(
"
c
esiumContainer
"
,
{
const
viewer
=
new
Cesium
.
Viewer
(
"
myC
esiumContainer
"
,
{
// terrainProvider: Cesium.createWorldTerrain(),
// terrainProvider: Cesium.createWorldTerrain(),
});
});
// viewer.scene.globe.depthTestAgainstTerrain = true;
// viewer.scene.globe.depthTestAgainstTerrain = true;
...
...
public/js/appChart.js
View file @
6c9c84ee
// DEBUG: for testing without a browser
"
use strict
"
;
// const axios = require("axios").default;
// Request parameters
// Request parameters
const
BASE_URL
=
const
BASE_URL
=
"
http://193.196.138.56/frost-icity-tp31/v1.1/Datastreams(48)/Observations
"
;
"
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_RESULT_FORMAT
=
"
dataArray
"
;
const
PARAM_ORDER_BY
=
"
phenomenonTime asc
"
;
const
PARAM_ORDER_BY
=
"
phenomenonTime asc
"
;
const
PARAM_FILTER
=
const
PARAM_FILTER
=
"
resultTime ge 20
18
-01-01T00:00:00.000Z and resultTime le 20
18
-0
2
-01T00:00:00.000Z
"
;
"
resultTime ge 20
20
-01-01T00:00:00.000Z and resultTime le 20
20
-0
7
-01T00:00:00.000Z
"
;
const
PARAM_SELECT
=
"
result,phenomenonTime
"
;
const
PARAM_SELECT
=
"
result,phenomenonTime
"
;
/**
/**
* Helper function for converting the observation time from a string to Unix epoch
* Draw an EMPTY chart using Apexcharts library
* @param {*} obsArray
* @param {HTMLElement} htmlElement - HTML element where chart will be drawn
* @param {String} mainTitle - Main chart title
* @param {String} yAxisTitle - Y-axis title
* @returns {Object} - An empty chart object
*/
*/
const
formatObservationTime
=
function
(
obsArray
)
{
const
drawEmptyLineChartAC
=
function
(
const
dataSTAFormatted
=
[];
htmlElement
,
obsArray
.
forEach
((
result
)
=>
{
mainTitle
=
"
Main Chart Title
"
,
const
timestampObs
=
new
Date
(
result
[
0
].
slice
(
0
,
-
1
)).
getTime
();
// slice() removes trailing "Z" character in timestamp
yAxisTitle
=
"
y-axis title
"
const
valueObs
=
result
[
1
];
)
{
dataSTAFormatted
.
push
([
timestampObs
,
valueObs
]);
});
return
dataSTAFormatted
;
};
/**
* Function draws chart using Highcharts library
* @param {*} dataArr
*/
const
drawLineChartHC
=
function
(
dataArr
)
{
// Create the chart
Highcharts
.
stockChart
(
"
chart-highcharts
"
,
{
rangeSelector
:
{
selected
:
1
,
},
title
:
{
text
:
"
AAPL Stock Price
"
,
},
series
:
[
{
name
:
"
AAPL
"
,
data
:
dataArr
,
tooltip
:
{
valueDecimals
:
2
,
},
// turboThreshold: 10000, // Disable this option; otherwise throws error if our array has a length > 1000
},
],
});
};
/**
* Function draws chart using Apexcharts library
* @param {*} dataArr
*/
const
drawLineChartAC
=
function
(
dataArr
)
{
// Chart constants
// Chart constants
const
CHART_HTML_ELEMENT
=
document
.
querySelector
(
"
#chart-apex-line
"
);
const
CHART_HTML_ELEMENT
=
htmlElement
;
const
CHART_NAME
=
"
Temperature
"
;
const
TITLE_TEXT
=
mainTitle
;
const
TITLE_TEXT
=
"
Inlet flow (Vorlauf)
"
;
const
Y_AXIS_TITLE
=
yAxisTitle
;
const
Y_AXIS_TITLE
=
"
Temperature
"
;
const
options
=
{
const
options
=
{
series
:
[
series
:
[],
{
name
:
CHART_NAME
,
data
:
dataArr
,
},
],
chart
:
{
chart
:
{
type
:
"
area
"
,
type
:
"
area
"
,
stacked
:
false
,
stacked
:
false
,
...
@@ -93,6 +53,9 @@ const drawLineChartAC = function (dataArr) {
...
@@ -93,6 +53,9 @@ const drawLineChartAC = function (dataArr) {
text
:
TITLE_TEXT
,
text
:
TITLE_TEXT
,
align
:
"
left
"
,
align
:
"
left
"
,
},
},
noData
:
{
text
:
"
Loading...
"
,
},
fill
:
{
fill
:
{
type
:
"
gradient
"
,
type
:
"
gradient
"
,
gradient
:
{
gradient
:
{
...
@@ -106,8 +69,9 @@ const drawLineChartAC = function (dataArr) {
...
@@ -106,8 +69,9 @@ const drawLineChartAC = function (dataArr) {
yaxis
:
{
yaxis
:
{
labels
:
{
labels
:
{
formatter
:
function
(
val
)
{
formatter
:
function
(
val
)
{
return
val
.
toFixed
(
2
);
return
val
.
toFixed
(
0
);
},
},
forceNiceScale
:
true
,
},
},
title
:
{
title
:
{
text
:
Y_AXIS_TITLE
,
text
:
Y_AXIS_TITLE
,
...
@@ -127,14 +91,296 @@ const drawLineChartAC = function (dataArr) {
...
@@ -127,14 +91,296 @@ const drawLineChartAC = function (dataArr) {
};
};
const
chart
=
new
ApexCharts
(
CHART_HTML_ELEMENT
,
options
);
const
chart
=
new
ApexCharts
(
CHART_HTML_ELEMENT
,
options
);
return
chart
;
};
// Line chart 1 constants
const
chart1LineHTML
=
document
.
querySelector
(
"
#chart-apex-line
"
);
const
chart1LineTitle
=
"
Inlet flow (Vorlauf)
"
;
const
chart1LineYAxisTitle
=
"
Temperature (°C)
"
;
// Draw an empty line chart
const
lineChartApex
=
drawEmptyLineChartAC
(
chart1LineHTML
,
chart1LineTitle
,
chart1LineYAxisTitle
);
lineChartApex
.
render
();
/**
* Update an empty chart created using Apexcharts library
* @param {String} chartName
* @param {Array} dataArr
* @returns {void}
*/
const
updateLineChartAC
=
function
(
chartName
,
dataArr
)
{
const
CHART_NAME
=
chartName
;
// Update the chart
lineChartApex
.
updateSeries
([
{
name
:
CHART_NAME
,
data
:
dataArr
,
},
]);
};
/**
* Draw a heatmap using the ApexCharts library
* ATTEMPT 1
* @param {Array} obsArray - Response from SensorThings API as array
* @returns {void}
*/
const
drawHeatMapAC1
=
function
(
obsArray
)
{
// Chart constants
const
CHART_HEATMAP_TITLE
=
"
HeatMap Chart
"
;
const
CHART_HEATMAP_NAME_SERIES_1
=
"
VL-225
"
;
// const CHART_HEATMAP_NAME_SERIES_2 = "W2";
/**
* Convert SensorThings API response (an array) into an object
* @returns {Object} - Chart series object
*/
const
generateHeatMapData
=
function
()
{
const
series
=
[];
obsArray
.
forEach
(([
obsTime
,
obsValue
])
=>
{
series
.
push
({
x
:
obsTime
.
slice
(
0
,
-
1
),
// remove trailing "Z" from timestamp
y
:
obsValue
,
});
});
return
series
;
};
const
data
=
[
{
name
:
CHART_HEATMAP_NAME_SERIES_1
,
data
:
generateHeatMapData
(
obsArray
),
},
// {
// name: CHART_HEATMAP_NAME_SERIES_2,
// data: generateHeatMapData(obsArray),
// },
];
data
.
reverse
();
const
colors
=
[
"
#F3B415
"
,
"
#F27036
"
,
"
#663F59
"
,
"
#6A6E94
"
,
"
#4E88B4
"
,
"
#00A7C6
"
,
"
#18D8D8
"
,
"
#A9D794
"
,
"
#46AF78
"
,
"
#A93F55
"
,
"
#8C5E58
"
,
"
#2176FF
"
,
"
#33A1FD
"
,
"
#7A918D
"
,
"
#BAFF29
"
,
];
// colors.reverse();
const
options
=
{
series
:
data
,
chart
:
{
height
:
350
,
type
:
"
heatmap
"
,
},
dataLabels
:
{
enabled
:
false
,
},
colors
:
colors
,
title
:
{
text
:
CHART_HEATMAP_TITLE
,
},
grid
:
{
padding
:
{
right
:
20
,
},
},
xaxis
:
{
type
:
"
datetime
"
,
},
tooltip
:
{
shared
:
false
,
x
:
{
formatter
:
function
(
val
)
{
return
new
Date
(
val
).
toLocaleString
();
},
},
y
:
{
formatter
:
function
(
val
)
{
return
val
.
toFixed
(
2
);
},
},
},
plotOptions
:
{
heatmap
:
{
useFillColorAsStroke
:
true
,
// we need this option for the chart to be visible
// distributed: true,
// enableShades: false,
},
},
};
const
chart
=
new
ApexCharts
(
document
.
querySelector
(
"
#chart-apex-heatmap
"
),
options
);
chart
.
render
();
};
/**
* Draw a heatmap using the ApexCharts library
* ATTEMPT 2
* @param {Array} obsArray - Response from SensorThings API as array
* @returns {void}
*/
const
drawHeatMapAC2
=
function
(
obsArray
)
{
// Chart constants
const
CHART_HEATMAP_TITLE
=
"
HeatMap Chart
"
;
const
CHART_HEATMAP_NAME_SERIES_1
=
"
VL-225
"
;
/**
* Convert SensorThings API response (an array) into an object
* @returns {Object} - Chart series object
*/
const
generateHeatMapData
=
function
()
{
const
series
=
[];
obsArray
.
forEach
(([
obsTime
,
obsValue
])
=>
{
series
.
push
({
x
:
obsTime
.
slice
(
0
,
-
1
),
// remove trailing "Z" from timestamp
y
:
obsValue
,
});
});
return
series
;
};
const
data
=
[
{
name
:
CHART_HEATMAP_NAME_SERIES_1
,
data
:
generateHeatMapData
(
obsArray
),
},
// {
// name: CHART_HEATMAP_NAME_SERIES_2,
// data: generateHeatMapData(obsArray),
// },
];
// Constants for our data range
const
LOW_FROM
=
65
;
const
LOW_TO
=
70
;
const
MEDIUM_FROM
=
70
;
const
MEDIUM_TO
=
75
;
const
HIGH_FROM
=
75
;
const
HIGH_TO
=
80
;
const
EXTREME_FROM
=
80
;
const
EXTREME_TO
=
85
;
const
options
=
{
series
:
data
,
chart
:
{
height
:
450
,
type
:
"
heatmap
"
,
},
plotOptions
:
{
heatmap
:
{
shadeIntensity
:
0.5
,
radius
:
0
,
useFillColorAsStroke
:
true
,
colorScale
:
{
ranges
:
[
{
from
:
null
,
to
:
null
,
name
:
"
null
"
,
color
:
"
#525252
"
,
},
{
from
:
LOW_FROM
,
to
:
LOW_TO
,
name
:
`
${
LOW_FROM
}
°C`
,
color
:
"
#1a9641
"
,
},
{
from
:
MEDIUM_FROM
,
to
:
MEDIUM_TO
,
name
:
`
${
MEDIUM_FROM
}
°C`
,
color
:
"
#a6d96a
"
,
},
{
from
:
HIGH_FROM
,
to
:
HIGH_TO
,
name
:
`
${
HIGH_FROM
}
°C`
,
color
:
"
#fdae61
"
,
},
{
from
:
EXTREME_FROM
,
to
:
EXTREME_TO
,
name
:
`
${
EXTREME_FROM
}
°C`
,
color
:
"
#d7191c
"
,
},
],
},
},
},
dataLabels
:
{
enabled
:
false
,
},
stroke
:
{
width
:
1
,
},
title
:
{
text
:
CHART_HEATMAP_TITLE
,
},
grid
:
{
padding
:
{
right
:
20
,
},
},
xaxis
:
{
type
:
"
datetime
"
,
// labels: {
// format: "MMM",
// },
},
tooltip
:
{
shared
:
false
,
x
:
{
formatter
:
function
(
val
)
{
return
new
Date
(
val
).
toLocaleString
();
},
},
y
:
{
formatter
:
function
(
val
)
{
if
(
val
)
{
return
val
.
toFixed
(
2
);
}
else
{
return
"
null
"
;
}
},
},
},
// distributed: true,
};
const
chart
=
new
ApexCharts
(
document
.
querySelector
(
"
#chart-apex-heatmap
"
),
options
);
chart
.
render
();
chart
.
render
();
};
};
/**
/**
* F
unction f
ollows "@iot.nextLink" links
* Follows "@iot.nextLink" links
in SensorThingsAPI's response
* Appends new results to existing results
* Appends new results to existing results
* @param {*} responsePromise
* @async
* @param {Object} responsePromise - Promise object
* @returns {Object} - Object containing results from all the "@iot.nextLink" links
*/
*/
const
followNextLink
=
function
(
responsePromise
)
{
const
followNextLink
=
function
(
responsePromise
)
{
return
responsePromise
return
responsePromise
...
@@ -182,6 +428,8 @@ followNextLink(
...
@@ -182,6 +428,8 @@ followNextLink(
});
});
// DEBUG: Check total number of observations
// DEBUG: Check total number of observations
console
.
log
(
combinedObservations
.
length
);
console
.
log
(
combinedObservations
.
length
);
// DEBUG: Print the array of observations
console
.
log
(
combinedObservations
);
return
combinedObservations
;
return
combinedObservations
;
})
})
...
@@ -189,7 +437,6 @@ followNextLink(
...
@@ -189,7 +437,6 @@ followNextLink(
console
.
log
(
err
);
console
.
log
(
err
);
})
})
.
then
((
observationArr
)
=>
{
.
then
((
observationArr
)
=>
{
const
formattedObservations
=
formatObservationTime
(
observationArr
);
updateLineChartAC
(
chart1LineTitle
,
observationArr
);
console
.
log
(
formattedObservations
);
drawHeatMapAC2
(
observationArr
);
drawLineChartAC
(
observationArr
);
});
});
public/js/thirdparty/scripts.js
0 → 100644
View file @
6c9c84ee
/*!
* Start Bootstrap - SB Admin v6.0.2 (https://startbootstrap.com/template/sb-admin)
* Copyright 2013-2020 Start Bootstrap
* Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE)
*/
(
function
(
$
)
{
"
use strict
"
;
// Add active state to sidbar nav links
var
path
=
window
.
location
.
href
;
// because the 'href' property of the DOM element is the absolute path
$
(
"
#layoutSidenav_nav .sb-sidenav a.nav-link
"
).
each
(
function
()
{
if
(
this
.
href
===
path
)
{
$
(
this
).
addClass
(
"
active
"
);
}
});
// Toggle the side navigation
$
(
"
#sidebarToggle
"
).
on
(
"
click
"
,
function
(
e
)
{
e
.
preventDefault
();
$
(
"
body
"
).
toggleClass
(
"
sb-sidenav-toggled
"
);
});
})(
jQuery
);
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment