Slide 1

Slide 1 text

Stefano Bovio 24/06/2025 Exploring 3D CesiumJS tools in MapStore Open Source WebGIS

Slide 2

Slide 2 text

● Our Services GeoSolutions ● Offices in Italy & US, Global Clients/Team ● 40+ collaborators ● Our products GeoServer MapStore GeoNode GeoNetwork Enterprise Support Subscription Professional Training Custom Solutions

Slide 3

Slide 3 text

GeoSolutions We design, develop and deploy end-to-end scalable, interoperable, secure geospatial solutions

Slide 4

Slide 4 text

Introduction

Slide 5

Slide 5 text

What’s MapStore? MapStore is an highly modular Open Source WebGIS framework to create, manage and share various types of contents, like maps, dashboards and geostories.

Slide 6

Slide 6 text

What’s MapStore? MapStore is written in ReactJS MapStore is map library agnostic and ensures the greatest flexibility: its abstraction tier allows to work with different web mapping libraries The mapping engines currently supported are OpenLayers, LeafletJS and CesiumJS 1. Municipality of Turin - MapStore using CesiumJS to render 3D Tiles of the urban area 2. MapStorle using CesiumJS with the full globe view

Slide 7

Slide 7 text

MapStore and CesiumJS integration ● CesiumJS was included in MapStore on Jan 26, 2016 for the first time ● Now is integrated inside a ReactJS component with the support of multiple layer types: ○ 3D Tiles ○ Terrain (Cesium Quantized Mesh) ○ Vector (GeoJSON, Shapefile) ○ Web Map Service (WMS) ○ Web Map Tile Service (WMTS) ○ Web Feature Service (WFS) ○ Tiles Map Service (TMS) ○ ArcGIS Map and Image services ○ IFC Model ○ … class CesiumMap extends React.Component { static propTypes = { id: PropTypes.string, // ... } // ... componentDidMount() { // ... this.map = new Cesium.Viewer(this.props.id, /* */); // ... } // ... render() { // ... return (
{children} {/* ... */}
); } https://github.com/geosolutions-it/MapStore2/blob/2025.01.xx/web/client/components/map/cesium/Map.jsx

Slide 8

Slide 8 text

MapStore and CesiumJS integration Relevant 3D tools implemented on top of CesiumJS are the result of many contributions coming from Italian Public Administrations and others organizations worldwide such as: ● Municipality of Genoa ● Municipality of Florence ● Austrocontrol Austrian Aerospace ● Total Energies ● Vlaanderen ● …

Slide 9

Slide 9 text

MapStore and CesiumJS integration Multiple implementations has been contributed to MapStore to enhance the user experience and user interface inside the 3D map viewer, next a selection of few of them: ● Map views and clipping 3D Tiles at runtime ● 3D drawing system, annotations and measurement ● Style editor and interactive legend ● Special 3D layers

Slide 10

Slide 10 text

3D Tools

Slide 11

Slide 11 text

Map views and clipping 3D Tiles at runtime Represent the same map as a sequence of multiple differently configured views: ● add description for view ● store camera position ● allow clipping 3D Tiles or terrain ● allow changing layer visibility and opacity ● allow changing globe opacity 1. Municipality of Genoa - A map view where the underground building structure are displayed using 3D Tiles 2. Municipality of Genoa - A map view where the master plan area of a new project is displayed using clipping functionality

Slide 12

Slide 12 text

Map views and clipping 3D Tiles at runtime 3D Clipping using Cesium.ClippingPlane const outerRingCoordinates = uniqBy(union ? coordinates : coordinates.reverse(), (coords) => `${coords[0]}${coords[1]}`); const points = outerRingCoordinates.map(([lng, lat, height = 0]) => { const point = Cesium.Cartesian3.fromDegrees(lng, lat, height); return point; }); const pointsLength = points.length; // Create center points for each clipping plane const clippingPlanes = []; for (let i = 0; i < pointsLength; ++i) { const nextIndex = (i + 1) % pointsLength; let midpoint = Cesium.Cartesian3.add(points[i], points[nextIndex], new Cesium.Cartesian3()); midpoint = Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint); const up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3()); let right = Cesium.Cartesian3.subtract(points[nextIndex], midpoint, new Cesium.Cartesian3()); right = Cesium.Cartesian3.normalize(right, right); let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3()); normal = Cesium.Cartesian3.normalize(normal, normal); // Compute distance by pretending the plane is at the origin const originCenteredPlane = new Cesium.Plane(normal, 0.0); const distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint); clippingPlanes.push(new Cesium.ClippingPlane(normal, distance)); } 1. Municipality of Genoa - Mapstore showing a clipped 3D Tiles to highlight a subway station inside the CesiumJS viewer 2. https://github.com/geosolutions-it/MapStore2/blob/2025.01.xx/web/client/utils/cesium/PrimitivesUtils.js

Slide 13

Slide 13 text

Map views and clipping 3D Tiles at runtime Current clipping plane approach limitations: ● One clipping area allowed per 3D Tiles ● Only convex shape supported Possible improvement: ● ClippingPolygons instead of ClippingPlanes const clippingPolygons = new ClippingPolygonCollection({ polygons: geoJSONPolygonFeatures.map(feature => { return new ClippingPolygon({ positions: Cesium.Cartesian3.fromRadiansArray( feature.geometry.coordinates[0] ) }); }), inverse: false }); tileset.clippingPolygons = clippingPolygons;

Slide 14

Slide 14 text

Map views and clipping 3D Tiles at runtime Historical data reconstruction to highlight evolution of the urban environment 1. Municipality of Genoa - 3D Tiles of the historical coastline (white) showing in red the lighthouse location 2. Municipality of Genoa - Clipped 3D Tiles showing now and then combined together

Slide 15

Slide 15 text

Map views and clipping 3D Tiles at runtime Municipality of Genoa - Usage of addiational tools such as the measurement tool inside a map view

Slide 16

Slide 16 text

3D drawing system, annotations and measurement Drawing system implementation focused on: ● Support of different types of geometry: Point, LineString, Polygon and Circle ● React component re-usable and extendable for new plugins 1. Municipality of Genoa - MapStore showing the measurement tool 2. Municipality of Florence - MapStore showing the annotations tool

Slide 17

Slide 17 text

3D drawing system, annotations and measurement Management of drawing interaction: ● Usage of ScreenSpaceEventHandler for mouse inputs ● Usage of Cesium.PrimitiveCollection and Cesium.BillboardCollection class CesiumDrawGeometryInteraction { constructor(options) { // ... this._handler = new Cesium.ScreenSpaceEventHandler( this._map.canvas ); this._handler.setInputAction((movement) => { this._handleDrawEnd(movement, true); }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); // ... this._dynamicPrimitivesCollection = new Cesium.PrimitiveCollection({ destroyPrimitives: true }); // ... this._dynamicBillboardCollection = new Cesium.BillboardCollection({ scene: this._map.scene }); // ... } https://github.com/geosolutions-it/MapStore2/blob/2025.01.xx/web/client/utils/cesium/DrawGeometryInteraction.js

Slide 18

Slide 18 text

3D drawing system, annotations and measurement Compute position information priorities: 1. scene.pickPosition 2. scene.drillPickFromRay 3. globe.pick export function computePositionInfo(map, movement, { pickObjectsLimit = Number.MAX_VALUE } = {}) { const scene = map.scene; const camera = map.camera; const position = movement.position || movement.endPosition; const feature = scene.pick(position); const depthCartesian = scene.pickPosition(position); if (!(feature?.primitive instanceof Cesium.GroundPrimitive) && !(feature?.primitive instanceof Cesium.GroundPolylinePrimitive) && !!(feature && depthCartesian)) { return {/* ... cartesian, cartographic, feature */}; } const ray = camera.getPickRay(position); const drillPickFeature = scene.drillPickFromRay(ray, pickObjectsLimit) .find(({ exclude, object, position: rayIntersectionPosition }) => !exclude && rayIntersectionPosition && object) || null; if (drillPickFeature) { return {/* ... cartesian, cartographic, feature */}; } const globeCartesian = scene.globe.pick(ray, scene); if (globeCartesian) { const cartographic = Cesium.Cartographic.fromCartesian(globeCartesian); return {/* ... cartesian, cartographic, feature */}; } return {}; } https://github.com/geosolutions-it/MapStore2/blob/2025.01.xx/web/client/utils/cesium/ClickUtils.js

Slide 19

Slide 19 text

3D drawing system, annotations and measurement The React component exposes props interface to control the activation, geometry type and all the drawing phases function MyDrawingSupport({ cesiumViewer, active, mapType, feature, geometryType = 'Point', geodesic = false, onChange = () => {} }) { return ( []} onDrawEnd={({ feature: newFeature }) => onChange(newFeature) } /> ); } - https://github.com/geosolutions- it/MapStore2/blob/2025.01.xx/web/client/plugins/Annotations/containers/AnnotationsMapInteractionsSuppo rt.jsx - https://github.com/geosolutions- it/MapStore2/blob/2025.01.xx/web/client/components/map/cesium/MeasurementSupport.jsx

Slide 20

Slide 20 text

3D drawing system, annotations and measurement Municipality of Florence - MapStore showing the measurement tool in action drawing distance polylines inside the CesiumJS viewer

Slide 21

Slide 21 text

3D drawing system, annotations and measurement Municipality of Genoa - MapStore showing annotation tool with the coordinates editor and in the CesiumJS viewer rendered annotation features using extrusions, labels and GLTF 3D models

Slide 22

Slide 22 text

Style editor and interactive legend Style editor implementation focused on: ● Interoperability with internal json style structure ● Style support for different layer types: 3D Tiles, GeoJSON and WFS 1. Municipality of Tertenia - MapStore in GeoNode showing 3D Tiles of the urban area 2. Municipality of Florence - MapStore showing 3D Tiles buildings with classified point cloud filtered and styled for rooftop and vegetation rendered inside CesiumJS viewer

Slide 23

Slide 23 text

Style editor and interactive legend Definition of style as a JSON list of rules and symbolizers with possibility of editing with User Interface { "rules": [ { "name": ">= 159.055 and < 77987.823", "filter": ["&&", [ ">=", "LAND_KM", 159.055 ], [ "<", "LAND_KM", 77987.823 ]], "symbolizers": [ { "kind": "Fill", "color": "#ffffcc", "fillOpacity": 1, "outlineColor": "#777777", "outlineWidth": 0, "msClassificationType": "both", "msClampToGround": false, "msExtrudedHeight": { "name": "property", "args": [ "LAND_KM" ] } } ] }, // ... ] } 1. MapStore showing classified style using property value to define the CesiumJS polygon entity extrusion 2. https://docs.mapstore.geosolutionsgroup.com/en/latest/developer-guide/vector-style/

Slide 24

Slide 24 text

Style editor and interactive legend A custom class GeoJSONStyledFeatures manages entities and primitives updates with filtering support class GeoJSONStyledFeatures { constructor(options = {}) { this._msId = options.id; this._dataSource = new Cesium.CustomDataSource( options.id); this._primitives = new Cesium.PrimitiveCollection({ destroyPrimitives: true }); // ... this.setFeatures(options.features); } // ... setStyleFunction(styleFunction) { this._styleFunction = styleFunction; this._update(); } 1. MapStore viewer showing interactivity between legend and Cesium viewer by filtering selected style rules 2. https://github.com/geosolutions- it/MapStore2/blob/2025.01.xx/web/client/utils/cesium/GeoJSONStyledFeatures.js

Slide 25

Slide 25 text

Style editor and interactive legend Municipality of Florence - Stylized WFS layer combined with 3D Tiles Buildings inside MapStore using CesiumJS to highlight degradation of building facades

Slide 26

Slide 26 text

Style editor and interactive legend Abitat Treviso - Rendering of multiple 3D Tiles layers with style applied via style editor inside MapStore using CesiumJS. Building have been stylized using filter rules based on building height property

Slide 27

Slide 27 text

Special 3D layers Additional layers supported by the 3D viewer: ● IFC 3D model layer ● Terrain layer 1. Austrocontrol Austrian Aerospace - terrain rendered with GeoServer WMS layer 2. Municipality of Genoa - IFC model showing underground portion of a building rendered inside MapStore with CesiumJS engine

Slide 28

Slide 28 text

IFC 3D model layer ● Implemented using web-ifc parser ● Usage of 2 Cesium.Primitive for opaque and translucent geometries ● Usage of Cesium.ColorGeometryInstan ceAttribute to assign different color to all geometries let primitives = new Cesium.PrimitiveCollection({ destroyPrimitives: true }); getIFCModel(options.url) .then(({ifcModule, data}) => { const { meshes } = ifcDataToJSON({ ifcModule, data }); const translucent = createPrimitiveFromMeshes( meshes, options, 'translucentPrimitive' ); const opaque = createPrimitiveFromMeshes( meshes, options, 'opaquePrimitive' ); primitives.add(translucent); primitives.add(opaque); updatePrimitivesMatrix(primitives, options?.features?.[0]); }); map.scene.primitives.add(primitives); https://github.com/geosolutions- it/MapStore2/blob/2025.01.xx/web/client/components/map/cesium/plugins/ModelLayer.js

Slide 29

Slide 29 text

IFC 3D model layer Municipality of Florence - Rendering of an IFC model inside MapStore using the CesiumJS engine

Slide 30

Slide 30 text

Terrain layers ● Terrain layer with support of multiple providers: cesium, cesium-ion and wms ● List component to manage terrain layer from map viewer let terrainProvider; switch (config.provider) { case 'wms': { terrainProvider = new GeoServerBILTerrainProvider( WMSUtils.wmsToCesiumOptionsBIL(config)); break; } case 'cesium': { terrainProvider = new Cesium.CesiumTerrainProvider( cesiumOptionsMapping(config)); break; } case 'ellipsoid': { terrainProvider = new Cesium.EllipsoidTerrainProvider(); break; } case 'cesium-ion': { terrainProvider = new Cesium.CesiumTerrainProvider( cesiumIonOptionsMapping(config)); break; } default: terrainProvider = new Cesium.EllipsoidTerrainProvider(); break; } map.terrainProvider = terrainProvider; https://github.com/geosolutions- it/MapStore2/blob/2025.01.xx/web/client/components/map/cesium/plugins/TerrainLayer.js

Slide 31

Slide 31 text

Terrain layers Municipality of Genoa - Cesium Quantized Mesh terrain

Slide 32

Slide 32 text

How to try MapStore

Slide 33

Slide 33 text

How to try MapStore ● MapStore documentation: https://docs.mapstore.geosolutionsgroup.com/en/latest/ ● MapStore repository: https://github.com/geosolutions-it/MapStore2 ● MapStore latest release 2025.01.00: https://github.com/geosolutions-it/MapStore2/releases/tag/v2025.01.00 ● MapStore demo: https://mapstore.geosolutionsgroup.com/mapstore/#/

Slide 34

Slide 34 text