Differences between Cartographic.height and globe.getHeight()

I have a MOUSE_MOVE event handling function written to get the lat, lon, alt of where the cursor is on the screen.

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

handler.setInputAction(function (event) {
  let cartesian = viewer.scene.pickPosition(event.endPosition);  

  if (cartesian) {
    let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
    let longitude = Cesium.Math.toDegrees(cartographic.longitude);
    let latitude = Cesium.Math.toDegrees(cartographic.latitude);
    let altitude = cartographic.height;
    let terrainHeight = viewer.scene.globe.getHeight(cartographic);
    let heightAboveTerrain = altitude - terrainHeight;

    console.log(`latitude: ${latitude},\nlongitude: ${longitude},\naltitude: ${altitude},\nterrain height: ${terrainHeight},\nheight above terrain: ${heightAboveTerrain}`);
  } else {
    console.log('no cartesian')
  }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

My question is about the difference in values between cartographic.height and viewer.scene.globe.getHeight(), when I have a terrain provider enabled or not.

No terrain provider:

Terrain provider enabled:

My understanding is there should be a difference between the height provided by cartographic.height (which should be the WGS84 ellipsoid height) and the globe.getHeight(), (which should be the height from the terrain provider); if this is incorrect please let me know.

My question is why do both height and getHeight values change when a terrain provider is enabled, and why is there a subtle difference between them?

Hi @newton67401,
The cartographic returned by Scene.pickPosition is computed by inverse projecting the coordinates of the pixel at the cursor position. The coordinates of that pixel include 3 values:

  1. The horizontal position on the screen (pixels from left edge of canvas)
  2. The vertical position on the screen (pixels from top edge of canvas)
  3. The “depth” of the scene, i.e., the distance from the Camera to the rendered mesh.

These three values are converted to physical meters and then inverse projected to be a geospatially meaningful value.

Note that the 3rd coordinate, the “depth” of the scene, will include the effects of the terrain provider, if it is enabled. This is why your two computed heights are very similar–within 3cm!

As for why they are not exactly equal: the getHeight call is taking the longitude and latitude from the pickPosition result, and then discarding the height value, and re-computing it by looking up the value from the terrain tile at that longitude and latitude.

For a given longitude,latitude pair, the getHeight method should theoretically return a slightly more accurate elevation. This is because the terrain tile has the original full-resolution height data, whereas the Scene.pickPosition call is depending on the depth values stored on the GPU, and the GPU depth buffer has limited precision.

However, keep in mind that if your camera is tilted to view the terrain at an angle, then the accuracy of the longitude,latitude values from Scene.pickPosition is already affected by that low-precision depth buffer… because in that case the longitude and latitude calculation has to take the depth buffer value into consideration.

To keep it all in perspective: the pixel under the cursor, in this case, looks like at least 50cm wide. So with +/- 25cm uncertainty in the longitude and latitude, only 3cm error in the height is pretty good!