Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
iCityDigitalTwinFramework
Air Pollutant Viewer
Commits
a5cb328a
Commit
a5cb328a
authored
Jun 12, 2025
by
Rushikesh Padsala
Browse files
Upload New File
parent
67793eb8
Changes
1
Show whitespace changes
Inline
Side-by-side
public/App.js
0 → 100644
View file @
a5cb328a
Cesium
.
Ion
.
defaultAccessToken
=
'
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxYjBlYmYyNC1kNWRhLTRiNTUtOGNlYi02NGY1YWVhNjI2MjIiLCJpZCI6MTEwNzEsImlhdCI6MTYxOTcwNjI0M30.tlpaagcH93SjaHIn7eEVpanGSiH2yDylbGJZr2gsXnY
'
;
//////////////////////////////////////////////////////////////////////////
// Loading Custom Terrain
//////////////////////////////////////////////////////////////////////////
var
customterrain
=
new
Cesium
.
CesiumTerrainProvider
({
url
:
'
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/QuantizedMesh/UDigiT4iCity/Stuttgart/Schlossstrasse/
'
,
});
var
terrainProviderViewModels
=
[];
terrainProviderViewModels
.
push
(
new
Cesium
.
ProviderViewModel
({
name
:
'
Custom Terrain
'
,
iconUrl
:
Cesium
.
buildModuleUrl
(
'
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/Images/TerrainProviders/MontrealTerrain.png
'
),
tooltip
:
'
Custom Terrain
'
,
creationFunction
:
function
()
{
return
customterrain
;
}
}));
//////////////////////////////////////////////////////////////////////////
// Creating the Viewer
//////////////////////////////////////////////////////////////////////////
var
viewer
=
new
Cesium
.
Viewer
(
'
cesiumContainer
'
,
{
scene3DOnly
:
true
,
selectionIndicator
:
false
,
timeline
:
false
,
animation
:
false
,
shadow
:
false
,
imageryProvider
:
new
Cesium
.
WebMapServiceImageryProvider
({
url
:
'
https://sgx.geodatenzentrum.de/wms_basemapde?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities
'
,
layers
:
"
de_basemapde_web_raster_farbe
"
,
enablePickFeatures
:
false
,
parameters
:
{
transparent
:
true
,
format
:
"
image/png
"
,
}
}),
baseLayerPicker
:
true
,
terrainProviderViewModels
:
terrainProviderViewModels
});
viewer
.
clock
.
shouldAnimate
=
true
;
// Start clock on load
viewer
.
clock
.
multiplier
=
4
;
// Real-time speed (adjust as needed)
viewer
.
clockViewModel
.
multiplier
=
4
;
// Sync with UI (if enabled later)
viewer
.
clockViewModel
.
shouldAnimate
=
true
;
// Enable Cesium Sun Light (Makes Scene Brighter)
viewer
.
scene
.
globe
.
enableLighting
=
true
;
// Add Directional Light to Improve Brightness
viewer
.
scene
.
light
=
new
Cesium
.
DirectionalLight
({
direction
:
new
Cesium
.
Cartesian3
(
0.5
,
-
0.5
,
-
1.0
),
// Adjusted light direction
intensity
:
10.0
// Increased brightness intensity
});
// Adjust Sky Atmosphere for Better Daylight Effects
viewer
.
scene
.
skyAtmosphere
.
brightnessShift
=
1.0
;
// Adjust Scene Environment to Reduce Overall Darkness
viewer
.
scene
.
globe
.
ambientOcclusion
=
false
;
viewer
.
scene
.
globe
.
showGroundAtmosphere
=
true
;
viewer
.
scene
.
globe
.
nightFadeInDistance
=
500000
;
//viewer.scene.postProcessStages.add(Cesium.PostProcessStageLibrary.createEdgeDetectionStage());
//viewer.scene.postProcessStages.add(Cesium.PostProcessStageLibrary.createEyeDomeLightingStage());
viewer
.
scene
.
screenSpaceCameraController
.
minimumZoomDistance
=
100
;
// meters
viewer
.
scene
.
screenSpaceCameraController
.
maximumZoomDistance
=
6000
;
// meters
//////////////////////////////////////////////////////////////////////////
// Set the camera to Schloßstraße, Stuttgart
//////////////////////////////////////////////////////////////////////////
var
SchloßstraßePosition
=
{
destination
:
Cesium
.
Cartesian3
.
fromDegrees
(
9.177352
,
48.780051
,
506.27
),
orientation
:
{
heading
:
Cesium
.
Math
.
toRadians
(
254.00
),
pitch
:
Cesium
.
Math
.
toRadians
(
-
30.38
),
roll
:
Cesium
.
Math
.
toRadians
(
0.00
)
}
};
viewer
.
scene
.
camera
.
setView
(
SchloßstraßePosition
);
viewer
.
homeButton
.
viewModel
.
command
.
beforeExecute
.
addEventListener
(
function
(
e
)
{
e
.
cancel
=
true
;
viewer
.
scene
.
camera
.
flyTo
(
SchloßstraßePosition
);
//////////////////////////////////////////////////////////////////////////
// Dropbox and Checkbox Behavior
//////////////////////////////////////////////////////////////////////////
// Reset dropdown to "--Select--"
document
.
getElementById
(
"
selectairpollutant
"
).
value
=
""
;
// Uncheck the Air Pressure checkbox
document
.
getElementById
(
"
toggleAirPressure
"
).
checked
=
false
;
// Uncheck the Air Velocity checkbox
document
.
getElementById
(
"
toggleAirVelocity
"
).
checked
=
false
;
// Hide legend
hideLegend
();
// Remove any active pollutant, air pressure, or air velocity 3D Tiles
if
(
currentPollutantTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
currentPollutantTileset
);
currentPollutantTileset
=
null
;
}
if
(
airPressureTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
airPressureTileset
);
airPressureTileset
=
null
;
}
if
(
airVelocityTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
airVelocityTileset
);
airVelocityTileset
=
null
;
}
viewer
.
entities
.
removeAll
();
// Reset building transparency
resetBuildingTransparency
();
// Hide the Cesium info box
viewer
.
selectedEntity
=
undefined
;
});
//////////////////////////////////////////////////////////////////////////
// Load Building 3D Tiles
//////////////////////////////////////////////////////////////////////////
var
bldgtileset
=
new
Cesium
.
Cesium3DTileset
({
url
:
'
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/3DTiles/Buildings/BuildingSolid/UDigiT4iCity/Stuttgart/Schlossstrasse/tileset.json
'
,
});
bldgtileset
.
readyPromise
.
then
(
function
(
tileset
)
{
var
loadingScreen
=
document
.
getElementById
(
"
loadingScreen
"
);
loadingScreen
.
style
.
display
=
"
none
"
;
});
bldgtileset
.
style
=
new
Cesium
.
Cesium3DTileStyle
({
color
:
"
color('white')
"
});
viewer
.
scene
.
primitives
.
add
(
bldgtileset
);
//////////////////////////////////////////////////////////////////////////
// Air Pollutant Selection & Visualization (Excluding Air Pressure and Velocity)
//////////////////////////////////////////////////////////////////////////
const
pollutantTiles
=
{
"
CO
"
:
"
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/3DTiles/CFD/UDigiT4iCity/Stuttgart/Schlossstrasse/AirPollutants/CO/tileset.json
"
,
"
NOx
"
:
"
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/3DTiles/CFD/UDigiT4iCity/Stuttgart/Schlossstrasse/AirPollutants/NOx/tileset.json
"
,
"
SO2
"
:
"
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/3DTiles/CFD/UDigiT4iCity/Stuttgart/Schlossstrasse/AirPollutants/SO2/tileset.json
"
,
"
HC
"
:
"
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/3DTiles/CFD/UDigiT4iCity/Stuttgart/Schlossstrasse/AirPollutants/HC/tileset.json
"
,
"
BAP
"
:
"
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/3DTiles/CFD/UDigiT4iCity/Stuttgart/Schlossstrasse/AirPollutants/BAP/tileset.json
"
};
let
currentPollutantTileset
=
null
;
document
.
getElementById
(
"
selectairpollutant
"
).
addEventListener
(
"
change
"
,
function
()
{
const
selectedPollutant
=
this
.
value
;
// Uncheck the checkbox if a pollutant is selected
if
(
selectedPollutant
)
{
document
.
getElementById
(
"
toggleAirPressure
"
).
checked
=
false
;
// Remove Air Pressure 3D Tiles if they are active
if
(
airPressureTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
airPressureTileset
);
airPressureTileset
=
null
;
}
}
if
(
currentPollutantTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
currentPollutantTileset
);
currentPollutantTileset
=
null
;
}
// Hide legend and reset transparency if "--Select--" is chosen
if
(
!
selectedPollutant
)
{
resetBuildingTransparency
();
hideLegend
();
return
;
}
const
pollutantTilesetUrl
=
pollutantTiles
[
selectedPollutant
];
if
(
pollutantTilesetUrl
)
{
currentPollutantTileset
=
new
Cesium
.
Cesium3DTileset
({
url
:
pollutantTilesetUrl
});
// Set point size for better visibility
currentPollutantTileset
.
style
=
new
Cesium
.
Cesium3DTileStyle
({
pointSize
:
6
});
viewer
.
scene
.
primitives
.
add
(
currentPollutantTileset
);
applyBuildingTransparency
();
updateLegend
(
selectedPollutant
);
}
});
//////////////////////////////////////////////////////////////////////////
// Air Pressure Checkbox Logic
//////////////////////////////////////////////////////////////////////////
let
airPressureTileset
=
null
;
document
.
getElementById
(
"
toggleAirPressure
"
).
addEventListener
(
"
change
"
,
function
()
{
if
(
this
.
checked
)
{
// Reset air pollutant dropdown to "--Select--"
document
.
getElementById
(
"
selectairpollutant
"
).
value
=
""
;
// Remove any existing pollutant 3D Tiles
if
(
currentPollutantTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
currentPollutantTileset
);
currentPollutantTileset
=
null
;
}
// Load Air Pressure 3D Tiles
airPressureTileset
=
new
Cesium
.
Cesium3DTileset
({
url
:
"
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/3DTiles/CFD/UDigiT4iCity/Stuttgart/Schlossstrasse/AirPollutants/AP/tileset.json
"
});
airPressureTileset
.
style
=
new
Cesium
.
Cesium3DTileStyle
({
pointSize
:
5
});
viewer
.
scene
.
primitives
.
add
(
airPressureTileset
);
applyBuildingTransparency
();
updateLegend
(
"
Air Pressure
"
);
}
else
{
// Remove Air Pressure 3D Tiles
if
(
airPressureTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
airPressureTileset
);
airPressureTileset
=
null
;
}
resetBuildingTransparency
();
hideLegend
();
}
});
//////////////////////////////////////////////////////////////////////////
// Air Velocity Checkbox Logic
//////////////////////////////////////////////////////////////////////////
let
airVelocityTileset
=
null
;
document
.
getElementById
(
"
toggleAirVelocity
"
).
addEventListener
(
"
change
"
,
function
()
{
if
(
this
.
checked
)
{
// Reset air pollutant dropdown and uncheck air pressure
document
.
getElementById
(
"
selectairpollutant
"
).
value
=
""
;
document
.
getElementById
(
"
toggleAirPressure
"
).
checked
=
false
;
// Remove pollutant and air pressure tilesets
if
(
currentPollutantTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
currentPollutantTileset
);
currentPollutantTileset
=
null
;
}
if
(
airPressureTileset
)
{
viewer
.
scene
.
primitives
.
remove
(
airPressureTileset
);
airPressureTileset
=
null
;
}
// Show loading screen
document
.
getElementById
(
"
loadingScreen
"
).
style
.
display
=
"
flex
"
;
// Load MF-JSON data and visualize
fetch
(
'
https://w2.iaf-ex.hft-stuttgart.de/CesiumData/MF-JSON/Stuttgart/Schlossstrasse/AirVelocity.json
'
)
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
visualizeWindVelocity
(
data
);
// Hide loading screen after visualization is complete
document
.
getElementById
(
"
loadingScreen
"
).
style
.
display
=
"
none
"
;
updateLegend
(
"
Air Velocity
"
);
// Show legend only after visualization
});
}
else
{
// Clear entities and hide legend
viewer
.
entities
.
removeAll
();
hideLegend
();
}
});
//////////////////////////////////////////////////////////////////////////
// Helper Functions
//////////////////////////////////////////////////////////////////////////
function
applyBuildingTransparency
()
{
bldgtileset
.
style
=
new
Cesium
.
Cesium3DTileStyle
({
defines
:
{
targetBuilding
:
"
${gml_id} === 'GML_668f7802-54ef-4f41-9e30-0018df4bf440'
"
},
color
:
{
conditions
:
[
[
"
${targetBuilding}
"
,
"
color('white', 0.6)
"
],
// 60% transparency
[
"
true
"
,
"
color('white')
"
]
// Other buildings remain fully visible
]
}
});
}
function
resetBuildingTransparency
()
{
bldgtileset
.
style
=
new
Cesium
.
Cesium3DTileStyle
({
color
:
"
color('white')
"
});
}
function
hideLegend
()
{
const
legendContainer
=
document
.
querySelector
(
"
.legend-container
"
);
if
(
legendContainer
)
{
legendContainer
.
style
.
display
=
"
none
"
;
}
}
// Ensure legend is hidden on application load
hideLegend
();
function
updateLegend
(
layerName
)
{
const
legendContainer
=
document
.
querySelector
(
"
.legend-container
"
);
const
legendTitle
=
document
.
querySelector
(
"
.legend-title
"
);
if
(
layerName
)
{
// Set custom title for Air Velocity, otherwise use "Concentration - X"
if
(
layerName
===
"
Air Velocity
"
)
{
legendTitle
.
textContent
=
"
Air Velocity
"
;
}
else
{
legendTitle
.
textContent
=
`Concentration -
${
layerName
}
`
;
}
legendContainer
.
style
.
display
=
"
block
"
;
}
else
{
hideLegend
();
}
}
//////////////////////////////////////////////////////////////////////////
// Wind Velocity
//////////////////////////////////////////////////////////////////////////
let
windEntities
=
[];
let
animationStartTime
=
Cesium
.
JulianDate
.
now
();
function
visualizeWindVelocity
(
data
)
{
viewer
.
entities
.
removeAll
();
windEntities
=
[];
animationStartTime
=
Cesium
.
JulianDate
.
now
();
const
loopTime
=
20.0
;
// seconds for one vector cycle
const
densityStep
=
2
;
// lower = denser (1 = full, 3 = every 3rd point)
for
(
let
i
=
0
;
i
<
data
.
features
.
length
;
i
+=
densityStep
)
{
const
feature
=
data
.
features
[
i
];
const
coords
=
feature
.
geometry
.
coordinates
;
const
velocity
=
feature
.
properties
.
velocity
;
const
classification
=
feature
.
properties
.
classification
;
const
start
=
Cesium
.
Cartesian3
.
fromDegrees
(
coords
[
0
],
coords
[
1
],
coords
[
2
]);
const
vel
=
new
Cesium
.
Cartesian3
(
velocity
.
x
,
velocity
.
y
,
velocity
.
z
);
const
magnitude
=
Cesium
.
Cartesian3
.
magnitude
(
vel
);
let
direction
;
if
(
magnitude
===
0
||
isNaN
(
magnitude
))
{
direction
=
new
Cesium
.
Cartesian3
(
0.0
,
0.0
,
10.0
);
}
else
{
const
scaledLength
=
Math
.
min
(
Math
.
max
(
magnitude
*
5
,
10
),
40
);
direction
=
Cesium
.
Cartesian3
.
normalize
(
vel
,
new
Cesium
.
Cartesian3
());
direction
=
Cesium
.
Cartesian3
.
multiplyByScalar
(
direction
,
scaledLength
,
direction
);
}
let
color
=
Cesium
.
Color
.
GRAY
;
if
(
classification
===
"
Calm
"
)
color
=
Cesium
.
Color
.
BLUE
;
else
if
(
classification
===
"
Moderate
"
)
color
=
Cesium
.
Color
.
GREEN
;
else
if
(
classification
===
"
Strong
"
)
color
=
Cesium
.
Color
.
RED
;
const
polylinePositions
=
new
Cesium
.
CallbackProperty
(
function
(
time
,
result
)
{
const
seconds
=
Cesium
.
JulianDate
.
secondsDifference
(
time
,
animationStartTime
);
const
t
=
(
seconds
%
loopTime
)
/
loopTime
;
const
displacement
=
Cesium
.
Cartesian3
.
multiplyByScalar
(
direction
,
t
,
new
Cesium
.
Cartesian3
());
const
end
=
Cesium
.
Cartesian3
.
add
(
start
,
displacement
,
new
Cesium
.
Cartesian3
());
return
[
start
,
end
];
},
false
);
viewer
.
entities
.
add
({
polyline
:
{
positions
:
polylinePositions
,
width
:
2.0
,
material
:
new
Cesium
.
PolylineArrowMaterialProperty
(
color
)
}
// No label — we remove it for performance
});
}
// Do NOT zoom to entities — keeps camera unchanged
}
//////////////////////////////////////////////////////////////////////////
// Selecting geometries in 3D Tileset
//////////////////////////////////////////////////////////////////////////
var
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
.
fontFamily
=
'
Fira Sans, sans-serif
'
;
//Selecting a Building
var
Pickers_3DTile_Activated
=
true
;
// Information about the currently highlighted feature
function
active3DTilePicker
()
{
var
highlighted
=
{
feature
:
undefined
,
originalColor
:
new
Cesium
.
Color
()
};
// Information about the currently selected feature
var
selected
=
{
feature
:
undefined
,
originalColor
:
new
Cesium
.
Color
()
};
// An entity object which will hold info about the currently selected feature for infobox display
var
selectedEntity
=
new
Cesium
.
Entity
();
// Get default left click handler for when a feature is not picked on left click
var
clickHandler
=
viewer
.
screenSpaceEventHandler
.
getInputAction
(
Cesium
.
ScreenSpaceEventType
.
LEFT_CLICK
);
// Color a feature yellow on hover.
viewer
.
screenSpaceEventHandler
.
setInputAction
(
function
onMouseMove
(
movement
)
{
if
(
Pickers_3DTile_Activated
)
{
// 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
var
picked3DtileFeature
=
viewer
.
scene
.
pick
(
movement
.
endPosition
);
if
(
!
Cesium
.
defined
(
picked3DtileFeature
))
{
nameOverlay
.
style
.
display
=
'
none
'
;
return
;
}
// A feature was picked, so show it's overlay content
// Highlight the feature if it's not already selected.
if
(
picked3DtileFeature
!==
selected
.
feature
)
{
highlighted
.
feature
=
picked3DtileFeature
;
Cesium
.
Color
.
clone
(
picked3DtileFeature
.
color
,
highlighted
.
originalColor
);
picked3DtileFeature
.
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
(
Pickers_3DTile_Activated
)
{
// If a feature was previously selected, undo the highlight
if
(
Cesium
.
defined
(
selected
.
feature
))
{
selected
.
feature
.
color
=
selected
.
originalColor
;
selected
.
feature
=
undefined
;
var
options
=
null
;
}
// Pick a new feature
var
picked3DtileFeature
=
viewer
.
scene
.
pick
(
movement
.
position
);
if
(
!
Cesium
.
defined
(
picked3DtileFeature
))
{
clickHandler
(
movement
);
return
;
}
// Select the feature if it's not already selected
if
(
selected
.
feature
===
picked3DtileFeature
)
{
return
;
}
selected
.
feature
=
picked3DtileFeature
;
// Save the selected feature's original color
if
(
picked3DtileFeature
===
highlighted
.
feature
)
{
Cesium
.
Color
.
clone
(
highlighted
.
originalColor
,
selected
.
originalColor
);
highlighted
.
feature
=
undefined
;
}
else
{
Cesium
.
Color
.
clone
(
picked3DtileFeature
.
color
,
selected
.
originalColor
);
}
// Highlight newly selected feature
picked3DtileFeature
.
color
=
Cesium
.
Color
.
AQUA
;
// Set feature infobox description
var
featureName
=
"
3D Tiles Viewer
"
;
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>gml ID</th><td>
'
+
picked3DtileFeature
.
getProperty
(
'
gml_id
'
)
+
'
</td></tr>
'
+
'
</tbody></table>
'
;
}
},
Cesium
.
ScreenSpaceEventType
.
LEFT_CLICK
);
}
active3DTilePicker
();
\ No newline at end of file
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