diff --git a/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx b/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx
index e51683cf7c..5d1cc702e1 100644
--- a/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx
+++ b/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx
@@ -254,13 +254,6 @@ function VisibilityLimitsForm({
clearMessages();
}, [ dpu, resolutionString ]);
- useEffect(() => {
- if (isMounted.current && (!isNil(maxResolution) || !isNil(minResolution))) {
- setCapabilitiesMessage(maxResolution, minResolution);
- setRangeError(maxResolution, minResolution);
- }
- }, [isMounted]);
-
return (
diff --git a/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx
index a9226b24e2..bd441c78bb 100644
--- a/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx
+++ b/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx
@@ -47,8 +47,6 @@ describe('VisibilityLimitsForm', () => {
'1 : 37795',
'layerProperties.visibilityLimits.scale'
]);
- const message = document.querySelector('.alert-success');
- expect(message.textContent).toBe('layerProperties.visibilityLimits.serverValuesUpdate');
});
it('should render maxResolution and minResolution labels as resolution', () => {
const layer = {
diff --git a/web/client/components/map/cesium/Layer.jsx b/web/client/components/map/cesium/Layer.jsx
index 6896af105b..bead9e5ee9 100644
--- a/web/client/components/map/cesium/Layer.jsx
+++ b/web/client/components/map/cesium/Layer.jsx
@@ -25,8 +25,11 @@ class CesiumLayer extends React.Component {
};
componentDidMount() {
- this.createLayer(this.props.type, this.props.options, this.props.position, this.props.map, this.props.securityToken);
- if (this.props.options && this.layer && this.getVisibilityOption(this.props)) {
+ // initial visibility should also take into account the visibility limits
+ // in particular for detached layers (eg. Vector, WFS, 3D Tiles, ...)
+ const visibility = this.getVisibilityOption(this.props);
+ this.createLayer(this.props.type, { ...this.props.options, visibility }, this.props.position, this.props.map, this.props.securityToken);
+ if (this.props.options && this.layer && visibility) {
this.addLayer(this.props);
this.updateZIndex();
}
@@ -129,19 +132,46 @@ class CesiumLayer extends React.Component {
}
};
+ setDetachedLayerVisibility = (visibility, props) => {
+ // use internal setVisible
+ // if a detached layers implements setVisible
+ if (this.layer?.setVisible) {
+ this.layer.setVisible(visibility);
+ return;
+ }
+ // if visible we will remove the layer and create a new one
+ if (visibility) {
+ this.removeLayer();
+ this.createLayer(props.type, {
+ ...props.options,
+ visibility
+ }, props.position, props.map, props.securityToken);
+ return;
+ }
+ // while hidden layers will be completely removed
+ this.removeLayer();
+ return;
+ };
+
+ setImageryLayerVisibility = (visibility, props) => {
+ // this type of layer will be added and removed from the imageryLayers array of Cesium
+ if (visibility) {
+ this.addLayer(props);
+ this.updateZIndex();
+ return;
+ }
+ this.removeLayer();
+ return;
+ }
+
setLayerVisibility = (newProps) => {
const oldVisibility = this.getVisibilityOption(this.props);
const newVisibility = this.getVisibilityOption(newProps);
if (newVisibility !== oldVisibility) {
- if (this.layer?.detached && this.layer?.setVisible) {
- this.layer.setVisible(newVisibility);
+ if (!!this.layer?.detached) {
+ this.setDetachedLayerVisibility(newVisibility, newProps);
} else {
- if (newVisibility) {
- this.addLayer(newProps);
- this.updateZIndex();
- } else {
- this.removeLayer();
- }
+ this.setImageryLayerVisibility(newVisibility, newProps);
}
newProps.map.scene.requestRender();
}
@@ -167,7 +197,7 @@ class CesiumLayer extends React.Component {
return false;
}
}
- return visibility;
+ return !!visibility;
};
setLayerOpacity = (opacity) => {
diff --git a/web/client/components/map/cesium/Map.jsx b/web/client/components/map/cesium/Map.jsx
index 9caa4d90bd..474e9d0339 100644
--- a/web/client/components/map/cesium/Map.jsx
+++ b/web/client/components/map/cesium/Map.jsx
@@ -336,7 +336,15 @@ class CesiumMap extends React.Component {
};
getZoomFromHeight = (height) => {
- return Math.log2(this.props.zoomToHeight / height) + 1;
+ let distance = height;
+ // when camera is tilted we could compute the height as the distance between the camera point of view and the viewed point on the map
+ // the viewed point or target is computed as the intersection of an imaginary vector based on the camera direction (ray) and the globe surface
+ // if the camera is orthogonal to the globe distance should match the height so this computation is still valid
+ const target = this.map.scene.globe.pick(new Cesium.Ray(this.map.camera.position, this.map.camera.direction), this.map.scene);
+ if (target) {
+ distance = Cesium.Cartesian3.distance(target, this.map.camera.position);
+ }
+ return Math.log2(this.props.zoomToHeight / distance) + 1;
};
getHeightFromZoom = (zoom) => {
diff --git a/web/client/components/map/cesium/__tests__/Layer-test.jsx b/web/client/components/map/cesium/__tests__/Layer-test.jsx
index 2e7ab23bb2..f8cf9e2cef 100644
--- a/web/client/components/map/cesium/__tests__/Layer-test.jsx
+++ b/web/client/components/map/cesium/__tests__/Layer-test.jsx
@@ -588,7 +588,8 @@ describe('Cesium layer', () => {
let options = {
id: 'overlay-1',
- position: { x: 13, y: 43 }
+ position: { x: 13, y: 43 },
+ visibility: true
};
// create layers
let layer = ReactDOM.render(
@@ -616,7 +617,8 @@ describe('Cesium layer', () => {
position: { x: 13, y: 43 },
onClose: () => {
closed = true;
- }
+ },
+ visibility: true
};
// create layers
let layer = ReactDOM.render(
@@ -643,7 +645,8 @@ describe('Cesium layer', () => {
document.body.appendChild(element);
let options = {
id: 'overlay-1',
- position: { x: 13, y: 43 }
+ position: { x: 13, y: 43 },
+ visibility: true
};
// create layers
let layer = ReactDOM.render(
@@ -659,7 +662,8 @@ describe('Cesium layer', () => {
it('creates a marker layer for cesium map', () => {
let options = {
- point: { lng: 13, lat: 43 }
+ point: { lng: 13, lat: 43 },
+ visibility: true
};
// create layers
let layer = ReactDOM.render(
diff --git a/web/client/components/map/cesium/__tests__/Map-test.jsx b/web/client/components/map/cesium/__tests__/Map-test.jsx
index 05f7bd6cca..04c4a7a8ff 100644
--- a/web/client/components/map/cesium/__tests__/Map-test.jsx
+++ b/web/client/components/map/cesium/__tests__/Map-test.jsx
@@ -168,7 +168,7 @@ describe('CesiumMap', () => {
try {
expect(Math.round(Math.round(center.y * precision) / precision)).toBe(30);
expect(Math.round(Math.round(center.x * precision) / precision)).toBe(20);
- expect(zoom).toBe(5);
+ expect(Math.round(zoom)).toBe(5);
expect(bbox.bounds).toBeTruthy();
expect(bbox.crs).toBeTruthy();
expect(size.height).toBeTruthy();
diff --git a/web/client/components/map/cesium/plugins/MarkerLayer.js b/web/client/components/map/cesium/plugins/MarkerLayer.js
index aeb1e5b201..1847d39152 100644
--- a/web/client/components/map/cesium/plugins/MarkerLayer.js
+++ b/web/client/components/map/cesium/plugins/MarkerLayer.js
@@ -16,6 +16,13 @@ import { isEqual } from 'lodash';
*/
Layers.registerType('marker', {
create: (options, map) => {
+ if (!options.visibility) {
+ return {
+ detached: true,
+ point: undefined,
+ remove: () => {}
+ };
+ }
const style = {
point: {
pixelSize: 5,
@@ -26,7 +33,7 @@ Layers.registerType('marker', {
...options.style
};
const point = map.entities.add({
- position: Cesium.Cartesian3.fromDegrees(options.point.lng, options.point.lat),
+ position: Cesium.Cartesian3.fromDegrees(options?.point?.lng || 0, options?.point?.lat || 0),
...style
});
return {
@@ -38,12 +45,8 @@ Layers.registerType('marker', {
};
},
update: function(layer, newOptions, oldOptions, map) {
- if (!isEqual(newOptions.point, oldOptions.point)
- || newOptions.visibility !== oldOptions.visibility) {
- layer.remove();
- return newOptions.visibility
- ? this.create(newOptions, map)
- : null;
+ if (!isEqual(newOptions.point, oldOptions.point)) {
+ return this.create(newOptions, map);
}
return null;
}
diff --git a/web/client/components/map/cesium/plugins/OverlayLayer.js b/web/client/components/map/cesium/plugins/OverlayLayer.js
index f7beacf075..81f233f101 100644
--- a/web/client/components/map/cesium/plugins/OverlayLayer.js
+++ b/web/client/components/map/cesium/plugins/OverlayLayer.js
@@ -166,11 +166,19 @@ const cloneOriginalOverlay = (original, options) => {
Layers.registerType('overlay', {
create: (options, map) => {
+ if (!options.visibility) {
+ return {
+ detached: true,
+ info: undefined,
+ remove: () => {}
+ };
+ }
const original = document.getElementById(options.id);
- const cloned = cloneOriginalOverlay(original, options);
+ // use a div fallback to avoid error if the original element does not exist
+ const cloned = original ? cloneOriginalOverlay(original, options) : document.createElement('div');
let infoWindow = new InfoWindow(map);
- infoWindow.showAt(options.position.y, options.position.x, cloned);
+ infoWindow.showAt(options?.position?.y || 0, options?.position?.x || 0, cloned);
infoWindow.setVisible(true);
let info = map.scene.primitives.add(infoWindow);
@@ -183,12 +191,8 @@ Layers.registerType('overlay', {
};
},
update: function(layer, newOptions, oldOptions, map) {
- if (!isEqual(newOptions.position, oldOptions.position)
- || newOptions.visibility !== oldOptions.visibility) {
- layer.remove();
- return newOptions.visibility
- ? this.create(newOptions, map)
- : null;
+ if (!isEqual(newOptions.position, oldOptions.position)) {
+ return this.create(newOptions, map);
}
return null;
}
diff --git a/web/client/components/map/cesium/plugins/TerrainLayer.js b/web/client/components/map/cesium/plugins/TerrainLayer.js
index 41755b5462..0351362f4b 100644
--- a/web/client/components/map/cesium/plugins/TerrainLayer.js
+++ b/web/client/components/map/cesium/plugins/TerrainLayer.js
@@ -48,8 +48,7 @@ const createLayer = (config, map) => {
terrainProvider,
remove: () => {
map.terrainProvider = new Cesium.EllipsoidTerrainProvider();
- },
- setVisible: () => {}
+ }
};
};
diff --git a/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js b/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js
index 812c0abd5e..109a5a82ef 100644
--- a/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js
+++ b/web/client/components/map/cesium/plugins/ThreeDTilesLayer.js
@@ -129,73 +129,67 @@ function updateShading(tileSet, options, map) {
Layers.registerType('3dtiles', {
create: (options, map) => {
- if (options.visibility && options.url) {
-
- let tileSet;
- const resource = new Cesium.Resource({
- url: options.url,
- proxy: needProxy(options.url) ? new Cesium.DefaultProxy(getProxyUrl()) : undefined
- // TODO: axios supports also adding access tokens or credentials (e.g. authkey, Authentication header ...).
- // if we want to use internal cesium functionality to retrieve data
- // we need to create a utility to set a CesiumResource that applies also this part.
- // in addition to this proxy.
- });
- Cesium.Cesium3DTileset.fromUrl(resource,
- {
- showCreditsOnScreen: true
- }
- ).then((_tileSet) => {
- tileSet = _tileSet;
- updateGooglePhotorealistic3DTilesBrandLogo(map, options, tileSet);
- map.scene.primitives.add(tileSet);
- // assign the original mapstore id of the layer
- tileSet.msId = options.id;
-
- ensureReady(tileSet, () => {
- updateModelMatrix(tileSet, options);
- clip3DTiles(tileSet, options, map);
- updateShading(tileSet, options, map);
- getStyle(options)
- .then((style) => {
- if (style) {
- tileSet.style = new Cesium.Cesium3DTileStyle(style);
- }
- });
- });
- });
-
+ if (!options.visibility) {
return {
detached: true,
- getTileSet: () => tileSet,
- resource,
- remove: () => {
- if (tileSet) {
- updateGooglePhotorealistic3DTilesBrandLogo(map, options, tileSet);
- map.scene.primitives.remove(tileSet);
- }
- },
- setVisible: (visible) => {
- if (tileSet) {
- tileSet.show = !!visible;
- }
- }
+ getTileSet: () => undefined,
+ remove: () => {}
};
}
+ let tileSet;
+ const resource = new Cesium.Resource({
+ url: options.url,
+ proxy: needProxy(options.url) ? new Cesium.DefaultProxy(getProxyUrl()) : undefined
+ // TODO: axios supports also adding access tokens or credentials (e.g. authkey, Authentication header ...).
+ // if we want to use internal cesium functionality to retrieve data
+ // we need to create a utility to set a CesiumResource that applies also this part.
+ // in addition to this proxy.
+ });
+ let promise = Cesium.Cesium3DTileset.fromUrl(resource,
+ {
+ showCreditsOnScreen: true
+ }
+ ).then((_tileSet) => {
+ tileSet = _tileSet;
+ updateGooglePhotorealistic3DTilesBrandLogo(map, options, tileSet);
+ map.scene.primitives.add(tileSet);
+ // assign the original mapstore id of the layer
+ tileSet.msId = options.id;
+
+ ensureReady(tileSet, () => {
+ updateModelMatrix(tileSet, options);
+ clip3DTiles(tileSet, options, map);
+ updateShading(tileSet, options, map);
+ getStyle(options)
+ .then((style) => {
+ if (style) {
+ tileSet.style = new Cesium.Cesium3DTileStyle(style);
+ }
+ });
+ });
+ });
+ const removeTileset = () => {
+ updateGooglePhotorealistic3DTilesBrandLogo(map, options, tileSet);
+ map.scene.primitives.remove(tileSet);
+ tileSet = undefined;
+ };
return {
detached: true,
- getTileSet: () => undefined,
- remove: () => {},
- setVisible: () => {}
+ getTileSet: () => tileSet,
+ resource,
+ remove: () => {
+ if (tileSet) {
+ removeTileset();
+ return;
+ }
+ promise.then(() => {
+ removeTileset();
+ });
+ return;
+ }
};
},
update: function(layer, newOptions, oldOptions, map) {
- if (newOptions.visibility && !oldOptions.visibility) {
- return this.create(newOptions, map);
- }
- if (!newOptions.visibility && oldOptions.visibility && layer?.remove) {
- layer.remove();
- return null;
- }
const tileSet = layer?.getTileSet();
if (
(!isEqual(newOptions.clippingPolygon, oldOptions.clippingPolygon)
diff --git a/web/client/components/map/cesium/plugins/VectorLayer.js b/web/client/components/map/cesium/plugins/VectorLayer.js
index ade7ce74b5..2020c1f8ee 100644
--- a/web/client/components/map/cesium/plugins/VectorLayer.js
+++ b/web/client/components/map/cesium/plugins/VectorLayer.js
@@ -18,42 +18,50 @@ import { applyDefaultStyleToVectorLayer } from '../../../../utils/StyleUtils';
const createLayer = (options, map) => {
- let dataSource = new Cesium.GeoJsonDataSource(options?.id);
+ if (!options.visibility) {
+ return {
+ detached: true,
+ dataSource: undefined,
+ remove: () => {}
+ };
+ }
+ let dataSource = new Cesium.GeoJsonDataSource(options?.id);
+ dataSource.loadingEvent.addEventListener(() => {
+ // ensure it updates render on every loading
+ map.scene.requestRender();
+ });
const features = flattenFeatures(options?.features || [], ({ style, ...feature }) => feature);
const collection = {
type: 'FeatureCollection',
features
};
-
- if (options.visibility) {
- dataSource.load(collection, {
- // ensure default style is not applied
- stroke: new Cesium.Color(0, 0, 0, 0),
- fill: new Cesium.Color(0, 0, 0, 0),
- markerColor: new Cesium.Color(0, 0, 0, 0),
- strokeWidth: 0,
- markerSize: 0
- }).then(() => {
- map.dataSources.add(dataSource);
- layerToGeoStylerStyle(options)
- .then((style) => {
- getStyle(applyDefaultStyleToVectorLayer({ ...options, style }), 'cesium')
- .then((styleFunc) => {
- if (styleFunc) {
- styleFunc({
- entities: dataSource?.entities?.values,
- map,
- opacity: options.opacity ?? 1,
- features: options.features
- }).then(() => {
- map.scene.requestRender();
- });
- }
- });
- });
- });
- }
+ dataSource.load(collection, {
+ // ensure default style is not applied
+ stroke: new Cesium.Color(0, 0, 0, 0),
+ fill: new Cesium.Color(0, 0, 0, 0),
+ markerColor: new Cesium.Color(0, 0, 0, 0),
+ strokeWidth: 0,
+ markerSize: 0
+ }).then(() => {
+ map.dataSources.add(dataSource);
+ layerToGeoStylerStyle(options)
+ .then((style) => {
+ getStyle(applyDefaultStyleToVectorLayer({ ...options, style }), 'cesium')
+ .then((styleFunc) => {
+ if (styleFunc) {
+ styleFunc({
+ entities: dataSource?.entities?.values,
+ map,
+ opacity: options.opacity ?? 1,
+ features: options.features
+ }).then(() => {
+ map.scene.requestRender();
+ });
+ }
+ });
+ });
+ });
dataSource.show = !!options.visibility;
dataSource.queryable = options.queryable === undefined || options.queryable;
@@ -66,16 +74,14 @@ const createLayer = (options, map) => {
map.dataSources.remove(dataSource);
dataSource = undefined;
}
- },
- setVisible: () => {}
+ }
};
};
Layers.registerType('vector', {
create: createLayer,
update: (layer, newOptions, oldOptions, map) => {
- if (!isEqual(newOptions.features, oldOptions.features)
- || newOptions.visibility !== oldOptions.visibility) {
+ if (!isEqual(newOptions.features, oldOptions.features)) {
return createLayer(newOptions, map);
}
diff --git a/web/client/components/map/cesium/plugins/WFSLayer.js b/web/client/components/map/cesium/plugins/WFSLayer.js
index d858f2d52a..dcf016c3ba 100644
--- a/web/client/components/map/cesium/plugins/WFSLayer.js
+++ b/web/client/components/map/cesium/plugins/WFSLayer.js
@@ -32,45 +32,54 @@ const requestFeatures = (options, params, cancelToken) => {
const createLayer = (options, map) => {
- let dataSource = new Cesium.GeoJsonDataSource(options?.id);
+ if (!options.visibility) {
+ return {
+ detached: true,
+ dataSource: undefined,
+ remove: () => {}
+ };
+ }
+ let dataSource = new Cesium.GeoJsonDataSource(options?.id);
+ dataSource.loadingEvent.addEventListener(() => {
+ // ensure it updates render on every loading
+ map.scene.requestRender();
+ });
const params = optionsToVendorParams(options);
const cancelToken = axios.CancelToken;
const source = cancelToken.source();
-
- if (options.visibility) {
- requestFeatures(options, params, source.token)
- .then(({ data: collection }) => {
- dataSource.load(collection, {
- // ensure default style is not applied
- stroke: new Cesium.Color(0, 0, 0, 0),
- fill: new Cesium.Color(0, 0, 0, 0),
- markerColor: new Cesium.Color(0, 0, 0, 0),
- strokeWidth: 0,
- markerSize: 0
- }).then(() => {
- map.dataSources.add(dataSource);
- dataSource['@wfsFeatureCollection'] = collection;
- layerToGeoStylerStyle(options)
- .then((style) => {
- getStyle(applyDefaultStyleToVectorLayer({ ...options, style }), 'cesium')
- .then((styleFunc) => {
- if (styleFunc) {
- styleFunc({
- entities: dataSource?.entities?.values,
- map,
- opacity: options.opacity ?? 1,
- features: collection.features
- }).then(() => {
- map.scene.requestRender();
- });
- }
- });
- });
- });
+ requestFeatures(options, params, source.token)
+ .then(({ data: collection }) => {
+ dataSource.load(collection, {
+ // ensure default style is not applied
+ stroke: new Cesium.Color(0, 0, 0, 0),
+ fill: new Cesium.Color(0, 0, 0, 0),
+ markerColor: new Cesium.Color(0, 0, 0, 0),
+ strokeWidth: 0,
+ markerSize: 0
+ }).then(() => {
+ map.dataSources.add(dataSource);
+ dataSource['@wfsFeatureCollection'] = collection;
+ layerToGeoStylerStyle(options)
+ .then((style) => {
+ getStyle(applyDefaultStyleToVectorLayer({ ...options, style }), 'cesium')
+ .then((styleFunc) => {
+ if (styleFunc) {
+ styleFunc({
+ entities: dataSource?.entities?.values,
+ map,
+ opacity: options.opacity ?? 1,
+ features: collection.features
+ }).then(() => {
+ map.scene.requestRender();
+ });
+ }
+ });
+ });
});
- }
+ });
+
dataSource.show = !!options.visibility;
dataSource.queryable = options.queryable === undefined || options.queryable;
@@ -86,16 +95,14 @@ const createLayer = (options, map) => {
map.dataSources.remove(dataSource);
dataSource = undefined;
}
- },
- setVisible: () => {}
+ }
};
};
Layers.registerType('wfs', {
create: createLayer,
update: (layer, newOptions, oldOptions, map) => {
- if (needsReload(oldOptions, newOptions)
- || newOptions.visibility !== oldOptions.visibility) {
+ if (needsReload(oldOptions, newOptions)) {
return createLayer(newOptions, map);
}
if (layer?.dataSource?.entities?.values