5

I am working withe Cesium 3D at the moment and want to determine what the equivalent map scale the globe is being viewed at.

I am able to get the viewing altitude in Meters that he Camera is at, but according to this discussion, I would need the eye distance from the surface as well, and I do not understand this.

My ultimate aim is to determine a map scale, convert that to an Openlayers 2 Zoom Level so I can sync the two views (approximately)

6

Update, March 2016: Cesium 1.19 has been released with a new function, Camera.computeViewRectangle, that does as good a job as can be done computing the view rectangle, given the caveats below. I now recommend using it instead of sniffing the imagery tiles as shown here. I'm keeping the original answer below because it explains the problems of computing a simple view rectangle on a 3D view of the globe.

Original answer: There's no easy mapping from a 3D globe to a 2D zoom level. On a round globe, different tiles are at different distances from the 3D camera: Some tiles are draped over the horizon, or viewed at steep angles. The camera may be down low near a point on the ground, but looking out towards the horizon, so it can see high-level tiles in the foreground and low-level ones in the background.

You can see this in action, graphically, using the Cesium Inspector.

  1. Click the tiny + sign next to + Terrain at the bottom of Cesium Inspector.
  2. Next, put a checkmark on Show tile coordinates.
  3. Zoom the camera around and watch the tiles change.

For an even deeper understanding, do the above an then continue:

  1. Zoom the camera close to a city or spot on the ground.
  2. Use middle-drag (or ctrl-left-drag) to tilt the view towards the horizon.
  3. Put a checkmark on Suspend LOD Update.
  4. Straighten out the camera and back up a little bit, to see tiles floating in space. Note the ones that were near the camera (when Level-of-Detail was suspended) are small and high-level, but the ones that were visible in the distance are large and low-level.

Now that you've seen that several different tile levels contribute to a single camera view, I'll share some highly un-official code. The Cesium Inspector is making use of some private API points to snoop on the tiles being loaded, and since this is open-source, you can snoop on those tiles too. Using private APIs is not supported and can break in any future release of Cesium. Ready? Open up a copy of Cesium Sandcastle and paste in the following code:

// This code reports the min/max values of ANY tiles being considered for rendering.
// The values will change asynchronously as the server sends higher-level tiles.
var viewer = new Cesium.Viewer('cesiumContainer');
var west, south, east, north;
var toolbar = document.getElementById('toolbar');

viewer.clock.onTick.addEventListener(function () {
    west = south = 999;
    east = north = -999;

    // CAUTION: Accessing _private variables is not officially supported and
    //          the API can break at any time without warning.
    var tilesToRender = viewer.scene.globe._surface.tileProvider._tilesToRenderByTextureCount;

    if (Cesium.defined(tilesToRender)) {
        var numArrays = tilesToRender.length;
        for (var j = 0; j < numArrays; ++j) {
            var quadtrees = tilesToRender[j];
            if (Cesium.defined(quadtrees)) {
                var numTrees = quadtrees.length;
                for (var i = 0; i < numTrees; ++i) {
                    var rectangle = quadtrees[i].rectangle;
                    west = Math.min(west, rectangle.west);
                    south = Math.min(south, rectangle.south);
                    east = Math.max(east, rectangle.east);
                    north = Math.max(north, rectangle.north);
                }
            }
        }
    }

    if (west > 900) {
        toolbar.innerHTML = 'Location not known.';
    } else {
        toolbar.innerHTML =
            'West: ' + Cesium.Math.toDegrees(west).toFixed(4) + '<br/>' +
            'South: ' + Cesium.Math.toDegrees(south).toFixed(4) + '<br/>' +
            'East: ' + Cesium.Math.toDegrees(east).toFixed(4) + '<br/>' +
            'North: ' + Cesium.Math.toDegrees(north).toFixed(4);
    }
});

If you move the camera around in this demo, you can spot the problems with this method. When the camera is far enough from the Earth, the reported coordinates are the entire planet, but as you get close to a particular location, the reported coordinates narrow down to just some tiles around that location. However, if you tilt the camera towards the horizon, then the reported coordinates include tiles stretching out beyond that horizon, and the camera ends up near one of the sides or corners of those coordinates, not the center. Still, there may be applications where it is handy to have a rough idea of what tiles are visible at all in the 3D scene.

  • Can you give a practical example of using Camera.computeViewRectangle to get the map scale? Just pointing to the manual don't help me too much. – Magno C Jul 22 at 17:01
  • There's no one scale. That's what my (lengthy) original answer tries to explain. It's a 3D camera, the map tiles in the foreground are a different scale than ones in the background. – emackey Jul 22 at 17:47
  • Uh. Understood... and in the plain view? How can I do it? – Magno C Jul 23 at 10:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.