Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
UGL
website
Commits
a5a49889
Commit
a5a49889
authored
Feb 18, 2024
by
JOE XMG
Browse files
update
parent
c6bc81bb
Changes
1
Hide whitespace changes
Inline
Side-by-side
public/udigit4icity/index.html
0 → 100644
View file @
a5a49889
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
Integrated Web Map and ECharts
</title>
<link
rel=
"stylesheet"
href=
"https://js.arcgis.com/4.28/esri/css/main.css"
>
<script
src=
"https://cdn.plot.ly/plotly-latest.min.js"
></script>
<script
src=
"https://api.mapbox.com/mapbox.js/v3.3.1/mapbox.js"
></script>
<link
href=
"https://api.mapbox.com/mapbox.js/v3.3.1/mapbox.css"
rel=
"stylesheet"
/>
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/echarts@5.0.0/dist/echarts.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/echarts-gl@5.0.0/dist/echarts-gl.min.css"> -->
<link
rel=
"stylesheet"
href=
"https://js.arcgis.com/4.28/esri/themes/dark/main.css"
/>
<style>
body
{
font-size
:
16px
}
#chartControls
label
,
#chartControls
select
,
#chartControls
button
{
font-size
:
16px
;
margin-bottom
:
15px
}
html
{
margin
:
0
;
padding
:
0
;
height
:
100%
;
overflow
:
hidden
;
}
#usage-pie-chart-container
{
z-index
:
1
;
position
:
absolute
;
top
:
10px
;
right
:
0px
;
width
:
20%
;
height
:
0.2px
;
bottom
:
10%
;
}
#mainMenu
{
position
:
absolute
;
top
:
10px
;
left
:
60px
;
z-index
:
4
;
background-color
:
rgba
(
53
,
51
,
51
,
0.8
);
padding
:
8px
;
border-radius
:
5px
;
display
:
flex
;
flex-direction
:
column
;
}
#mainMenu
label
{
margin-bottom
:
5px
;
}
#mainMenu
button
{
margin-top
:
10px
;
padding
:
5px
10px
;
cursor
:
pointer
;
}
#main-menu
{
width
:
20%
;
height
:
100%
;
background-color
:
#2c3e50
;
color
:
rgb
(
91
,
89
,
89
);
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
padding-top
:
20px
;
}
#container
{
display
:
flex
;
width
:
100%
;
height
:
100vh
;
flex-direction
:
row
;
}
#main
{
width
:
36%
;
height
:
65%
;
position
:
relative
;
top
:
35%
;
right
:
0
;
z-index
:
1
;
}
#viewDiv
{
width
:
100%
;
height
:
100%
;
position
:
absolute
;
top
:
0
;
right
:
0
;
}
#spaceTimeCubeContainer
{
display
:
none
;
}
.legend-building
{
position
:
absolute
;
bottom
:
10px
;
right
:
10px
;
padding
:
5px
;
background-color
:
rgba
(
255
,
255
,
255
,
0.8
);
border-radius
:
5px
;
z-index
:
1
;
display
:
flex
;
flex-direction
:
column
;
}
.legend
{
position
:
absolute
;
top
:
90%
;
left
:
36%
;
padding
:
5px
;
background-color
:
rgba
(
255
,
255
,
255
,
0.8
);
border-radius
:
5px
;
z-index
:
1
;
display
:
flex
;
flex-direction
:
column
;
}
.legend-item
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
5px
;
cursor
:
pointer
;
}
.legend-item
div
{
align-items
:
relative
;
width
:
20px
;
height
:
20px
;
margin-right
:
10px
;
}
#chartControls
{
position
:
absolute
;
top
:
34px
;
left
:
20px
;
z-index
:
3
;
}
#plotlyContainer
{
height
:
100vh
;
position
:
absolute
;
bottom
:
0
;
left
:
0
;
top
:
2%
;
right
:
100
;
width
:
100%
;
z-index
:
3
;
}
.dark-popup-content
{
color
:
#232020
;
background-color
:
#171515
;
/* to dark gray */
}
</style>
</head>
<body>
<div
id=
"container"
>
<div
id=
"usage-pie-chart-container"
style=
"width: 300px; height: 300px; "
>
<canvas
id=
"usage-pie-chart"
style=
"width: 100%; height: 100%;"
></canvas>
</div>
<div
id=
"main"
></div>
<div
id=
"viewDiv"
></div>
<div
id=
"timeSlider"
></div>
<div
class=
"legend"
></div>
<div
class=
"legend-building"
></div>
<div
id=
"spaceTimeCubeContainer"
>
<div
id=
"plotlyContainer"
></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
id=
"mainMenu"
>
<label
for=
"measurementType"
>
Select Measurement Type:
</label>
<select
id=
"measurementType"
>
<option
value=
"temperature"
>
Temperature °C
</option>
<option
value=
"humidity"
>
Humidity %
</option>
<option
value=
"illuminance"
>
Illuminance in Lux
</option>
</select>
<label
for=
"chartType"
>
Select Chart Type:
</label>
<select
id=
"chartType"
>
<option
value=
"line"
>
Line Chart
</option>
<option
value=
"bar"
>
Bar Chart
</option>
</select>
<button
id=
"loadDatastream"
>
Load Datastream
</button>
<button
id=
"toggleSpaceTime"
>
Bus Space-Time Visualization
</button>
<button
id=
"toggle3DButton"
>
3D City Building
</button>
</div>
<script
src=
"https://cdn.plot.ly/plotly-latest.min.js"
></script>
<script
src=
"https://api.mapbox.com/mapbox.js/v3.3.1/mapbox.js"
></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/echarts@5.0.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts-gl@5.0.0/dist/echarts-gl.min.js"></script> -->
<script
src=
"https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"
integrity=
"sha512-k37wQcV4v2h6jgYf5IUz1MoSKPpDs630XGSmCaCCOXxy2awgAWKHGZWr9nMyGgk3IOxA1NxdkN8r1JHgkUtMoQ=="
crossorigin=
"anonymous"
referrerpolicy=
"no-referrer"
></script>
<script
src=
"https://cdn.jsdelivr.net/npm/chart.js"
></script>
<script
src=
"https://code.highcharts.com/highcharts.js"
></script>
<script
src=
"https://js.arcgis.com/4.28/"
></script>
<script>
const
data
=
[
];
//to ensure if plotly and mapbox are loaded
if
(
window
.
Plotly
&&
window
.
L
)
{
document
.
getElementById
(
"
loadDatastream
"
).
addEventListener
(
"
click
"
,
function
()
{
var
selectedMeasurementType
=
document
.
getElementById
(
"
measurementType
"
).
value
;
var
selectedChartType
=
document
.
getElementById
(
"
chartType
"
).
value
;
// Mapping from sensor location to their corresponding Datastream id
var
datastreamIds
=
{
"
Co-working
"
:
{
illuminance
:
7
,
temperature
:
8
,
humidity
:
9
},
"
Eaves
"
:
{
illuminance
:
1
,
temperature
:
2
,
humidity
:
3
},
"
Cafeteria
"
:
{
illuminance
:
4
,
temperature
:
5
,
humidity
:
6
}
};
//adding color for each
var
locationColors
=
{
"
Co-working
"
:
'
orange
'
,
"
Eaves
"
:
'
#229954
'
,
"
Cafeteria
"
:
'
#355EC2
'
};
var
resultData
=
[];
Object
.
entries
(
datastreamIds
).
forEach
(([
location
,
types
])
=>
{
var
datastreamId
=
types
[
selectedMeasurementType
];
var
datastreamUrl
=
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Datastreams(
"
+
datastreamId
+
"
)/Observations?$select=result,phenomenonTime&$top=10000
"
;
fetch
(
datastreamUrl
)
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
var
locationData
=
data
.
value
.
filter
(
item
=>
item
.
result
!==
null
&&
item
.
result
>=
1
)
.
map
(
item
=>
({
phenomenonTime
:
new
Date
(
item
.
phenomenonTime
),
result
:
item
.
result
}));
resultData
.
push
({
location
:
location
,
data
:
locationData
});
if
(
resultData
.
length
===
Object
.
keys
(
datastreamIds
).
length
)
{
createChart
(
resultData
,
selectedMeasurementType
,
selectedChartType
,
locationColors
);
}
})
.
catch
(
error
=>
{
console
.
error
(
"
Error loading data:
"
,
error
);
});
});
});
function
createChart
(
resultData
,
selectedMeasurementType
,
selectedChartType
,
locationColors
)
{
var
chartDom
=
document
.
getElementById
(
'
main
'
);
var
myChart
=
echarts
.
init
(
chartDom
);
myChart
.
setOption
({
backgroundColor
:
'
dark-gray
'
,
title
:
{
text
:
selectedMeasurementType
.
charAt
(
0
).
toUpperCase
()
+
selectedMeasurementType
.
slice
(
1
)
+
'
Results at Different Sensor Locations
'
,
left
:
'
center
'
,
textStyle
:
{
color
:
'
white
'
,
fontSize
:
20
,
},
},
tooltip
:
{
trigger
:
'
axis
'
,
backgroundColor
:
'
rgba(0, 0, 0, 0.7)
'
,
formatter
:
function
(
params
)
{
var
timestamp
=
new
Date
(
params
[
0
].
value
[
0
]);
var
formattedTime
=
timestamp
.
toLocaleString
();
var
tooltip
=
'
Timestamp:
'
+
formattedTime
+
'
<br>
'
;
params
.
forEach
(
function
(
item
)
{
tooltip
+=
item
.
seriesName
+
'
:
'
+
item
.
value
[
1
]
+
'
<br>
'
;
});
return
tooltip
;
},
},
grid
:
{
left
:
'
5%
'
,
right
:
'
15%
'
,
top
:
'
10%
'
,
bottom
:
'
10%
'
,
},
xAxis
:
{
name
:
'
Phenomenon Time
'
,
type
:
'
time
'
,
boundaryGap
:
false
,
axisLabel
:
{
color
:
'
green
'
,
fontSize
:
18
,
},
},
yAxis
:
{
name
:
selectedMeasurementType
.
charAt
(
0
).
toUpperCase
()
+
selectedMeasurementType
.
slice
(
1
),
axisLabel
:
{
color
:
'
red
'
,
fontSize
:
18
,
},
},
series
:
resultData
.
map
(
locationData
=>
({
name
:
locationData
.
location
,
type
:
selectedChartType
,
data
:
locationData
.
data
.
map
(
item
=>
[
item
.
phenomenonTime
,
item
.
result
]),
itemStyle
:
{
color
:
locationColors
[
locationData
.
location
],
emphasis
:
{
color
:
'
white
'
,
fontWeight
:
'
bold
'
,
},
},
})),
legend
:
{
data
:
resultData
.
map
(
locationData
=>
locationData
.
location
),
right
:
10
,
bottom
:
10
,
textStyle
:
{
fontSize
:
14
,
color
:
'
white
'
,
},
emphasis
:
{
textStyle
:
{
fontSize
:
14
,
color
:
'
black
'
,
fontWeight
:
'
bold
'
,
},
backgroundColor
:
'
white
'
,
},
},
dataZoom
:
[
{
type
:
'
slider
'
,
xAxisIndex
:
[
0
],
startValue
:
resultData
[
0
].
data
[
0
].
phenomenonTime
,
endValue
:
resultData
[
0
].
data
[
resultData
[
0
].
data
.
length
-
1
].
phenomenonTime
,
zoomLock
:
false
,
showDetail
:
false
,
top
:
'
92%
'
},
{
type
:
'
inside
'
,
xAxisIndex
:
[
0
],
},
],
toolbox
:
{
feature
:
{
dataZoom
:
{
show
:
true
,
},
dataView
:
{
show
:
true
,
},
saveAsImage
:
{
show
:
true
,
},
},
},
});
// adding legend items dynamically
var
legend
=
document
.
querySelector
(
'
.legend
'
);
legend
.
innerHTML
=
''
;
Object
.
keys
(
locationColors
).
forEach
(
location
=>
{
var
color
=
locationColors
[
location
];
var
legendItem
=
createLegendItem
(
color
,
location
,
location
);
legend
.
appendChild
(
legendItem
);
});
}
function
createLegendItem
(
color
,
label
,
id
)
{
const
legendItem
=
document
.
createElement
(
"
div
"
);
legendItem
.
className
=
"
legend-item
"
;
legendItem
.
id
=
id
;
legendItem
.
innerHTML
=
`
<div style="background-color:
${
color
}
;"></div>
<span>
${
label
}
</span>
`
;
return
legendItem
;
}
function
createBuildingLegendItem
(
color
,
label
,
id
)
{
const
legendItem
=
document
.
createElement
(
"
div
"
);
legendItem
.
className
=
"
legend-item
"
;
legendItem
.
id
=
id
;
legendItem
.
innerHTML
=
`
<div style="background-color:
${
color
}
;"></div>
<span>
${
label
}
</span>
`
;
return
legendItem
;
}
// To animating space-time routes directly
animateSpaceTimeRoutes
(
'
https://ogcapi.hft-stuttgart.de/ogc_api_moving_features/collections/bus_1/items
'
);
function
animateSpaceTimeRoutes
(
url
)
{
fetch
(
url
)
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
!
data
||
!
data
.
features
)
{
console
.
error
(
'
Invalid data format:
'
,
data
);
return
;
}
const
frames
=
data
.
features
.
map
((
feature
,
index
)
=>
{
const
coordinates
=
feature
.
geometry
.
coordinates
;
const
timestamps
=
feature
.
properties
.
datetimes
;
if
(
!
Array
.
isArray
(
coordinates
)
||
!
Array
.
isArray
(
timestamps
))
{
console
.
error
(
'
Invalid feature format:
'
,
feature
);
return
;
}
return
{
data
:
[
{
type
:
'
scattermapbox
'
,
lat
:
coordinates
.
map
(
coord
=>
coord
[
1
]),
lon
:
coordinates
.
map
(
coord
=>
coord
[
0
]),
mode
:
'
lines+markers
'
,
marker
:
{
size
:
6
,
color
:
'
black
'
},
line
:
{
color
:
'
black
'
},
name
:
'
Bus route
'
,
},
{
type
:
'
scatter3d
'
,
x
:
coordinates
.
map
(
coord
=>
coord
[
0
]),
y
:
coordinates
.
map
(
coord
=>
coord
[
1
]),
z
:
timestamps
.
map
((
time
,
i
)
=>
new
Date
(
time
).
toISOString
()),
mode
:
'
lines+markers
'
,
line
:
{
color
:
'
dark
'
},
name
:
'
Bus trajectory line
'
,
},
],
name
:
`frame
${
index
+
1
}
`
,
};
});
// to set the layout with mapbox _ access token
const
layout
=
{
mapbox
:
{
style
:
'
light
'
,
center
:
{
lon
:
frames
[
0
].
data
[
0
].
lon
[
0
],
lat
:
frames
[
0
].
data
[
0
].
lat
[
0
]
},
zoom
:
10
,
accesstoken
:
'
pk.eyJ1IjoicmVkaWV0OTk2MyIsImEiOiJjbHJ1cmk5aGUwaDRvMmpuYWM4Z2NqcmZuIn0.c11HYT-5ucd6g6mL03bp3Q
'
,
// Mapbox access token
},
margin
:
{
t
:
0
,
b
:
0
,
l
:
0
,
r
:
0
},
scene
:
{
xaxis
:
{
title
:
'
X Axis
'
,
titlefont
:
{
family
:
'
Arial, sans-serif
'
,
size
:
15
,
color
:
'
black
'
},
tickfont
:
{
family
:
'
Arial, sans-serif
'
,
size
:
15
,
color
:
'
black
'
,
bold
:
true
}
},
yaxis
:
{
title
:
'
Y Axis
'
,
titlefont
:
{
family
:
'
Arial, sans-serif
'
,
size
:
15
,
color
:
'
black
'
},
tickfont
:
{
family
:
'
Arial, sans-serif
'
,
size
:
15
,
color
:
'
black
'
,
bold
:
true
}
},
zaxis
:
{
title
:
'
Time
'
,
titlefont
:
{
family
:
'
Arial, sans-serif
'
,
size
:
16
,
color
:
'
black
'
},
tickfont
:
{
family
:
'
Arial, sans-serif
'
,
size
:
15
,
color
:
'
black
'
,
bold
:
true
}
},
// Common font settings for both
font
:
{
family
:
'
Arial, sans-serif
'
,
size
:
12
,
color
:
'
black
'
,
bold
:
true
}
}
};
// Initialize Plotly container
Plotly
.
newPlot
(
'
plotlyContainer
'
,
frames
[
0
].
data
,
layout
);
Plotly
.
addFrames
(
'
plotlyContainer
'
,
frames
);
})
.
catch
(
error
=>
{
console
.
error
(
'
Error fetching data:
'
,
error
);
});
}
// Function to toggle space-time visualization
document
.
getElementById
(
"
toggleSpaceTime
"
).
addEventListener
(
"
click
"
,
function
()
{
const
plotlyContainer
=
document
.
getElementById
(
"
plotlyContainer
"
);
plotlyContainer
.
style
.
display
=
plotlyContainer
.
style
.
display
===
"
none
"
?
"
block
"
:
"
none
"
;
});
}
else
{
console
.
error
(
"
Plotly or Mapbox not loaded. Check if the libraries are loaded correctly.
"
);
}
function
loadHistoricalRoutes
()
{
// to fetch historical data and update the scene layer
animateHistoricalMovingFeatures
(
'
https://ogcapi.hft-stuttgart.de/ogc_api_moving_features/collections/bus_1/items
'
,
[
226
,
119
,
40
],
'
Bus
'
,
'
Bus
'
,
'
busLegend
'
);
}
</script>
<script>
require
([
"
esri/Map
"
,
"
esri/views/SceneView
"
,
"
esri/layers/SceneLayer
"
,
"
esri/layers/FeatureLayer
"
,
"
esri/Graphic
"
,
"
esri/geometry/Polyline
"
,
"
esri/geometry/Point
"
,
"
esri/symbols/WebStyleSymbol
"
,
"
esri/symbols/SimpleLineSymbol
"
,
"
esri/layers/GraphicsLayer
"
,
"
esri/request
"
,
"
esri/renderers/UniqueValueRenderer
"
,
"
esri/renderers/SimpleRenderer
"
,
"
esri/symbols/SimpleMarkerSymbol
"
,
"
esri/PopupTemplate
"
],
function
(
Map
,
SceneView
,
SceneLayer
,
FeatureLayer
,
Graphic
,
Polyline
,
Point
,
WebStyleSymbol
,
SimpleLineSymbol
,
GraphicsLayer
,
esriRequest
,
UniqueValueRenderer
,
SimpleRenderer
,
SimpleMarkerSymbol
,
PopupTemplate
)
{
const
map
=
new
Map
({
basemap
:
"
dark-gray-vector
"
});
const
view
=
new
SceneView
({
container
:
"
viewDiv
"
,
map
:
map
,
camera
:
{
position
:
{
x
:
130.5180055250
,
y
:
33.7766570370
,
z
:
25000
,
spatialReference
:
{
wkid
:
4326
}
},
heading
:
0
,
tilt
:
0
},
environment
:
{
atmosphereEnabled
:
false
,
// clearer view
lighting
:
{
directShadowsEnabled
:
true
,
}
}
});
const
webStyleSymbol
=
new
WebStyleSymbol
({
name
:
"
Telecom
"
,
styleName
:
"
EsriIconsStyle
"
});
const
popupTemplate
=
new
PopupTemplate
({
title
:
"
{title}
"
,
// dynamic titles
content
:
`
<table >
<tr>
<th>Datastream Description</th>
<td>{Datastream_Description}</td>
</tr>
<tr>
<th>Datastream ID</th>
<td>{Datastream_ID}</td>
</tr>
<tr>
<th>Date</th>
<td>{Date}</td>
</tr>
<tr>
<th>Latitude</th>
<td>{Latitude}</td>
</tr>
<tr>
<th>Longitude</th>
<td>{Longitude}</td>
</tr>
<tr>
<th>observedArea</th>
<td>{observedArea}</td>
</tr>
<tr>
<th>Result</th>
<td>{Result}</td>
</tr>
</table>
`
});
const
featureLayer1
=
new
FeatureLayer
({
url
:
"
https://services.arcgis.com/1lplwYilIlo008hQ/arcgis/rest/services/Datastreams_observation_xy_splited/FeatureServer
"
,
renderer
:
new
SimpleRenderer
({
symbol
:
webStyleSymbol
// specified symbol
}),
popupTemplate
:
popupTemplate
,
outFields
:
[
"
*
"
]
// …include all fields
});
map
.
add
(
featureLayer1
);
const
featureLayer2
=
new
FeatureLayer
({
url
:
"
https://services.arcgis.com/1lplwYilIlo008hQ/arcgis/rest/services/Munakata_City_Road/FeatureServer
"
,
renderer
:
new
SimpleRenderer
({
symbol
:
new
SimpleLineSymbol
({
color
:
'
#7DF9FF
'
,
width
:
0.1
})
}),
outFields
:
[
"
*
"
],
});
map
.
add
(
featureLayer2
);
// Adding hosted 3D building layer
const
hostedLayer
=
new
SceneLayer
({
url
:
"
https://tiles.arcgis.com/tiles/1lplwYilIlo008hQ/arcgis/rest/services/Munakata_City_Building_0h/SceneServer
"
,
renderer
:
new
UniqueValueRenderer
({
field
:
"
usage
"
,
// actual usage name
defaultSymbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
[
253
,
127
,
111
,
1
]
// #fd7f6fff
}
}
]
},
uniqueValueInfos
:
[
{
value
:
"
文教厚生施設
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
'
#FF6384
'
,
}
}
]
}
},
{
value
:
"
商業施設
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#347fb3
"
}
}
]
}
},
{
value
:
"
共同住宅
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#FFCE56
"
,
}
}
]
}
},
{
value
:
"
工場
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#4BC0C0
"
,
}
}
]
}
},
{
value
:
"
Other
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#9966FF
"
,
}
}
]
}
},
{
value
:
"
住宅
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#b57433
"
,
}
}
]
}
},
{
value
:
"
業務施設
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#3ca33c
"
,
}
}
]
}
},
{
value
:
"
農林漁業用施設
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#FF99CC
"
,
}
}
]
}
},
{
value
:
"
運輸倉庫施設
"
,
symbol
:
{
type
:
"
mesh-3d
"
,
symbolLayers
:
[
{
type
:
"
fill
"
,
material
:
{
color
:
"
#BDBDBD
"
,
}
}
]
}
},
]
}),
elevationInfo
:
{
"
mode
"
:
"
on-the-ground
"
,
"
offset
"
:
20
,
"
featureExpressionInfo
"
:
{
"
expression
"
:
"
$feature.ELEVATION_Meters
"
},
"
unit
"
:
"
meters
"
},
visible
:
false
// 3D building initially hidden
});
map
.
add
(
hostedLayer
);
// usage data with 'usage' and 'count' properties
const
usageData
=
[
{
usage
:
"
educational facilities
"
,
count
:
100
},
{
usage
:
"
Commercial Facilities
"
,
count
:
200
},
];
// Additional usage
const
additionalUsageData
=
[
{
usage
:
"
apartment house
"
,
count
:
150
},
{
usage
:
"
factory
"
,
count
:
75
},
{
usage
:
"
Other
"
,
count
:
50
},
{
usage
:
"
Residential
"
,
count
:
300
},
{
usage
:
"
business facility
"
,
count
:
180
},
{
usage
:
"
Agriculture, Forestry and Fishing Facilities
"
,
count
:
90
},
{
usage
:
"
Transportation warehouse facilities
"
,
count
:
120
}
];
// Combines all usage_data
const
allUsageData
=
usageData
.
concat
(
additionalUsageData
);
// to extract labels and counts
const
labels
=
allUsageData
.
map
(
item
=>
item
.
usage
);
const
counts
=
allUsageData
.
map
(
item
=>
item
.
count
);
// colors for each category
const
colors
=
[
"
#FF6384
"
,
"
#347fb3
"
,
"
#FFCE56
"
,
"
#4BC0C0
"
,
"
#9966FF
"
,
"
#b57433
"
,
"
#3ca33c
"
,
"
#FF99CC
"
,
"
#BDBDBD
"
];
// Creating pie chart
const
pieChartCanvas
=
document
.
getElementById
(
"
usage-pie-chart
"
).
getContext
(
"
2d
"
);
const
usagePieChart
=
new
Chart
(
pieChartCanvas
,
{
type
:
"
pie
"
,
data
:
{
labels
:
labels
,
datasets
:
[{
data
:
counts
,
backgroundColor
:
colors
}]
},
options
:
{
title
:
{
display
:
true
,
text
:
"
Building Usage
"
},
legend
:
{
labels
:
{
fontColor
:
'
white
'
}
}
}
});
// To add an event listener for toggling 3D plot visibility
document
.
getElementById
(
"
toggle3DButton
"
).
addEventListener
(
"
click
"
,
function
()
{
hostedLayer
.
visible
=
!
hostedLayer
.
visible
;
});
const
graphicsLayer
=
new
GraphicsLayer
();
map
.
add
(
graphicsLayer
);
function
updatePopupContent
(
thingId
,
newData
)
{
const
graphic
=
IoTGraphics
.
find
(
item
=>
item
.
thingId
===
thingId
);
if
(
graphic
)
{
// updating the popup content
graphic
.
graphic
.
popupTemplate
.
content
=
`New Content:
${
newData
}
`
;
}
}
function
createSymbol
(
styleName
)
{
return
new
WebStyleSymbol
({
name
:
styleName
,
styleName
:
"
EsriIconsStyle
"
});
}
function
createCallout
(
point
,
symbol
,
height
,
time
)
{
const
iconGraphic
=
new
Graphic
({
geometry
:
point
,
symbol
:
symbol
,
attributes
:
{
time
:
time
},
popupTemplate
:
{
title
:
"
Time
"
,
content
:
"
{time}
"
}
});
graphicsLayer
.
add
(
iconGraphic
);
// adds graphic to layer
}
function
animateMovingFeatures
(
url
,
styleName
,
legendLabel
,
id
,
height
)
{
esriRequest
(
url
,
{
responseType
:
"
json
"
})
.
then
(
response
=>
{
const
features
=
response
.
data
.
features
;
const
coordinates
=
features
.
reduce
((
acc
,
curr
)
=>
acc
.
concat
(
curr
.
geometry
.
coordinates
),
[]);
const
datetimes
=
features
.
reduce
((
acc
,
curr
)
=>
acc
.
concat
(
curr
.
properties
.
datetimes
),
[]);
const
symbol
=
createSymbol
(
styleName
);
// Create symbol once
let
currentPoint
=
0
;
let
movingFeatureGraphic
;
// To animate moving features
const
animationInterval
=
setInterval
(()
=>
{
if
(
currentPoint
<
coordinates
.
length
)
{
const
[
longitude
,
latitude
,
featureHeight
]
=
coordinates
[
currentPoint
];
const
time
=
new
Date
(
datetimes
[
currentPoint
]).
toLocaleString
();
const
movingFeaturePoint
=
new
Point
({
x
:
longitude
,
y
:
latitude
,
z
:
0
,
spatialReference
:
{
wkid
:
4326
}
});
if
(
!
movingFeatureGraphic
)
{
// Create the moving feature graphic for the first time
movingFeatureGraphic
=
new
Graphic
({
geometry
:
movingFeaturePoint
,
symbol
:
symbol
,
attributes
:
{
time
:
time
},
popupTemplate
:
{
title
:
"
Time
"
,
content
:
"
{time}
"
}
});
graphicsLayer
.
add
(
movingFeatureGraphic
);
}
else
{
// Update the position of the existing moving feature graphic
movingFeatureGraphic
.
geometry
=
movingFeaturePoint
;
movingFeatureGraphic
.
attributes
.
time
=
time
;
}
currentPoint
++
;
}
},
1000
);
})
.
catch
(
error
=>
{
console
.
error
(
"
Error fetching data:
"
,
error
);
});
}
// Function to animate moving features for east and west bound trains
function
animateTrainRoutes
(
eastboundUrl
,
westboundUrl
,
styleName
,
legendLabel
,
id
,
height
)
{
Promise
.
all
([
esriRequest
(
eastboundUrl
,
{
responseType
:
"
json
"
}),
esriRequest
(
westboundUrl
,
{
responseType
:
"
json
"
})])
.
then
(
responses
=>
{
const
eastboundFeatures
=
responses
[
0
].
data
.
features
;
const
westboundFeatures
=
responses
[
1
].
data
.
features
;
//Combine east and westbound features into one array
const
allFeatures
=
[...
eastboundFeatures
,
...
westboundFeatures
];
const
symbol
=
createSymbol
(
styleName
);
// Create symbol once
let
currentFeatureIndex
=
0
;
let
movingFeatureGraphic
;
// animate features
const
animationInterval
=
setInterval
(()
=>
{
if
(
currentFeatureIndex
<
allFeatures
.
length
)
{
const
feature
=
allFeatures
[
currentFeatureIndex
];
const
[
longitude
,
latitude
,
featureHeight
]
=
feature
.
geometry
.
coordinates
;
const
time
=
new
Date
(
feature
.
properties
.
datetimes
[
0
]).
toLocaleString
();
const
movingFeaturePoint
=
new
Point
({
x
:
longitude
,
y
:
latitude
,
z
:
featureHeight
,
spatialReference
:
{
wkid
:
4326
}
});
if
(
!
movingFeatureGraphic
)
{
// Create the moving feature graphic for the first time
movingFeatureGraphic
=
new
Graphic
({
geometry
:
movingFeaturePoint
,
symbol
:
symbol
,
attributes
:
{
time
:
time
},
popupTemplate
:
{
title
:
"
Time
"
,
content
:
"
{time}
"
}
});
graphicsLayer
.
add
(
movingFeatureGraphic
);
}
else
{
// update the position of the existing M_feature graphic
movingFeatureGraphic
.
geometry
=
movingFeaturePoint
;
movingFeatureGraphic
.
attributes
.
time
=
time
;
}
currentFeatureIndex
++
;
}
else
{
// Reset the animation when all features have been displayed
currentFeatureIndex
=
0
;
}
},
1000
);
})
.
catch
(
error
=>
{
console
.
error
(
"
Error fetching data:
"
,
error
);
});
}
animateMovingFeatures
(
"
https://ogcapi.hft-stuttgart.de/ogc_api_moving_features/collections/bus_1/items
"
,
"
Bus
"
,
"
Bus
"
,
"
busLegend
"
,
100
);
animateMovingFeatures
(
"
https://ogcapi.hft-stuttgart.de/ogc_api_moving_features/collections/train_EastBound_WD/items
"
,
"
Train
"
,
"
Eastbound Train
"
,
"
trainEastLegend
"
,
500
);
animateMovingFeatures
(
"
https://ogcapi.hft-stuttgart.de/ogc_api_moving_features/collections/train_WestBound_WD/items
"
,
"
Train
"
,
"
Westbound Train
"
,
"
trainWestLegend
"
,
500
);
document
.
getElementById
(
"
toggleSpaceTime
"
).
addEventListener
(
"
click
"
,
function
()
{
const
spaceTimeContainer
=
document
.
getElementById
(
"
spaceTimeCubeContainer
"
);
console
.
log
(
"
Toggle button clicked
"
);
console
.
log
(
"
Current display style:
"
,
spaceTimeContainer
.
style
.
display
);
spaceTimeContainer
.
style
.
display
=
spaceTimeContainer
.
style
.
display
===
"
none
"
?
"
block
"
:
"
none
"
;
console
.
log
(
"
New display style:
"
,
spaceTimeContainer
.
style
.
display
);
// If the space-time container is set to block initialize/reload the space-time visualization
if
(
spaceTimeContainer
.
style
.
display
===
"
block
"
)
{
console
.
log
(
"
Initializing space-time visualization...
"
);
animateSpaceTimeRoutes
(
'
https://ogcapi.hft-stuttgart.de/ogc_api_moving_features/collections/bus_1/items
'
);
}
});
});
function
animateSpaceTimeRoute
(
url
,
containerId
,
trainColor
,
trainName
)
{
fetch
(
url
)
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
!
data
||
!
data
.
features
)
{
console
.
error
(
'
Invalid data format:
'
,
data
);
return
;
}
const
features
=
Array
.
isArray
(
data
.
features
)
?
data
.
features
:
[
data
.
features
];
const
frames
=
features
.
map
((
feature
,
index
)
=>
{
const
coordinates
=
feature
.
geometry
.
coordinates
;
const
timestamps
=
feature
.
properties
.
datetimes
;
if
(
!
Array
.
isArray
(
coordinates
)
||
!
Array
.
isArray
(
timestamps
))
{
console
.
error
(
'
Invalid feature format:
'
,
feature
);
return
;
}
return
{
data
:
[
{
type
:
'
scattermapbox
'
,
lat
:
coordinates
.
map
(
coord
=>
coord
[
1
]),
lon
:
coordinates
.
map
(
coord
=>
coord
[
0
]),
mode
:
'
lines+markers
'
,
marker
:
{
size
:
20
,
color
:
trainColor
},
line
:
{
color
:
trainColor
},
name
:
`Bus route`
,
},
{
type
:
'
scatter3d
'
,
x
:
coordinates
.
map
(
coord
=>
coord
[
0
]),
y
:
coordinates
.
map
(
coord
=>
coord
[
1
]),
z
:
timestamps
.
map
((
time
,
i
)
=>
new
Date
(
time
).
toISOString
()),
// convert to ISO format
mode
:
'
lines+markers
'
,
line
:
{
color
:
trainColor
},
text
:
timestamps
.
map
((
time
)
=>
new
Date
(
time
).
toLocaleTimeString
()),
// labels on the points
name
:
`Bus Space-Time -
${
index
+
1
}
`
,
},
],
name
:
`Frame
${
index
+
1
}
`
,
};
});
// Set the layout with mapbox style and access token
const
layout
=
{
mapbox
:
{
style
:
'
light
'
,
center
:
{
lon
:
features
[
0
].
geometry
.
coordinates
[
0
][
0
],
lat
:
features
[
0
].
geometry
.
coordinates
[
0
][
1
]
},
zoom
:
10
,
accesstoken
:
'
pk.eyJ1IjoicmVkaWV0OTk2MyIsImEiOiJjbHJ1cmk5aGUwaDRvMmpuYWM4Z2NqcmZuIn0.c11HYT-5ucd6g6mL03bp3Q
'
,
},
margin
:
{
t
:
0
,
b
:
0
,
l
:
0
,
r
:
0
},
};
// initialize Plotly container
Plotly
.
newPlot
(
containerId
,
frames
[
0
].
data
,
layout
);
// adding frames
Plotly
.
addFrames
(
containerId
,
frames
);
})
.
catch
(
error
=>
{
console
.
error
(
'
Error fetching data:
'
,
error
);
});
}
</script>
<script>
const
thingsLocations
=
[
{
thingId
:
1
,
locationURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(1)/Locations
"
,
datastreamsURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(1)/Datastreams
"
},
{
thingId
:
2
,
locationURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(2)/Locations
"
,
datastreamsURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(2)/Datastreams
"
},
{
thingId
:
3
,
locationURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(3)/Locations
"
,
datastreamsURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(3)/Datastreams
"
},
{
thingId
:
4
,
locationURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(4)/Locations
"
,
datastreamsURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(4)/Datastreams
"
},
{
thingId
:
5
,
locationURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(5)/Locations
"
,
datastreamsURL
:
"
https://ogcapi.hft-stuttgart.de/sta/udigit4icity/v1.1/Things(5)/Datastreams
"
},
];
</script>
</body>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a 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