Skip to content

Commit

Permalink
geosolutions-it#9830: Support for IFC as a further 3D model
Browse files Browse the repository at this point in the history
Description:
- Creating ModelTransformation component that handle moving the model center via display TOC settings for 'model' layers
- Add translations
- Handle show/hide the model layers via TOC
- Handle logic on show/hide 'model' layers based on  max/min scale in display TOC settings
  • Loading branch information
mahmoudadel54 committed Jan 23, 2024
1 parent 67e3f34 commit 7adaf68
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 80 deletions.
54 changes: 21 additions & 33 deletions web/client/api/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
import * as Cesium from 'cesium';

// extract the tile format from the uri
function getFormat(uri) {
const parts = uri.split(/\./g);
Expand All @@ -27,18 +25,11 @@ function extractCapabilities(ifcApi, modelID, url) {
};
}

const applyMatrix = (matrix, coords) => {
const result = Cesium.Matrix4.multiplyByPoint(
Cesium.Matrix4.fromArray(matrix),
new Cesium.Cartesian3(...coords),
new Cesium.Cartesian3()
);

return [result.x, result.y, result.z];
};

export const ifcDataToJSON = ({ data, ifcApi }) => {
const settings = {};
const settings = {
COORDINATE_TO_ORIGIN: true,
USE_FAST_BOOLS: true
};
let rawFileData = new Uint8Array(data);
const modelID = ifcApi.OpenModel(rawFileData, settings); // eslint-disable-line
ifcApi.LoadAllGeometry(modelID); // eslint-disable-line
Expand All @@ -61,14 +52,9 @@ export const ifcDataToJSON = ({ data, ifcApi }) => {
const positions = new Float64Array(ifcVertices.length / 2);
const normals = new Float32Array(ifcVertices.length / 2);
for (let j = 0; j < ifcVertices.length; j += 6) {
const [x, y, z] = applyMatrix(
coordinationMatrix,
applyMatrix(placedGeometry.flatTransformation, [
ifcVertices[j],
ifcVertices[j + 1],
ifcVertices[j + 2]
], Cesium), Cesium
);
const x = ifcVertices[j]; // index = 0
const y = ifcVertices[j + 1]; // index = 1
const z = ifcVertices[j + 2]; // index = 2
if (x < minx) { minx = x; }
if (y < miny) { miny = y; }
if (z < minz) { minz = z; }
Expand All @@ -78,15 +64,16 @@ export const ifcDataToJSON = ({ data, ifcApi }) => {
positions[j / 2] = x;
positions[j / 2 + 1] = y;
positions[j / 2 + 2] = z;
normals[j / 2] = ifcVertices[j + 3];
normals[j / 2 + 1] = ifcVertices[j + 4];
normals[j / 2 + 2] = ifcVertices[j + 5];
normals[j / 2] = ifcVertices[j + 3]; // index = 3
normals[j / 2 + 1] = ifcVertices[j + 4]; // index = 4
normals[j / 2 + 2] = ifcVertices[j + 5]; // index = 5
}
geometry.push({
color: placedGeometry.color,
positions,
normals,
indices: Array.from(ifcIndices)
indices: Array.from(ifcIndices),
flatTransformation: placedGeometry.flatTransformation
});
ifcGeometry.delete();
}
Expand Down Expand Up @@ -143,15 +130,16 @@ export const getCapabilities = (url) => {
// todo: read IFCProjectedCRS, IFCMapCONVERSION in case of IFC4
let bbox = {
bounds: capabilities.version !== "IFC4" ? {
minx: 0,
miny: 0,
maxx: 0,
maxy: 0
minx: 0 - 2,
miny: 0 - 2,
maxx: 0 + 2,
maxy: 0 + 2
} : {
minx: 0,
miny: 0,
maxx: 0,
maxy: 0},
minx: 0 - 2,
miny: 0 - 2,
maxx: 0 + 2,
maxy: 0 + 2
},
crs: 'EPSG:4326'
};
return { modelData: data, ...capabilities, ...(bbox && { bbox })};
Expand Down
5 changes: 5 additions & 0 deletions web/client/components/TOC/fragments/settings/Display.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Select from 'react-select';
import { getSupportedFormat } from '../../../../api/WMS';
import WMSCacheOptions from './WMSCacheOptions';
import ThreeDTilesSettings from './ThreeDTilesSettings';
import ModelTransformation from './ModelTransformation';
export default class extends React.Component {
static propTypes = {
opacityText: PropTypes.node,
Expand Down Expand Up @@ -211,6 +212,10 @@ export default class extends React.Component {
layer={this.props.element}
onChange={this.props.onChange}
/>
<ModelTransformation
layer={this.props.element}
onChange={this.props.onChange}
/>

{this.props.element.type === "wms" &&
<Row>
Expand Down
108 changes: 108 additions & 0 deletions web/client/components/TOC/fragments/settings/ModelTransformation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@

/*
* Copyright 2023, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/


import React from 'react';
import PropTypes from 'prop-types';
import { FormGroup, ControlLabel, InputGroup } from 'react-bootstrap';
import DebouncedFormControl from '../../../misc/DebouncedFormControl';
import Message from '../../../I18N/Message';

/**
* ModelTransformation. This component shows the model transformation options available
* @prop {object} layer the layer options
* @prop {object} onChange callback on every on change event
*/
function ModelTransformation({
layer,
onChange
}) {
if (layer?.type !== 'model') {
return null;
}
return (
<div style={{ margin: '0 -8px' }}>
<FormGroup className="form-group-flex">
<ControlLabel><Message msgId="layerProperties.modelCenterLat"/></ControlLabel>
<InputGroup style={{ maxWidth: 210 }}>
<DebouncedFormControl
type="number"
name={"modelCenterLat"}
value={layer?.center?.[1] || 0}
fallbackValue={0}
onChange={(val)=> {
const newCenter = [
layer?.center?.[0] ?? 0,
val !== undefined
? parseFloat(val) : 0,
layer?.center?.[2] ?? 0
];
onChange('center', newCenter);
}}
/>
<InputGroup.Addon>DD</InputGroup.Addon>
</InputGroup>
</FormGroup>
<FormGroup className="form-group-flex">
<ControlLabel><Message msgId="layerProperties.modelCenterLng"/></ControlLabel>
<InputGroup style={{ maxWidth: 210 }}>
<DebouncedFormControl
type="number"
name={"modelCenterLng"}
value={layer?.center?.[0] || 0}
fallbackValue={0}
onChange={(val)=> {
const newCenter = [
val !== undefined
? parseFloat(val) : 0,
layer?.center?.[1] ?? 0,
layer?.center?.[2] ?? 0
];
onChange('center', newCenter);
}}
/>
<InputGroup.Addon>DD</InputGroup.Addon>
</InputGroup>
</FormGroup>
<FormGroup className="form-group-flex">
<ControlLabel><Message msgId="layerProperties.heightOffset"/></ControlLabel>
<InputGroup style={{ maxWidth: 120 }}>
<DebouncedFormControl
type="number"
name={"heightOffset"}
value={layer?.center?.[2] || 0}
fallbackValue={0}
onChange={(val)=> {
const newCenter = [
layer?.center?.[0] ?? 0,
layer?.center?.[1] ?? 0,
val !== undefined
? parseFloat(val) : 0
];
onChange('center', newCenter);
}}
/>
<InputGroup.Addon>m</InputGroup.Addon>
</InputGroup>
</FormGroup>
</div>
);
}

ModelTransformation.propTypes = {
layer: PropTypes.object,
onChange: PropTypes.func
};

ModelTransformation.defaultProps = {
layer: {},
onChange: () => {}
};

export default ModelTransformation;
3 changes: 2 additions & 1 deletion web/client/components/map/cesium/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ class CesiumMap extends React.Component {
: undefined,
requestRenderMode: true,
maximumRenderTimeChange: Infinity,
skyBox: false
skyBox: false,
scene3DOnly: true
}, this.getMapOptions(this.props.mapOptions)));

// prevent default behavior
Expand Down
94 changes: 48 additions & 46 deletions web/client/components/map/cesium/plugins/ModelLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,40 @@
*/

import * as Cesium from 'cesium';
import isEqual from 'lodash/isEqual';
import Layers from '../../../../utils/cesium/Layers';
import { ifcDataToJSON, getWebIFC } from '../../../../api/Model'; // todo: change path to MODEL


const transform = (positions, coords, matrix) => {
let transformed = [];
for (let i = 0; i < positions.length; i += 3) {
const cartesian = Cesium.Matrix4.multiplyByPoint(matrix, new Cesium.Cartesian3(
positions[i] + coords[0],
positions[i + 1] + coords[1],
positions[i + 2] + coords[2]
), new Cesium.Cartesian3());
transformed.push(
cartesian.x,
cartesian.y,
cartesian.z
const updatePrimitivesPosition = (primitives, center) => {
for (let i = 0; i < primitives.length; i++) {
const primitive = primitives.get(i);
primitive.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
// review the center properties
// based on other existing layer parameters
Cesium.Cartesian3.fromDegrees(...(center ? [
center[0],
center[1],
center[2]
] : [0, 0, 0]))
);
}
return transformed;
};

const updatePrimitivesVisibility = (primitives, visibilityOption) => {
for (let i = 0; i < primitives.length; i++) {
const primitive = primitives.get(i);
primitive.show = visibilityOption;
}
};
const getGeometryInstances = ({
meshes,
center,
options
meshes
}) => {
return meshes
.map((mesh) => mesh.geometry.map(({
color,
positions,
normals,
indices
indices,
flatTransformation
}) => {
const rotationMatrix = Cesium.Matrix4.fromTranslationQuaternionRotationScale(
new Cesium.Cartesian3(0.0, 0.0, 0.0), // 0,0
Expand All @@ -49,30 +51,15 @@ const getGeometryInstances = ({
new Cesium.Cartesian3(1.0, 1.0, 1.0),
new Cesium.Matrix4()
);
const transformedPositions = transform(
positions,
[-center[0], -center[1], -center[2]],
Cesium.Matrix4.multiply(
Cesium.Transforms.eastNorthUpToFixedFrame(
// review the center properties
// based on other existing layer parameters
Cesium.Cartesian3.fromDegrees(...(options.center ? [
options.center[0],
options.center[1],
options.center[2]
] : [0, 0, 0]))
),
rotationMatrix,
new Cesium.Matrix4()
)
);
const transformedNormals = transform(
normals,
[0, 0, 0],
rotationMatrix
);
const transformedPositions = positions;
const transformedNormals = normals;
return new Cesium.GeometryInstance({
id: mesh.id,
modelMatrix: Cesium.Matrix4.multiply(
rotationMatrix,
flatTransformation,
new Cesium.Matrix4()
),
geometry: new Cesium.Geometry({
attributes: {
position: new Cesium.GeometryAttribute({
Expand All @@ -91,8 +78,6 @@ const getGeometryInstances = ({
primitiveType: Cesium.PrimitiveType.TRIANGLES,
boundingSphere: Cesium.BoundingSphere.fromVertices(transformedPositions)
}),

// modelMatrix: ,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(
color.x,
Expand Down Expand Up @@ -157,6 +142,8 @@ const createLayer = (options, map) => {
opaquePrimitive.msId = options.id;
opaquePrimitive.id = 'opaquePrimitive';
primitives.add(opaquePrimitive);
updatePrimitivesPosition(primitives, options.center);

});
});
map.scene.primitives.add(primitives);
Expand All @@ -171,17 +158,32 @@ const createLayer = (options, map) => {
}
},
setVisible: (
// newVisiblity
newVisibility
) => {
// todo: add the logic of setting visibility
if (primitives && map) {
updatePrimitivesVisibility(primitives, newVisibility);
}
}
};
};

Layers.registerType('model', {
create: createLayer,
update: (/* layer, newOptions, oldOptions, map */) => {
// todo: here we can put change opacity logic
update: (layer, newOptions, oldOptions) => {
if (layer?.primitives && !isEqual(newOptions?.center, oldOptions?.center)) {
// update layer.bbox
layer.bbox = {
...layer.bbox,
bounds: {
minx: newOptions?.center?.[0] || 0 - 2,
miny: newOptions?.center?.[1] || 0 - 2,
maxx: newOptions?.center?.[0] || 0 + 2,
maxy: newOptions?.center?.[1] || 0 + 2
}
};
updatePrimitivesPosition(layer?.primitives, newOptions?.center);
}
return null;
}
});
2 changes: 2 additions & 0 deletions web/client/translations/data.de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
"templateError": "Beim Anwenden der Vorlage auf die vom Server zurückgegebenen Informationen ist ein Fehler aufgetreten. Bitte überprüfen Sie die Vorlage",
"templatePreview": "Vorschau template",
"heightOffset": "Höhenversatz (m)",
"modelCenterLat": "Breitengrad des Zentrums (DD)",
"modelCenterLng": "Längengrad des Zentrums (DD)",
"wmsLayerTileSize": "Kachelgröße (WMS)",
"serverType": "Servertyp",
"formatError": "Es war nicht möglich, Format und Informationsblattformat vom konfigurierten Dienst abzurufen. Wahrscheinlich verwenden Sie einen No-Vendor-Dienst und dieser wird nicht unterstützt (z. B. GeoNetwork).",
Expand Down
Loading

0 comments on commit 7adaf68

Please sign in to comment.