Commit a5a49889 authored by JOE XMG's avatar JOE XMG
Browse files

update

parent c6bc81bb
<!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>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment