CesiumGS / cesium

An open-source JavaScript library for world-class 3D globes and maps :earth_americas:
https://cesium.com/cesiumjs/
Apache License 2.0
12.73k stars 3.45k forks source link

Camera.computeViewRectangle doesn't work in 2D or CV #4346

Open hpinkos opened 7 years ago

hpinkos commented 7 years ago

Reported on the forum: https://groups.google.com/forum/?hl=en#!topic/cesium-dev/Ildibeod608

var viewer = new Cesium.Viewer('cesiumContainer', {
    sceneMode: Cesium.SceneMode.SCENE2D
});

viewer.scene.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(-90, 43, 50000),
    duration: 0,
});

viewer.camera.moveEnd.addEventListener(logViewRectangle);

function logViewRectangle() {
    console.log('camera.computeViewRectangle() is ' + JSON.stringify(viewer.scene.camera.computeViewRectangle()));
}
jhalbroo commented 7 years ago

I've also encountered this issue. It is important to the use case of maintaining the user's view after a change in dimension. I implemented a solution as described here to capture the view before a morph and reset it on morph complete, and it works when transitioning from 3D but not when transitioning from 2D or Columbus. I think if this issue is fixed, it would work for those as well.

esraerik commented 7 years ago

Hi @hpinkos @jhalbroo I'm using compute view rectangle very well in 2d .You did't give ellipsoid parameter in computeview rectangle. Maybe this cause returning undefined.

rahwang commented 7 years ago

Came up again on the forum: https://groups.google.com/d/msg/cesium-dev/oWnstR1geGY/WkxhyZ79DQAJ

mramato commented 7 years ago

This came up again in an offline conversation I had with a user.

@tfili didn't this used to work?

tfili commented 7 years ago

didn't this used to work?

I'm not sure.

hpinkos commented 7 years ago

Nope, this never worked. Just tested it in 1.19 when this function was added

thw0rted commented 7 years ago

Hey all, it looks like this has been around for a while. My issue is, I need to know the lat/lon bounding box of where the viewer is looking so I can dynamically load in entities. computeViewRectangle seemed like the obvious way to do this, but if it's not going to work in 2D view, I'm going to need another solution. Is this issue being worked, so I just have to stall until another release or two, or is there some way I can get a good bounding box right now? I think I could also work with a lat/lon point under the camera (camera.positionWC always works) plus an approximate radius (in KM), if the box isn't possible.

zbennett10 commented 7 years ago

I'm wondering about this issue as well!

thw0rted commented 7 years ago

The more I play with it, I'd really like a polygon (arbitrary number of sides) representing the visible surface, in world coordinates. The subject function more or less works in 3D but gives some strange results when you have the camera at low/oblique angles, or rotated at off-cardinal headings. I think in 2D I might be able to work this out -- assuming pickEllipsoid works, I could grab the lat/lon of each corner of the viewport and construct a poly -- but in 3D things get complicated if the horizon is in view.

Maybe somebody with a little more experience could put together a quick Sandcastle demo of drawing a polygon to fit the current camera view, on camera.changed? Otherwise, I might work on it later today if I have some time.

hpinkos commented 7 years ago

Hi @thw0rted. I'm not sure if this is something we're going to get a chance to look into in the next few months, but it is labeled priority because a number of other users have run into this issue. As a workaround, you can try calling camera.pickEllipsoid for coordinates at the corners of the screen and use that to create a Rectangle.

However, for dynamically loading entities, I think using the 3D tiles format will ultimately solve your problem. We're still working on the vector format, but you can learn more about it here: https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/vector-tiles/TileFormats/VectorData

thw0rted commented 7 years ago

I ended up playing with this and camera.pickEllipsoid definitely helped me make a Polygon (Rectangle would only work in 2D). It's not perfect, because if the horizon is visible then the top corners can't be computed (you're trying to pick a pixel in space :D) but it's a start.

If it helps anybody, it looks a little something like this:

var pixWidth = viewer.scene.canvas.clientWidth;
var pixHeight = viewer.scene.canvas.clientHeight;
var topRight = viewer.scene.camera.pickEllipsoid(new Cesium.Cartesian2(pixWidth - 1, 1));
var topLeft = viewer.scene.camera.pickEllipsoid(new Cesium.Cartesian2(1, 1));
var bottomLeft = viewer.scene.camera.pickEllipsoid(new Cesium.Cartesian2(1 , pixHeight - 1));
var bottomRight = viewer.scene.camera.pickEllipsoid(new Cesium.Cartesian2(pixWidth - 1, pixHeight -1));
if(!topRight || !topLeft || !bottomLeft || !bottomRight){
    console.warn("Failed to find viewer edge coordinates",
        [topLeft, bottomLeft, bottomRight, topRight]);
    return;
}
return [topLeft, bottomLeft, bottomRight, topRight, topLeft];

At some point I may try to get fancy and figure out where the horizon intersects the edge of the screen and use that as my top corner(s), to handle my current problematic edge case.

hacknaman commented 7 years ago

Any update on this bug? or a better work around?

ggetz commented 6 years ago

Reported again on the forum: https://groups.google.com/forum/#!topic/cesium-dev/vq777-nhFuI

mlui00 commented 5 years ago

I've used this. (Cesium.Rectangle)

var rect = viewer.camera.computeViewRectangle(viewer.scene.globe.ellipsoid, scratchRectangle);

if(rect == undefined)
{
    var cl2 = new Cesium.Cartesian2(0, 0);
    var leftTop = viewer.scene.camera.pickEllipsoid(cl2, ellipsoid);

    var cr2 = new Cesium.Cartesian2(viewer.scene.canvas.width, viewer.scene.canvas.height);
    var rightDown = viewer.scene.camera.pickEllipsoid(cr2, ellipsoid);

    leftTop = ellipsoid.cartesianToCartographic(leftTop);
    rightDown = ellipsoid.cartesianToCartographic(rightDown);
    rect = new Cesium.Rectangle(leftTop.longitude, rightDown.latitude, rightDown.longitude, leftTop.latitude);
}
thw0rted commented 5 years ago

Be aware, you can wind up with a corner that's off the ellipsoid (i.e. out in space) -- your code would throw because I think in that case pickEllipsoid returns undefined.

onderaltintas commented 5 years ago

Seconding that, in camera.js culling sphere and bounding volume visibility test comes as -1. Can be cause of test is using maximum radius. It happens even though you give the right ellipsoid.

The bug disappears when you remove if(visibility === Intersect.OUTSIDE)... check but that can cause other problems in the future that I don't know >.<.

thw0rted commented 5 years ago

Hey @onderaltintas, your observation could mean that the root cause of this issue is #7917, which I just reported. If computeVisibility worked reliably in 2D/CV, maybe that would fix computeViewRectangle?

Do you have any ideas why computeVisibilty falls apart? If you look at my Sandcastle example in that issue, I think it's interesting that computeVisibility actually works in 2D when zoomed out, and only stops working once you zoom in a bit.

Maarondesigns commented 4 years ago

Any word on this issue? I am looking for a solution so I can limit the 2d map from panning outside the edges of the map into blackness. Also, computeViewRectangle doesn't work if the corners of the screen are outside of the 3D globe, however I can't reliable figure out how much of the screen space the earth takes up using the camera height because it is vastly different depending on the devicePixelRatio. Any suggestion would be greatly appreciated :)

thw0rted commented 4 years ago

You can see in my workaround a few posts back that I use camera.pickEllipsoid to find a Cartesian3 on the surface based on screen-space (pixel) coordinates. I use the edges of the canvas, but you could use some fractional offset to determine if, say, more than 20% of the screen on one side is empty space. viewer.scene.camera.pickEllipsoid(new Cesium.Cartesian2(pixWidth * 0.2, pixHeight * 0.2)) would give you a Cartesian3 of a point 20% of the way in from the left side of the canvas, and 20% down from the top. If you get an undefined result, it means that you've zoomed out enough that this point is out in space, not over the globe.

electricsam commented 4 years ago

I just ran into this issue as well. Are there any updates on a fix?

cavencj commented 4 years ago

I also get issue now , when setting the view bounds in 2D, the method return the value 'undefinde'

mrbankim commented 3 years ago

Hello, Can anyone suggest me how can I get the coordinate Array from two points (Cartographic)? Basically I need all the edge coordinate of canvas.

projeto-arcanjo commented 3 years ago

In my case the computeViewRectangle simply stops to update values when I navigate near to Long -78 ( Panama ) at a certain zoom level ( camera at 2717175m. If I zoom out I can take the updates again. Im only in 2D mode.


    var rect = viewer.camera.computeViewRectangle( viewer.scene.globe.ellipsoid, scratchRectangle );
    if( Cesium.defined(rect) ){
        var bWest = Cesium.Math.toDegrees(rect.west);
        var bSouth = Cesium.Math.toDegrees(rect.south);
        var bEast = Cesium.Math.toDegrees(rect.east);
        var bNorth = Cesium.Math.toDegrees(rect.north);

        globalScreenViewport.bWest = bWest;
        globalScreenViewport.bSouth = bSouth;
        globalScreenViewport.bEast = bEast;
        globalScreenViewport.bNorth = bNorth;

        $("#vpW").text( bWest );
        $("#vpE").text( bEast );
        $("#vpN").text( bNorth );
        $("#vpS").text( bSouth );
    }