Skip to content

Commit

Permalink
[Backport 2023.02.xx] Fix #9775 Visibility limits not working in 3D f…
Browse files Browse the repository at this point in the history
…or detached layers (#9777) (#9812)

* Fix #9775 Visibility limits not working in 3D for detached layers (#9777)

* Fix #9775 Visibility limits not working in 3D for detached layers

* requested changes

* udpate zoom from height comment

* fix failing tests
  • Loading branch information
allyoucanmap authored Dec 13, 2023
1 parent 67ea7b6 commit bf050d7
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className="ms-visibility-limits-form">
<div className="ms-visibility-limits-form-title" style={{ display: 'flex', alignItems: 'center' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
50 changes: 40 additions & 10 deletions web/client/components/map/cesium/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
}
Expand Down
10 changes: 9 additions & 1 deletion web/client/components/map/cesium/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,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) => {
Expand Down
12 changes: 8 additions & 4 deletions web/client/components/map/cesium/__tests__/Layer-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,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(
Expand Down Expand Up @@ -612,7 +613,8 @@ describe('Cesium layer', () => {
position: { x: 13, y: 43 },
onClose: () => {
closed = true;
}
},
visibility: true
};
// create layers
let layer = ReactDOM.render(
Expand All @@ -639,7 +641,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(
Expand All @@ -655,7 +658,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(
Expand Down
2 changes: 1 addition & 1 deletion web/client/components/map/cesium/__tests__/Map-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
17 changes: 10 additions & 7 deletions web/client/components/map/cesium/plugins/MarkerLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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;
}
Expand Down
20 changes: 12 additions & 8 deletions web/client/components/map/cesium/plugins/OverlayLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;
}
Expand Down
3 changes: 1 addition & 2 deletions web/client/components/map/cesium/plugins/TerrainLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ const createLayer = (config, map) => {
terrainProvider,
remove: () => {
map.terrainProvider = new Cesium.EllipsoidTerrainProvider();
},
setVisible: () => {}
}
};
};

Expand Down
110 changes: 52 additions & 58 deletions web/client/components/map/cesium/plugins/ThreeDTilesLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit bf050d7

Please sign in to comment.