From 311be122a46ce31ab7e4578f82e1d8942d8fe0e2 Mon Sep 17 00:00:00 2001 From: mahmoudadel54 Date: Wed, 24 Jan 2024 15:57:51 +0200 Subject: [PATCH] #9830: Support for IFC as a further 3D model Description: - Hide opacity from display tab for ifc model - Write unit tests - Update copyright year for the new created files --- web/client/api/Model.js | 2 +- web/client/api/catalog/Model.js | 2 +- .../api/catalog/__tests__/Model-test.js | 142 ++++++++++++++++++ .../TOC/fragments/settings/Display.jsx | 2 +- .../settings/ModelTransformation.jsx | 2 +- .../__tests__/ModelTransformation-test.jsx | 2 +- .../map/cesium/plugins/ModelLayer.js | 2 +- .../utils/mapinfo/__tests__/model-test.js | 65 ++++++++ web/client/utils/mapinfo/model.js | 14 +- 9 files changed, 220 insertions(+), 13 deletions(-) create mode 100644 web/client/api/catalog/__tests__/Model-test.js create mode 100644 web/client/utils/mapinfo/__tests__/model-test.js diff --git a/web/client/api/Model.js b/web/client/api/Model.js index 6a7eed10c3..23d9ad3fcd 100644 --- a/web/client/api/Model.js +++ b/web/client/api/Model.js @@ -1,5 +1,5 @@ /* - * Copyright 2023, GeoSolutions Sas. + * Copyright 2024, GeoSolutions Sas. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/web/client/api/catalog/Model.js b/web/client/api/catalog/Model.js index c5724441a1..9f08fee315 100644 --- a/web/client/api/catalog/Model.js +++ b/web/client/api/catalog/Model.js @@ -1,5 +1,5 @@ /* - * Copyright 2023, GeoSolutions Sas. + * Copyright 2024, GeoSolutions Sas. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/web/client/api/catalog/__tests__/Model-test.js b/web/client/api/catalog/__tests__/Model-test.js new file mode 100644 index 0000000000..870366de45 --- /dev/null +++ b/web/client/api/catalog/__tests__/Model-test.js @@ -0,0 +1,142 @@ +/** + * Copyright 2024, 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 expect from 'expect'; +import { + textSearch, + getLayerFromRecord, + getCatalogRecords, + validate +} from '../Model'; + + +describe('Test IFC Model catalog API', () => { + it('should return a single record for ifcModel.ifc', (done) => { + textSearch('base/web/client/test-resources/ifcModels/ifcModel.ifc') + .then((response) => { + expect(response.records.length).toBe(1); + expect(response.records[0].title).toBe('ifcModels'); + done(); + }); + }); + it('should always return a single record even if title not match the filter', (done) => { + textSearch('base/web/client/test-resources/ifcModels/ifcModel.ifc', undefined, undefined, 'filter') + .then((response) => { + expect(response.records.length).toBe(1); + done(); + }); + }); + it('should return a single record if title match the filter', (done) => { + textSearch('base/web/client/test-resources/ifcModels/ifcModel.ifc', undefined, undefined, 'service') + .then((response) => { + expect(response.records.length).toBe(1); + expect(response.records[0].title).toBe('ifcModels'); + done(); + }); + }); + it('should return a single record with title equal to the last path fragment', (done) => { + textSearch('base/web/client/test-resources/ifcModels/ifcModel.ifc') + .then((response) => { + expect(response.records.length).toBe(1); + expect(response.records[0].title).toBe('ifcModels'); + done(); + }); + }); + it('should return a single record with title from info service if available', (done) => { + textSearch('base/web/client/test-resources/ifcModels/ifcModel.ifc', undefined, undefined, '', { options: { service: { title: 'ifc model title' } } }) + .then((response) => { + expect(response.records.length).toBe(1); + expect(response.records[0].title).toBe('ifc model title'); + done(); + }); + }); + it('should map the tileset record to a catalog record', () => { + const records = getCatalogRecords({ records: [{ + title: 'Title', + url: 'base/web/client/test-resources/ifcModels/ifcModel.ifc', + type: 'model', + version: '1.0' + }] }); + expect(records.length).toBe(1); + const { + serviceType, + isValid, + description, + title, + identifier, + url + } = records[0]; + + expect(serviceType).toBe('model'); + expect(isValid).toBe(true); + expect(description).toBe('v. 1.0'); + expect(title).toBe('Title'); + expect(identifier).toBe('base/web/client/test-resources/ifcModels/ifcModel.ifc'); + expect(url).toBe('base/web/client/test-resources/ifcModels/ifcModel.ifc'); + }); + + it('should extract the layer config from a catalog record', () => { + const catalogRecord = { + serviceType: 'model', + isValid: true, + description: 'v. 1.0', + title: 'Title', + identifier: 'base/web/client/test-resources/ifcModels/ifcModel.ifc', + url: 'base/web/client/test-resources/ifcModels/ifcModel.ifc', + thumbnail: null, + bbox: { + crs: 'EPSG:4326', + bounds: { + minx: 0, + miny: 0, + maxx: 0, + maxy: 0 + } + }, + references: [] + }; + const layer = getLayerFromRecord(catalogRecord); + expect(layer).toEqual({ + type: 'model', + url: 'base/web/client/test-resources/ifcModels/ifcModel.ifc', + title: 'Title', + visibility: true, + bbox: { + crs: 'EPSG:4326', + bounds: { + minx: 0, + miny: 0, + maxx: 0, + maxy: 0 + } + }, + center: [0, 0, 0] + }); + }); + it('should validate if the service url ends with .ifc', (done) => { + const service = { title: 'IFC Model', url: 'https://service.org/ifcModel.ifc' }; + validate(service) + .toPromise() + .then((response) => { + expect(response).toBeTruthy(true); + done(); + }) + .catch(e => { + done(e); + }); + }); + it('should throw an error on validation if service does not ends with .ifc', (done) => { + const service = { title: 'IFC Model', url: 'http://service.org/ifcmodel' }; + try { + validate(service); + } catch (e) { + expect(e.message).toBe('catalog.config.notValidURLTemplate'); + done(); + } + }); +}); diff --git a/web/client/components/TOC/fragments/settings/Display.jsx b/web/client/components/TOC/fragments/settings/Display.jsx index 34d823d546..ba52c1ae39 100644 --- a/web/client/components/TOC/fragments/settings/Display.jsx +++ b/web/client/components/TOC/fragments/settings/Display.jsx @@ -178,7 +178,7 @@ export default class extends React.Component { } - {this.props.element.type !== "3dtiles" && + {!["3dtiles", 'model'].includes(this.props.element.type) && {this.props.opacityText} % diff --git a/web/client/components/TOC/fragments/settings/ModelTransformation.jsx b/web/client/components/TOC/fragments/settings/ModelTransformation.jsx index f5127dbc34..3f95914e2e 100644 --- a/web/client/components/TOC/fragments/settings/ModelTransformation.jsx +++ b/web/client/components/TOC/fragments/settings/ModelTransformation.jsx @@ -1,6 +1,6 @@ /* - * Copyright 2023, GeoSolutions Sas. + * Copyright 2024, GeoSolutions Sas. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/web/client/components/TOC/fragments/settings/__tests__/ModelTransformation-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/ModelTransformation-test.jsx index 87ddd73c4b..4fdf07ce89 100644 --- a/web/client/components/TOC/fragments/settings/__tests__/ModelTransformation-test.jsx +++ b/web/client/components/TOC/fragments/settings/__tests__/ModelTransformation-test.jsx @@ -1,5 +1,5 @@ /* - * Copyright 2023, GeoSolutions Sas. + * Copyright 2024, GeoSolutions Sas. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/web/client/components/map/cesium/plugins/ModelLayer.js b/web/client/components/map/cesium/plugins/ModelLayer.js index a39a370450..a9f4cae0c6 100644 --- a/web/client/components/map/cesium/plugins/ModelLayer.js +++ b/web/client/components/map/cesium/plugins/ModelLayer.js @@ -1,5 +1,5 @@ /* - * Copyright 2023, GeoSolutions Sas. + * Copyright 2024, GeoSolutions Sas. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/web/client/utils/mapinfo/__tests__/model-test.js b/web/client/utils/mapinfo/__tests__/model-test.js new file mode 100644 index 0000000000..357ac50b9c --- /dev/null +++ b/web/client/utils/mapinfo/__tests__/model-test.js @@ -0,0 +1,65 @@ +/* + * Copyright 2024, 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 expect from 'expect'; +import model from '../model'; + +describe('mapinfo 3D tiles utils', () => { + it('should create a request with empty features', () => { + const layer = { + title: 'Title' + }; + const request = model.buildRequest(layer); + expect(request).toEqual({ + request: { + features: [], + outputFormat: 'application/json', + lat: undefined, + lng: undefined + }, + metadata: { + title: layer.title, fields: [], resolution: undefined, buffer: 2, units: undefined, rowViewer: undefined, viewer: undefined, layerId: undefined + }, + url: 'client' + }); + }); + it('should create a request with features retrieved from the intersected ones', () => { + const layer = { + id: 'layer-id', + title: 'Title' + }; + const point = { + intersectedFeatures: [{ id: 'layer-id', features: [{ type: 'Feature', properties: { key: 'value' }, geometry: null }] }], latlng: { lat: 1, lng: 1 } + }; + const request = model.buildRequest(layer, { point }); + expect(request).toEqual({ + request: { + features: [{ type: 'Feature', properties: { key: 'value' }, geometry: null }], + outputFormat: 'application/json', + lat: 1, + lng: 1 + }, + metadata: { + title: layer.title, fields: [], resolution: undefined, buffer: 2, units: undefined, rowViewer: undefined, viewer: undefined, layerId: 'layer-id' + }, + url: 'client' + }); + }); + it('should return the response object from getIdentifyFlow', (done) => { + model.getIdentifyFlow(undefined, undefined, { features: [] }) + .toPromise() + .then((response) => { + expect(response).toEqual({ + data: { + features: [] + } + }); + done(); + }); + }); +}); diff --git a/web/client/utils/mapinfo/model.js b/web/client/utils/mapinfo/model.js index 862c148105..79aa607640 100644 --- a/web/client/utils/mapinfo/model.js +++ b/web/client/utils/mapinfo/model.js @@ -1,5 +1,5 @@ /** - * Copyright 2017, GeoSolutions Sas. + * Copyright 2024, GeoSolutions Sas. * All rights reserved. * * This source code is licensed under the BSD-style license found in the @@ -22,17 +22,17 @@ export default { request: { features: [...features], outputFormat: 'application/json', - lat: props.point.latlng.lat, - lng: props.point.latlng.lng + lat: props?.point?.latlng?.lat, + lng: props?.point?.latlng?.lng }, metadata: { fields: layer.features?.[0]?.properties && Object.keys(layer.features[0].properties) || [], title, resolution: isNil(props?.map?.resolution) - ? props?.map?.zoom && getCurrentResolution(props.map.zoom, 0, 21, 96) - : props.map.resolution, - buffer: props.buffer || 2, - units: props.map && props.map.units, + ? props?.map?.zoom && getCurrentResolution(props?.map?.zoom, 0, 21, 96) + : props?.map?.resolution, + buffer: props?.buffer || 2, + units: props?.map && props.map.units, rowViewer: layer.rowViewer, viewer: layer.viewer, layerId: layer.id