WMS layergroup issue

We are trying to use a WMS layer where the map layer changes at certain zoom levels. For example we have a single WMS layer from ArcMap that is configured to show layer GNC from scale 1:10,000,000 - 1:2,000,000 and show layer JNC from scale 1:2,000,000 - 1:1. The bounds/coverage of both layers are the same. We have the same problem if we use a GeoServer Layer Group with an SLD/Style to set the minzoom/maxzoom on the layers of the layer group.

As we zoom-in and zoom-out around the 1:2,000,000 scale where the maps are supposed to change, we often get left with artifacts of the “wrong” layer on the screen. We have been poking around ImageryLayer.prototype._createTileImagerySkeletons and lowering the “var errorRatio” seems to help but we haven’t been able to totally fix it. If we lower the errorRatio to 0.5, then most of the time we only see our problem is if we slowly zoom out from around 1:1,800,000 to 1:2,200,000. It seems to be requesting imageryLevels that jump around quite a bit even with slow zooming out (from 9 to 6 to 4 to 5).

Any ideas on where to look to resolve this issue? Thanks!!!

Hi Ashley,

I’d argue that the concept of a “map scale” doesn’t make a whole lot of sense on a 3D globe. In particular, with the camera tipped toward the horizon, different parts of the map can (and will be) drawn at very different scales.

Instead, Cesium chooses which zoom level to use based on an estimate of the “screen-space error” that would result by using a particular tile. You can see that computation in CentralBodySurface.js in the screenSpaceError function. The screen-space error is a function of the distance from the camera to the closest point on the tile, but we don’t know the closest point on the tile precisely. Instead, we compute the distance to planes approximating the horizontal boundaries of the tile, as well as the distance to a curved surface at the tile’s maximum height. The end result is that it’s somewhat common for tiles in the center of the viewport to be rendered at higher detail than those near the edges, because the camera is closer to the ones in the center. If that’s what you’re seeing, I’m not sure what we can do to improve the situation.

I’m concerned that you see the zoom level jumping around as you move the camera, though. Can you elaborate on that?

Kevin

Ok that makes sense. Thanks for the explanation.

When I talk about the zoom level jumping around…first I zoom in to our layer which is at level 9 and a bunch of tiles load. Then I pan the map towards the west slightly a few times and I get tile requests from levels 5,6,7,9, etc which seems a bit strange to me. The “imagery level” logs come from a console.log that I inserted in _createTileImagerySkeletons.

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9

NOW PAN AROUND

Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 7 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 5 Cesium.js:73265

Imagery level: 5 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 6 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

Imagery level: 9 Cesium.js:73265

I see what you mean, now. That’s intentional, though not ideal.

Cesium, currently, always refines tiles from the root toward the leaves. In other words, it will never download a tile at level 9 until its parent tile at level 8 is already downloaded. This is true even when those ancestor tiles will not be displayed anymore once the more detailed tiles are downloaded. This strategy makes a number of things simpler, but it’s definitely a weakness in Cesium’s terrain and imagery rendering that will (eventually) be addressed.

Kevin

Thanks for getting back to us Kevin. This makes a lot of sense, and we realize it is by design. Other than telling our customer to use 2D mode for this use case which doesn’t have this problem, can you recommend any type of quick band-aid for us? We would just like to have all tiles use the center scale/zoom level if the globe is being viewed from straight on and not tilted. I am going to include another screenshot of what we are seeing where the center tiles are pulling imagery tiles of the higher zoom level than the tiles around it.

Hi Jonah,

I don’t know of a quick band-aid. If you’re willing to try something a little less quick, you might try this…

First, you should probably make sure you have a (nearly) top-down view. Otherwise a horizon view will cause the algorithm to follow to try to render an absurd number of tiles. To do that, I’d probably take the dot product of the ellipsoid surface normal under the camera’s position with the inverse of the camera’s view direction. If that’s close to 1 (you’ll have to try it out to see how close it needs to be), then you’re in a top-down view. If not, use the existing screen-space error computation.

For horizon views, you’ll use a different method of computing the distance that goes into the screen-space error computation. Instead of using the actual (estimated) distance to the tile, you’ll use the camera height as the distance. The rest of the computation should be the same.

I haven’t tried this, but I think it will work reasonably well, especially if your surface is just the ellipsoid (no terrain). I can foresee some problems when terrain data is in use because the terrain data can make the surface much closer to the camera than the camera height would make it appear.

If you try this out, let me know how it goes.

Kevin

Thanks a lot for the suggestion, Kevin. I was able to use

0.2 > camera.direction.add(camera.position.normalize()).magnitude()

to check if the camera was not tilted so much to only do this when in top down view. Then I played around with the screenspace error until I got the tiles rendering how we needed.

Sorry for all of the deleted posts. I could not get an image to attach without showing up in the quoted text for anything.

Hey Jonah,

I’d be interested in what you did in the end? I am doing similar stuff and also realise that rendering every tile is by design, but would prefer them not to…

Thanks

Toby

Sorry for the late response Toby, but this basically solved my problem like Kevin had suggested. Again, the problem for us was that we wanted to render imagery tiles all at the same zoom level when the camera was not tilted.

In Scene/CentralBodySurface.js

In the function…

function screenSpaceError(surface, context, frameState, cameraPosition, cameraPositionCartographic, tile) {

I put this before the return

if (0.2 > camera.direction.add(camera.position.normalize()).magnitude()){ // if there is minimal camera tilt, make this more like the 2D screen space error

distance = cameraPositionCartographic.height;

}

I had to do some extra work because we show a scale value based on the center map pixel size, and I wanted the correct imagery level to switch based more closely on that value.