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
86bff050
Commit
86bff050
authored
3 years ago
by
Sven Schneider
Browse files
Options
Download
Email Patches
Plain Diff
added aggregation method into appCharts.js
parent
02bd3700
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
index.html
+84
-115
index.html
public/js/appCesium.js
+264
-259
public/js/appCesium.js
public/js/appChart.js
+422
-240
public/js/appChart.js
with
770 additions
and
614 deletions
+770
-614
index.html
+
84
-
115
View file @
86bff050
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<head>
<meta
charset=
"utf-8"
/>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<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"
/>
<link
href=
"https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css"
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>
<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> -->
...
...
@@ -46,122 +37,100 @@
<!-- 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"
/>
<link
href=
"https://cesium.com/downloads/cesiumjs/releases/1.48/Build/Cesium/Widgets/widgets.css"
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=
"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
type=
"module"
src=
"js/appCesium.js"
></script>
<script
defer
type=
"module"
src=
"js/appChart.js"
></script>
</head>
<body
class=
"sb-nav-fixed"
>
</head>
<body
class=
"sb-nav-fixed"
>
<nav
class=
"sb-topnav navbar navbar-expand navbar-dark bg-dark"
>
<a
class=
"navbar-brand"
href=
"index.html"
>
iCity Bosch Dashboard
</a>
<button
class=
"btn btn-link btn-sm order-1 order-lg-0"
id=
"sidebarToggle"
href=
"#"
>
<a
class=
"navbar-brand"
href=
"index.html"
>
iCity Bosch Dashboard
</a>
<button
class=
"btn btn-link btn-sm order-1 order-lg-0"
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
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>
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>
</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=
"cesiumGlobeContainer"
width=
"100%"
height=
"40"
></div>
</div>
<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=
"cesiumGlobeContainer"
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-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-heatmap"
width=
"100%"
height=
"40"
></div>
</div>
</div>
</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-line"
width=
"100%"
height=
"40"
></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>
</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-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>
</footer>
</div>
</div>
</body>
</html>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
Click to expand it.
public/js/appCesium.js
+
264
-
259
View file @
86bff050
...
...
@@ -2,26 +2,27 @@
// Functions
import
{
getDatastreamIdFromBuildingNumber
,
getObservationsUrl
,
createTemporalFilterString
,
formatSTAResponseForHeatMap
,
drawHeatMapHC
,
formatSTAResponseForLineChart
,
drawLineChartHC
,
followNextLink
,
getDatastreamIdFromBuildingNumber
,
getObservationsUrl
,
createTemporalFilterString
,
formatSTAResponseForHeatMap
,
drawHeatMapHC
,
formatSTAResponseForLineChart
,
drawLineChartHC
,
followNextLink
,
aggregateResponse
,
}
from
"
./appChart.js
"
;
// Constants
import
{
BASE_URL
,
PARAM_RESULT_FORMAT
,
PARAM_ORDER_BY
,
PARAM_SELECT
,
BASE_URL
,
PARAM_RESULT_FORMAT
,
PARAM_ORDER_BY
,
PARAM_SELECT
,
}
from
"
./appChart.js
"
;
Cesium
.
Ion
.
defaultAccessToken
=
"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyODgxYzJlNi1kNDZiLTQ3ZmQtYmUxYy0yMWI0OGM3NDA5MzAiLCJpZCI6NDczOSwic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTU0MTUyMzU0MX0.shj2hM3pvsvcmE_wMb2aBDuk_cKWmFmbolltInGImwU
"
;
"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyODgxYzJlNi1kNDZiLTQ3ZmQtYmUxYy0yMWI0OGM3NDA5MzAiLCJpZCI6NDczOSwic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTU0MTUyMzU0MX0.shj2hM3pvsvcmE_wMb2aBDuk_cKWmFmbolltInGImwU
"
;
// Flag to determine models that will be loaded
// Set to `true` or `false`
...
...
@@ -29,10 +30,10 @@ const LOAD_DETAILED_BLDG225 = false;
// Global variable
const
viewer
=
new
Cesium
.
Viewer
(
"
cesiumGlobeContainer
"
,
{
scene3DOnly
:
true
,
imageryProvider
:
Cesium
.
createOpenStreetMapImageryProvider
({
url
:
"
https://a.tile.openstreetmap.org/
"
,
}),
scene3DOnly
:
true
,
imageryProvider
:
Cesium
.
createOpenStreetMapImageryProvider
({
url
:
"
https://a.tile.openstreetmap.org/
"
,
}),
});
/**
...
...
@@ -40,26 +41,25 @@ const viewer = new Cesium.Viewer("cesiumGlobeContainer", {
* @param {String} urlTiles URL to the 3DTiles to be loaded
* @returns {undefined} undefined
*/
const
loadTiles
=
function
(
urlTiles
)
{
const
tileset
=
new
Cesium
.
Cesium3DTileset
({
url
:
urlTiles
,
});
viewer
.
scene
.
primitives
.
add
(
tileset
);
tileset
.
readyPromise
.
then
(
function
()
{
viewer
.
zoomTo
(
tileset
,
new
Cesium
.
HeadingPitchRange
(
0.0
,
-
0.5
,
tileset
.
boundingSphere
.
radius
/
0.5
)
)
.
otherwise
(
function
(
err
)
{
throw
err
;
});
});
const
loadTiles
=
function
(
urlTiles
)
{
const
tileset
=
new
Cesium
.
Cesium3DTileset
({
url
:
urlTiles
,
});
viewer
.
scene
.
primitives
.
add
(
tileset
);
tileset
.
readyPromise
.
then
(
function
()
{
viewer
.
zoomTo
(
tileset
,
new
Cesium
.
HeadingPitchRange
(
0.0
,
-
0.5
,
tileset
.
boundingSphere
.
radius
/
0.5
)
)
.
otherwise
(
function
(
err
)
{
throw
err
;
});
});
};
/**
...
...
@@ -67,12 +67,12 @@ const loadTiles = function (urlTiles) {
* @param{*}
* @returns {undefined} undefined
*/
const
loadNonDetailed
=
function
()
{
// Paths to data sources
const
URL_3DTILES
=
"
data_3d/3dtiles/1_full/tileset.json
"
;
const
loadNonDetailed
=
function
()
{
// Paths to data sources
const
URL_3DTILES
=
"
data_3d/3dtiles/1_full/tileset.json
"
;
// Tileset with all buildings
loadTiles
(
URL_3DTILES
);
// Tileset with all buildings
loadTiles
(
URL_3DTILES
);
};
/**
...
...
@@ -81,19 +81,19 @@ const loadNonDetailed = function () {
* @param {String} gltfId Name of the glTF model file without the extension i.e. exclude the `.gltf` suffix
* @returns {undefined} undefined
*/
const
gltfLoad
=
function
(
gltfUrl
,
gltfId
)
{
const
modelMatrix
=
Cesium
.
Transforms
.
eastNorthUpToFixedFrame
(
Cesium
.
Cartesian3
.
fromDegrees
(
9.083385
,
48.881342
,
0
)
);
viewer
.
scene
.
primitives
.
add
(
Cesium
.
Model
.
fromGltf
({
url
:
`
${
gltfUrl
}
/
${
gltfId
}
.gltf`
,
modelMatrix
:
modelMatrix
,
scale
:
0.0254
,
allowPicking
:
true
,
})
);
const
gltfLoad
=
function
(
gltfUrl
,
gltfId
)
{
const
modelMatrix
=
Cesium
.
Transforms
.
eastNorthUpToFixedFrame
(
Cesium
.
Cartesian3
.
fromDegrees
(
9.083385
,
48.881342
,
0
)
);
viewer
.
scene
.
primitives
.
add
(
Cesium
.
Model
.
fromGltf
({
url
:
`
${
gltfUrl
}
/
${
gltfId
}
.gltf`
,
modelMatrix
:
modelMatrix
,
scale
:
0.0254
,
allowPicking
:
true
,
})
);
};
/**
...
...
@@ -101,64 +101,64 @@ const gltfLoad = function (gltfUrl, gltfId) {
* @param{*}
* @returns {undefined} undefined
*/
const
loadDetailed
=
function
()
{
// Paths to data sources
const
URL_3DTILES
=
"
data_3d/3dtiles/2_partial/tileset.json
"
;
const
URL_GLTF
=
"
data_3d/gltf
"
;
// Tileset without building 225
loadTiles
(
URL_3DTILES
);
// Load Building 225
gltfLoad
(
URL_GLTF
,
"
bosch_si225_3
"
);
// Load sensors in Building 225
const
gltfArray
=
[
"
sensor_013
"
,
"
sensor_023
"
,
"
sensor_033
"
,
"
sensor_053
"
,
"
sensor_063
"
,
"
sensor_073
"
,
"
sensor_083
"
,
"
sensor_093
"
,
"
sensor_103
"
,
"
sensor_113
"
,
"
sensor_123
"
,
"
sensor_133
"
,
"
sensor_143
"
,
"
sensor_153
"
,
"
sensor_163
"
,
"
sensor_173
"
,
"
sensor_183
"
,
"
sensor_213
"
,
"
sensor_223
"
,
"
sensor_233
"
,
"
sensor_253
"
,
"
sensor_263
"
,
"
sensor_273
"
,
"
sensor_283
"
,
"
sensor_293
"
,
"
sensor_303
"
,
"
sensor_313
"
,
"
sensor_323
"
,
"
sensor_333
"
,
"
sensor_343
"
,
"
sensor_353
"
,
"
sensor_363
"
,
"
sensor_373
"
,
"
sensor_383_v2
"
,
];
gltfArray
.
forEach
((
sensor
)
=>
gltfLoad
(
URL_GLTF
,
sensor
));
const
loadDetailed
=
function
()
{
// Paths to data sources
const
URL_3DTILES
=
"
data_3d/3dtiles/2_partial/tileset.json
"
;
const
URL_GLTF
=
"
data_3d/gltf
"
;
// Tileset without building 225
loadTiles
(
URL_3DTILES
);
// Load Building 225
gltfLoad
(
URL_GLTF
,
"
bosch_si225_3
"
);
// Load sensors in Building 225
const
gltfArray
=
[
"
sensor_013
"
,
"
sensor_023
"
,
"
sensor_033
"
,
"
sensor_053
"
,
"
sensor_063
"
,
"
sensor_073
"
,
"
sensor_083
"
,
"
sensor_093
"
,
"
sensor_103
"
,
"
sensor_113
"
,
"
sensor_123
"
,
"
sensor_133
"
,
"
sensor_143
"
,
"
sensor_153
"
,
"
sensor_163
"
,
"
sensor_173
"
,
"
sensor_183
"
,
"
sensor_213
"
,
"
sensor_223
"
,
"
sensor_233
"
,
"
sensor_253
"
,
"
sensor_263
"
,
"
sensor_273
"
,
"
sensor_283
"
,
"
sensor_293
"
,
"
sensor_303
"
,
"
sensor_313
"
,
"
sensor_323
"
,
"
sensor_333
"
,
"
sensor_343
"
,
"
sensor_353
"
,
"
sensor_363
"
,
"
sensor_373
"
,
"
sensor_383_v2
"
,
];
gltfArray
.
forEach
((
sensor
)
=>
gltfLoad
(
URL_GLTF
,
sensor
));
};
if
(
!
LOAD_DETAILED_BLDG225
)
{
// Default case: load only 3dTiles
loadNonDetailed
();
// Default case: load only 3dTiles
loadNonDetailed
();
}
else
{
// Alternative case: load 3dTiles + glTF
loadDetailed
();
// Alternative case: load 3dTiles + glTF
loadDetailed
();
}
/**
...
...
@@ -166,109 +166,109 @@ if (!LOAD_DETAILED_BLDG225) {
* @param {*}
* @returns {undefined}
*/
const
activate3DTileFeaturePicking
=
function
()
{
// HTML overlay for showing feature name on mouseover
const
nameOverlay
=
document
.
createElement
(
"
div
"
);
viewer
.
container
.
appendChild
(
nameOverlay
);
nameOverlay
.
className
=
"
backdrop
"
;
nameOverlay
.
style
.
display
=
"
none
"
;
nameOverlay
.
style
.
position
=
"
absolute
"
;
nameOverlay
.
style
.
bottom
=
"
0
"
;
nameOverlay
.
style
.
left
=
"
0
"
;
nameOverlay
.
style
[
"
pointer-events
"
]
=
"
none
"
;
nameOverlay
.
style
.
padding
=
"
4px
"
;
nameOverlay
.
style
.
backgroundColor
=
"
black
"
;
nameOverlay
.
style
.
color
=
"
white
"
;
nameOverlay
.
style
.
fontFamily
=
"
Fira Sans, sans-serif
"
;
nameOverlay
.
style
.
fontSize
=
"
0.75em
"
;
// Information about the currently selected feature
const
selected
=
{
feature
:
undefined
,
originalColor
:
new
Cesium
.
Color
(),
};
// An entity object which will hold info about the currently selected feature for infobox display
const
selectedEntity
=
new
Cesium
.
Entity
();
// Get default left click handler for when a feature is not picked on left click
const
clickHandler
=
viewer
.
screenSpaceEventHandler
.
getInputAction
(
Cesium
.
ScreenSpaceEventType
.
LEFT_CLICK
);
// Change the feature color on mouse over
// Information about the currently highlighted feature
const
highlighted
=
{
feature
:
undefined
,
originalColor
:
new
Cesium
.
Color
(),
};
// Color a feature on hover.
viewer
.
screenSpaceEventHandler
.
setInputAction
(
function
onMouseMove
(
movement
)
{
// If a feature was previously highlighted, undo the highlight
if
(
Cesium
.
defined
(
highlighted
.
feature
))
{
highlighted
.
feature
.
color
=
highlighted
.
originalColor
;
highlighted
.
feature
=
undefined
;
}
// Pick a new feature
const
pickedFeature
=
viewer
.
scene
.
pick
(
movement
.
endPosition
);
if
(
!
Cesium
.
defined
(
pickedFeature
))
{
nameOverlay
.
style
.
display
=
"
none
"
;
return
;
}
// A feature was picked, so show it's overlay content
nameOverlay
.
style
.
display
=
"
block
"
;
nameOverlay
.
style
.
bottom
=
viewer
.
canvas
.
clientHeight
-
movement
.
endPosition
.
y
+
"
px
"
;
nameOverlay
.
style
.
left
=
movement
.
endPosition
.
x
+
"
px
"
;
let
name
=
pickedFeature
.
getProperty
(
"
_gebaeude
"
);
if
(
!
Cesium
.
defined
(
name
))
{
name
=
pickedFeature
.
getProperty
(
"
id
"
);
}
nameOverlay
.
textContent
=
name
;
// Highlight the feature if it's not already selected.
if
(
pickedFeature
!==
selected
.
feature
)
{
highlighted
.
feature
=
pickedFeature
;
Cesium
.
Color
.
clone
(
pickedFeature
.
color
,
highlighted
.
originalColor
);
pickedFeature
.
color
=
Cesium
.
Color
.
GREY
;
}
},
Cesium
.
ScreenSpaceEventType
.
MOUSE_MOVE
);
// Color a feature on selection and show metadata in the InfoBox.
viewer
.
screenSpaceEventHandler
.
setInputAction
(
function
onLeftClick
(
movement
)
{
// If a feature was previously selected, undo the highlight
if
(
Cesium
.
defined
(
selected
.
feature
))
{
selected
.
feature
.
color
=
selected
.
originalColor
;
selected
.
feature
=
undefined
;
}
// Pick a new feature
const
pickedFeature
=
viewer
.
scene
.
pick
(
movement
.
position
);
if
(
!
Cesium
.
defined
(
pickedFeature
))
{
clickHandler
(
movement
);
return
;
}
// Select the feature if it's not already selected
if
(
selected
.
feature
===
pickedFeature
)
{
return
;
}
selected
.
feature
=
pickedFeature
;
// Save the selected feature's original color
if
(
pickedFeature
===
highlighted
.
feature
)
{
Cesium
.
Color
.
clone
(
highlighted
.
originalColor
,
selected
.
originalColor
);
highlighted
.
feature
=
undefined
;
}
else
{
Cesium
.
Color
.
clone
(
pickedFeature
.
color
,
selected
.
originalColor
);
}
// Highlight newly selected feature
pickedFeature
.
color
=
Cesium
.
Color
.
LIME
;
// Set feature infobox description
const
featureName
=
pickedFeature
.
getProperty
(
"
name
"
);
selectedEntity
.
name
=
featureName
;
selectedEntity
.
description
=
'
Loading <div class="cesium-infoBox-loading"></div>
'
;
viewer
.
selectedEntity
=
selectedEntity
;
selectedEntity
.
description
=
`
const
activate3DTileFeaturePicking
=
function
()
{
// HTML overlay for showing feature name on mouseover
const
nameOverlay
=
document
.
createElement
(
"
div
"
);
viewer
.
container
.
appendChild
(
nameOverlay
);
nameOverlay
.
className
=
"
backdrop
"
;
nameOverlay
.
style
.
display
=
"
none
"
;
nameOverlay
.
style
.
position
=
"
absolute
"
;
nameOverlay
.
style
.
bottom
=
"
0
"
;
nameOverlay
.
style
.
left
=
"
0
"
;
nameOverlay
.
style
[
"
pointer-events
"
]
=
"
none
"
;
nameOverlay
.
style
.
padding
=
"
4px
"
;
nameOverlay
.
style
.
backgroundColor
=
"
black
"
;
nameOverlay
.
style
.
color
=
"
white
"
;
nameOverlay
.
style
.
fontFamily
=
"
Fira Sans, sans-serif
"
;
nameOverlay
.
style
.
fontSize
=
"
0.75em
"
;
// Information about the currently selected feature
const
selected
=
{
feature
:
undefined
,
originalColor
:
new
Cesium
.
Color
(),
};
// An entity object which will hold info about the currently selected feature for infobox display
const
selectedEntity
=
new
Cesium
.
Entity
();
// Get default left click handler for when a feature is not picked on left click
const
clickHandler
=
viewer
.
screenSpaceEventHandler
.
getInputAction
(
Cesium
.
ScreenSpaceEventType
.
LEFT_CLICK
);
// Change the feature color on mouse over
// Information about the currently highlighted feature
const
highlighted
=
{
feature
:
undefined
,
originalColor
:
new
Cesium
.
Color
(),
};
// Color a feature on hover.
viewer
.
screenSpaceEventHandler
.
setInputAction
(
function
onMouseMove
(
movement
)
{
// If a feature was previously highlighted, undo the highlight
if
(
Cesium
.
defined
(
highlighted
.
feature
))
{
highlighted
.
feature
.
color
=
highlighted
.
originalColor
;
highlighted
.
feature
=
undefined
;
}
// Pick a new feature
const
pickedFeature
=
viewer
.
scene
.
pick
(
movement
.
endPosition
);
if
(
!
Cesium
.
defined
(
pickedFeature
))
{
nameOverlay
.
style
.
display
=
"
none
"
;
return
;
}
// A feature was picked, so show it's overlay content
nameOverlay
.
style
.
display
=
"
block
"
;
nameOverlay
.
style
.
bottom
=
viewer
.
canvas
.
clientHeight
-
movement
.
endPosition
.
y
+
"
px
"
;
nameOverlay
.
style
.
left
=
movement
.
endPosition
.
x
+
"
px
"
;
let
name
=
pickedFeature
.
getProperty
(
"
_gebaeude
"
);
if
(
!
Cesium
.
defined
(
name
))
{
name
=
pickedFeature
.
getProperty
(
"
id
"
);
}
nameOverlay
.
textContent
=
name
;
// Highlight the feature if it's not already selected.
if
(
pickedFeature
!==
selected
.
feature
)
{
highlighted
.
feature
=
pickedFeature
;
Cesium
.
Color
.
clone
(
pickedFeature
.
color
,
highlighted
.
originalColor
);
pickedFeature
.
color
=
Cesium
.
Color
.
GREY
;
}
},
Cesium
.
ScreenSpaceEventType
.
MOUSE_MOVE
);
// Color a feature on selection and show metadata in the InfoBox.
viewer
.
screenSpaceEventHandler
.
setInputAction
(
function
onLeftClick
(
movement
)
{
// If a feature was previously selected, undo the highlight
if
(
Cesium
.
defined
(
selected
.
feature
))
{
selected
.
feature
.
color
=
selected
.
originalColor
;
selected
.
feature
=
undefined
;
}
// Pick a new feature
const
pickedFeature
=
viewer
.
scene
.
pick
(
movement
.
position
);
if
(
!
Cesium
.
defined
(
pickedFeature
))
{
clickHandler
(
movement
);
return
;
}
// Select the feature if it's not already selected
if
(
selected
.
feature
===
pickedFeature
)
{
return
;
}
selected
.
feature
=
pickedFeature
;
// Save the selected feature's original color
if
(
pickedFeature
===
highlighted
.
feature
)
{
Cesium
.
Color
.
clone
(
highlighted
.
originalColor
,
selected
.
originalColor
);
highlighted
.
feature
=
undefined
;
}
else
{
Cesium
.
Color
.
clone
(
pickedFeature
.
color
,
selected
.
originalColor
);
}
// Highlight newly selected feature
pickedFeature
.
color
=
Cesium
.
Color
.
LIME
;
// Set feature infobox description
const
featureName
=
pickedFeature
.
getProperty
(
"
name
"
);
selectedEntity
.
name
=
featureName
;
selectedEntity
.
description
=
'
Loading <div class="cesium-infoBox-loading"></div>
'
;
viewer
.
selectedEntity
=
selectedEntity
;
selectedEntity
.
description
=
`
<table class="cesium-infoBox-defaultTable">
<tbody>
<tr><th>Bau</th><td>
...
...
@@ -290,57 +290,62 @@ const activate3DTileFeaturePicking = function () {
</table>
`
;
const
clickedBuilding
=
pickedFeature
.
getProperty
(
"
_gebaeude
"
);
const
clickedBuildingDatastreamId
=
getDatastreamIdFromBuildingNumber
(
clickedBuilding
,
"
vl
"
,
"
60min
"
);
const
clickedBuilding
=
pickedFeature
.
getProperty
(
"
_gebaeude
"
);
const
clickedBuildingDatastreamId
=
getDatastreamIdFromBuildingNumber
(
clickedBuilding
,
"
vl
"
,
"
60min
"
);
const
BASE_URL_OBSERVATIONS
=
getObservationsUrl
(
BASE_URL
,
clickedBuildingDatastreamId
);
const
PARAM_FILTER
=
createTemporalFilterString
(
"
2020-01-01
"
,
"
2021-01-01
"
);
const
axiosGetRequest
=
axios
.
get
(
BASE_URL_OBSERVATIONS
,
{
params
:
{
"
$resultFormat
"
:
PARAM_RESULT_FORMAT
,
"
$orderBy
"
:
PARAM_ORDER_BY
,
"
$filter
"
:
PARAM_FILTER
,
"
$select
"
:
PARAM_SELECT
,
},
});
const
BASE_URL_OBSERVATIONS
=
getObservationsUrl
(
BASE_URL
,
clickedBuildingDatastreamId
);
const
PARAM_FILTER
=
createTemporalFilterString
(
"
2020-01-01
"
,
"
2021-01-01
"
);
const
axiosGetRequest
=
axios
.
get
(
BASE_URL_OBSERVATIONS
,
{
params
:
{
"
$resultFormat
"
:
PARAM_RESULT_FORMAT
,
"
$orderBy
"
:
PARAM_ORDER_BY
,
"
$filter
"
:
PARAM_FILTER
,
"
$select
"
:
PARAM_SELECT
,
},
});
// Get "ALL" the Observations that satisfy our query
followNextLink
(
axiosGetRequest
)
.
then
((
success
)
=>
{
const
successValue
=
success
.
data
.
value
;
// Get "ALL" the Observations that satisfy our query
followNextLink
(
axiosGetRequest
)
.
then
((
success
)
=>
{
const
successValue
=
success
.
data
.
value
;
// Array that will hold the combined observations
const
combinedObservations
=
[];
// 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
;
successValue
.
forEach
((
dataObj
)
=>
{
// Each page of results will have a dataArray that holds the observations
const
dataArrays
=
dataObj
.
dataArray
;
combinedObservations
.
push
(...
dataArrays
);
});
// DEBUG: Check total number of observations
console
.
log
(
combinedObservations
.
length
);
// DEBUG: Print the array of observations
console
.
log
(
combinedObservations
);
return
combinedObservations
;
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
})
.
then
((
observationArr
)
=>
{
var
agg
=
aggregateResponse
(
observationArr
,
0
,
'
mean
'
);
console
.
log
(
agg
);
drawHeatMapHC
(
formatSTAResponseForHeatMap
(
observationArr
));
drawLineChartHC
(
formatSTAResponseForLineChart
(
observationArr
));
});
},
Cesium
.
ScreenSpaceEventType
.
LEFT_CLICK
);
combinedObservations
.
push
(...
dataArrays
);
});
// DEBUG: Check total number of observations
console
.
log
(
combinedObservations
.
length
);
// DEBUG: Print the array of observations
console
.
log
(
combinedObservations
);
return
combinedObservations
;
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
})
.
then
((
observationArr
)
=>
{
drawHeatMapHC
(
formatSTAResponseForHeatMap
(
observationArr
));
drawLineChartHC
(
formatSTAResponseForLineChart
(
observationArr
));
});
},
Cesium
.
ScreenSpaceEventType
.
LEFT_CLICK
);
};
activate3DTileFeaturePicking
();
activate3DTileFeaturePicking
();
\ No newline at end of file
This diff is collapsed.
Click to expand it.
public/js/appChart.js
+
422
-
240
View file @
86bff050
...
...
@@ -13,72 +13,72 @@ export const BASE_URL = "http://193.196.39.91:8080/frost-icity-tp31/v1.1";
* @param {*} samplingRate String representing the sampling rate of the observations
* @returns {Number} Datastream corresponding to the input building
*/
export
const
getDatastreamIdFromBuildingNumber
=
function
(
buildingNumber
,
phenomenon
,
samplingRate
export
const
getDatastreamIdFromBuildingNumber
=
function
(
buildingNumber
,
phenomenon
,
samplingRate
)
{
const
buildingToDatastreamMapping
=
{
101
:
{
vl
:
{
"
15min
"
:
"
69
"
,
"
60min
"
:
"
75
"
},
rl
:
{
"
15min
"
:
"
81
"
,
"
60min
"
:
"
87
"
},
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
"
},
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
"
},
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
"
},
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
"
},
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
(
!
buildingNumber
)
return
;
// check if building is contained in mapping object
if
(
!
(
buildingNumber
in
buildingToDatastreamMapping
))
return
;
const
datastreamIdMatched
=
Number
(
buildingToDatastreamMapping
[
buildingNumber
][
phenomenon
][
samplingRate
]
);
return
datastreamIdMatched
;
const
buildingToDatastreamMapping
=
{
101
:
{
vl
:
{
"
15min
"
:
"
69
"
,
"
60min
"
:
"
75
"
},
rl
:
{
"
15min
"
:
"
81
"
,
"
60min
"
:
"
87
"
},
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
"
},
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
"
},
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
"
},
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
"
},
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
(
!
buildingNumber
)
return
;
// check if building is contained in mapping object
if
(
!
(
buildingNumber
in
buildingToDatastreamMapping
))
return
;
const
datastreamIdMatched
=
Number
(
buildingToDatastreamMapping
[
buildingNumber
][
phenomenon
][
samplingRate
]
);
return
datastreamIdMatched
;
};
/**
...
...
@@ -86,10 +86,10 @@ export const getDatastreamIdFromBuildingNumber = function (
* @param {Number} datastreamID - Integer representing the Datastream ID
* @returns {String} URL string for fetching the Observations corresponding to a Datastream
*/
export
const
getObservationsUrl
=
function
(
baseUrl
,
datastreamID
)
{
if
(
!
datastreamID
)
return
;
const
fullDatastreamURL
=
`
${
baseUrl
}
/Datastreams(
${
datastreamID
}
)/Observations`
;
return
fullDatastreamURL
;
export
const
getObservationsUrl
=
function
(
baseUrl
,
datastreamID
)
{
if
(
!
datastreamID
)
return
;
const
fullDatastreamURL
=
`
${
baseUrl
}
/Datastreams(
${
datastreamID
}
)/Observations`
;
return
fullDatastreamURL
;
};
/**
...
...
@@ -98,10 +98,10 @@ export const getObservationsUrl = function (baseUrl, datastreamID) {
* @param {String} dateStop Stop date in YYYY-MM-DD format
* @returns {String} Temporal filter string
*/
export
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
;
export
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);
...
...
@@ -119,31 +119,218 @@ export const PARAM_SELECT = "result,phenomenonTime";
// },
// });
/**
*
* @param {JSON} obj JSON object on which to replace a specific key.
* @param {String} oldKey is the old key in the JSON to be renamed to newKey
* @param {String} newKey is the key that should replace the oldKey
* usage: myjson.forEach((obj) => renameKey(obj, "oldkey", "newkey"));
*/
function
renameKey
(
obj
,
oldKey
,
newKey
)
{
obj
[
newKey
]
=
obj
[
oldKey
];
delete
obj
[
oldKey
];
}
/**
*
* @param {Array} arr is the Array to be converted into a JSON
* @returns {JSON} stringToJsonObject
*/
function
convertArray2JSON
(
arr
)
{
var
arrayToString
=
JSON
.
stringify
(
Object
.
assign
({},
arr
));
// convert array to string
var
stringToJsonObject
=
JSON
.
parse
(
arrayToString
);
// convert string to json object
return
stringToJsonObject
;
}
function
createDateFromDateTimeString
(
jsonData
)
{
let
datx
=
[];
let
daty
=
[];
const
MONTH
=
[
'
Jan
'
,
'
Feb
'
,
'
Mar
'
,
'
Apr
'
,
'
May
'
,
'
Jun
'
,
'
Jul
'
,
'
Aug
'
,
'
Sep
'
,
'
Oct
'
,
'
Nov
'
,
'
Dec
'
];
for
(
var
u
=
0
;
u
<
jsonData
.
length
;
u
++
)
{
daty
.
push
(
jsonData
[
u
].
temperature
);
let
date
=
new
Date
(
jsonData
[
u
].
datetime
);
let
datum
=
date
.
getDate
();
let
month
=
MONTH
[
date
.
getMonth
()];
let
hour
=
date
.
getHours
()
+
"
:00
"
;
let
newDateStr
=
datum
+
"
/
"
+
month
+
"
-
"
+
hour
;
datx
.
push
(
newDateStr
);
}
return
[
datx
,
daty
];
}
/**
* Format the response from SensorThings API to make it suitable for heatmap
* @param {Array} obsArray Response from SensorThings API as array
* @param {Int8} hours Number of hours to aggregate over. If hours=0,
* it will be aggregated by date (e.g. all values recorded on 1st of May etc...)
* if hours to any number (also hours=24) data will be aggregated over every 24hours,
* even if the date changes (e.g. data from 1st of May from 10pm up 9pm on 2nd of May etc.)
* @param {String} method Specify how to aggregate date. Use: 'mean' = default, 'sum', 'min' or 'max'
* @returns {Array} Aggregated Response
*/
export
const
aggregateResponse
=
function
(
obsArray
,
hours
,
method
)
{
if
(
!
obsArray
)
return
;
if
(
hours
<
0
)
return
;
// check if we have a defined method or the method specified is accepted, rest is handeled in switch/case below
if
(
method
==
undefined
)
method
=
'
mean
'
;
// convert obsArray to json
let
jsonFromArr
=
[];
for
(
var
i
=
0
;
i
<
obsArray
.
length
;
i
++
)
{
jsonFromArr
.
push
(
convertArray2JSON
(
obsArray
[
i
]));
}
// rename the keys in the jason
jsonFromArr
.
forEach
((
obj
)
=>
renameKey
(
obj
,
"
0
"
,
"
datetime
"
));
let
jsonData
=
jsonFromArr
;
jsonData
.
forEach
((
obj
)
=>
renameKey
(
obj
,
"
1
"
,
"
temperature
"
));
let
newOutput
=
[];
var
aggDates
=
[];
var
aggregatedVals
=
[];
var
vals
=
[];
// store values temporarily to use for processing
if
(
hours
==
0
)
{
// i.e. aggregate over one Date / Day
var
currentDate
;
var
oldDate
;
for
(
var
d
=
0
;
d
<
jsonData
.
length
;
d
++
)
{
let
tmpDate
=
new
Date
(
jsonData
[
d
].
datetime
);
currentDate
=
tmpDate
.
getDate
();
// gets the day of the month 1...31
if
(
d
===
0
)
oldDate
=
currentDate
;
if
(
currentDate
==
oldDate
)
{
vals
.
push
(
jsonData
[
d
].
temperature
);
}
else
{
aggDates
.
push
(
new
Date
(
tmpDate
-
1
));
if
(
vals
.
length
==
0
)
{
aggregatedVals
.
push
(
-
1
);
oldDate
=
currentDate
;
continue
;
}
switch
(
method
)
{
case
'
mean
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
a
+
b
/
vals
.
length
;
},
0
));
break
;
case
'
sum
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
a
+
b
;
},
0
));
break
;
case
'
min
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
Math
.
min
(
a
,
b
);
}));
break
;
case
'
max
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
Math
.
max
(
a
,
b
);
}));
break
;
default
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
a
+
b
/
vals
.
length
;
},
0
));
}
vals
=
[];
// clear the daily value vector
vals
.
push
(
jsonData
[
d
].
temperature
);
// now push first entry of new day into my temp value vector.
}
oldDate
=
currentDate
;
}
// end of for loop
// create output to be in the same List format as the original data from obsArray.
for
(
let
i
=
0
;
i
<
aggregatedVals
.
length
;
i
++
)
{
newOutput
.
push
([
aggDates
[
i
].
toISOString
(),
aggregatedVals
[
i
]]);
}
}
else
{
// i.e. aggregate over X hours, irrespective of the day.
let
cnt
=
0
;
let
cumHours
=
0
;
for
(
var
d
=
0
;
d
<
jsonData
.
length
;
d
++
)
{
if
(
cnt
<
hours
)
{
vals
.
push
(
jsonData
[
d
].
temperature
);
cnt
++
;
}
else
{
cumHours
+=
cnt
;
cnt
=
0
;
aggDates
.
push
(
cumHours
);
if
(
vals
.
length
==
0
)
{
aggregatedVals
.
push
(
-
1
);
continue
;
}
switch
(
method
)
{
case
'
mean
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
a
+
b
/
vals
.
length
;
},
0
));
break
;
case
'
sum
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
a
+
b
;
},
0
));
break
;
case
'
min
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
Math
.
min
(
a
,
b
);
}));
break
;
case
'
max
'
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
Math
.
max
(
a
,
b
);
}));
break
;
default
:
aggregatedVals
.
push
(
vals
.
reduce
(
function
(
a
,
b
)
{
return
a
+
b
/
vals
.
length
;
},
0
));
}
vals
=
[];
// clear the daily value vector
vals
.
push
(
jsonData
[
d
].
temperature
);
// now push first entry of new day into my temp value vector.
cnt
++
;
}
oldDate
=
currentDate
;
}
// end of for loop
// create output to be in the same List format as the original data from obsArray.
for
(
let
i
=
0
;
i
<
aggregatedVals
.
length
;
i
++
)
{
newOutput
.
push
([
aggDates
[
i
],
aggregatedVals
[
i
]]);
}
}
// end else
return
newOutput
;
}
/**
* 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
*/
export
const
formatSTAResponseForHeatMap
=
function
(
obsArray
)
{
if
(
!
obsArray
)
return
;
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
;
export
const
formatSTAResponseForHeatMap
=
function
(
obsArray
)
{
if
(
!
obsArray
)
return
;
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
;
};
/**
...
...
@@ -151,94 +338,91 @@ export const formatSTAResponseForHeatMap = function (obsArray) {
* @param {Array} formattedObsArrayForHeatmap Response from SensorThings API formatted for use in a heatmap
* @returns {Object} Highcharts library heatmap object
*/
export
const
drawHeatMapHC
=
function
(
formattedObsArrayForHeatmap
)
{
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
:
formattedObsArrayForHeatmap
,
boostThreshold
:
100
,
borderWidth
:
0
,
nullColor
:
"
#525252
"
,
colsize
:
24
*
36
e5
,
// one day
tooltip
:
{
headerFormat
:
"
Temperature<br/>
"
,
pointFormat
:
"
{point.x:%e %b, %Y} {point.y}:00: <b>{point.value} ℃</b>
"
,
export
const
drawHeatMapHC
=
function
(
formattedObsArrayForHeatmap
)
{
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
,
},
turboThreshold
:
Number
.
MAX_VALUE
,
// #3404, remove after 4.0.5 release
},
],
});
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
:
formattedObsArrayForHeatmap
,
boostThreshold
:
100
,
borderWidth
:
0
,
nullColor
:
"
#525252
"
,
colsize
:
24
*
36
e5
,
// 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
},
],
});
};
/**
...
...
@@ -246,15 +430,15 @@ export const drawHeatMapHC = function (formattedObsArrayForHeatmap) {
* @param {Array} obsArray Response from SensorThings API as array
* @returns {Array} Array of formatted observations suitable for use in a line chart
*/
export
const
formatSTAResponseForLineChart
=
function
(
obsArray
)
{
if
(
!
obsArray
)
return
;
const
dataSTAFormatted
=
[];
obsArray
.
forEach
((
result
)
=>
{
const
timestampObs
=
new
Date
(
result
[
0
].
slice
(
0
,
-
1
)).
getTime
();
// slice() removes trailing "Z" character in timestamp
const
valueObs
=
result
[
1
];
dataSTAFormatted
.
push
([
timestampObs
,
valueObs
]);
});
return
dataSTAFormatted
;
export
const
formatSTAResponseForLineChart
=
function
(
obsArray
)
{
if
(
!
obsArray
)
return
;
const
dataSTAFormatted
=
[];
obsArray
.
forEach
((
result
)
=>
{
const
timestampObs
=
new
Date
(
result
[
0
].
slice
(
0
,
-
1
)).
getTime
();
// slice() removes trailing "Z" character in timestamp
const
valueObs
=
result
[
1
];
dataSTAFormatted
.
push
([
timestampObs
,
valueObs
]);
});
return
dataSTAFormatted
;
};
/**
...
...
@@ -262,38 +446,36 @@ export const formatSTAResponseForLineChart = function (obsArray) {
* @param {Array} formattedObsArrayForLineChart - Response from SensorThings API formatted for use in a line chart
* @returns {Object} Highcharts library line chart object
*/
export
const
drawLineChartHC
=
function
(
formattedObsArrayForLineChart
)
{
// Create the chart
Highcharts
.
stockChart
(
"
chart-line
"
,
{
chart
:
{
zoomType
:
"
x
"
,
},
rangeSelector
:
{
selected
:
1
,
},
title
:
{
text
:
"
Inlet flow (Vorlauf)
"
,
"
align
"
:
"
left
"
,
},
subtitle
:
{
text
:
"
Temperature variation by hour in 2020
"
,
align
:
"
left
"
,
},
series
:
[
{
name
:
"
AAPL
"
,
data
:
formattedObsArrayForLineChart
,
tooltip
:
{
valueDecimals
:
2
,
export
const
drawLineChartHC
=
function
(
formattedObsArrayForLineChart
)
{
// Create the chart
Highcharts
.
stockChart
(
"
chart-line
"
,
{
chart
:
{
zoomType
:
"
x
"
,
},
rangeSelector
:
{
selected
:
1
,
},
turboThreshold
:
Number
.
MAX_VALUE
,
// #3404, remove after 4.0.5 release
},
],
});
title
:
{
text
:
"
Inlet flow (Vorlauf)
"
,
"
align
"
:
"
left
"
,
},
subtitle
:
{
text
:
"
Temperature variation by hour in 2020
"
,
align
:
"
left
"
,
},
series
:
[{
name
:
"
AAPL
"
,
data
:
formattedObsArrayForLineChart
,
tooltip
:
{
valueDecimals
:
2
,
},
turboThreshold
:
Number
.
MAX_VALUE
,
// #3404, remove after 4.0.5 release
},
],
});
};
/**
...
...
@@ -303,26 +485,26 @@ export const drawLineChartHC = function (formattedObsArrayForLineChart) {
* @param {Object} responsePromise Promise object
* @returns {Object} - Object containing results from all the "@iot.nextLink" links
*/
export
const
followNextLink
=
function
(
responsePromise
)
{
if
(
!
responsePromise
)
return
;
return
responsePromise
.
then
(
function
(
lastSuccess
)
{
if
(
lastSuccess
.
data
[
"
@iot.nextLink
"
])
{
return
followNextLink
(
axios
.
get
(
lastSuccess
.
data
[
"
@iot.nextLink
"
])
).
then
(
function
(
nextLinkSuccess
)
{
nextLinkSuccess
.
data
.
value
=
lastSuccess
.
data
.
value
.
concat
(
nextLinkSuccess
.
data
.
value
);
return
nextLinkSuccess
;
export
const
followNextLink
=
function
(
responsePromise
)
{
if
(
!
responsePromise
)
return
;
return
responsePromise
.
then
(
function
(
lastSuccess
)
{
if
(
lastSuccess
.
data
[
"
@iot.nextLink
"
])
{
return
followNextLink
(
axios
.
get
(
lastSuccess
.
data
[
"
@iot.nextLink
"
])
).
then
(
function
(
nextLinkSuccess
)
{
nextLinkSuccess
.
data
.
value
=
lastSuccess
.
data
.
value
.
concat
(
nextLinkSuccess
.
data
.
value
);
return
nextLinkSuccess
;
});
}
else
{
return
lastSuccess
;
}
})
.
catch
(
function
(
err
)
{
console
.
log
(
err
);
});
}
else
{
return
lastSuccess
;
}
})
.
catch
(
function
(
err
)
{
console
.
log
(
err
);
});
};
// Get "ALL" the Observations that satisfy our query
...
...
@@ -352,4 +534,4 @@ export const followNextLink = function (responsePromise) {
// .then((observationArr) => {
// drawHeatMapHC(observationArr);
// drawLineChartHC(observationArr);
// });
// });
\ No newline at end of file
This diff is collapsed.
Click to expand it.
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