camera.computeViewRectangle not working with terrain enabled

Hello. My company has been using Cesium for about a year now to develop a viewer for Wide Area Motion Imagery (WAMI). Part of our functionality in the viewer is a region of interest query. This feature calculates the geographic area that the user is viewing, and sends it to our master application, so the master application can trim the imagery and only send the portion that the user is viewing. To do accomplish this is cesium, we have been using the camera.computeViewRectangle function to find the range of geographic bounds that the camera has been viewing, and it has worked great. Unfortunately, a couple weeks ago, our company purchased a subscription of Cesium Ion so that we could view 3d terrain in our application. However, enabling terrain made the computeViewRectangle no longer work as expected. Now, the rectangle that this function provides is very incorrect to what we are actually viewing.

Here is the relevant code

  CesiumViewer = new Cesium.Viewer("cesiumContainer", {
    imageryProvider: USGSProvider,
    terrainProvider: Cesium.createWorldTerrain({
         requestWaterMask : true
     }),
    //terrainProvider: arcGisProvider,
    baseLayerPicker: false,
    geocoder: false,
    timeline: false,
    animation: false,
    homeButton: false,
    fullscreenButton: false,
    selectionIndicator: false,
    infoBox: false,
    useDefaultRenderLoop: true,
    orderIndependentTranslucency: true,
    scene3DOnly: true,
    automaticallyTrackDataSourceClocks: false,
    dataSources: null,
    clock: null,
    targetFrameRate: 60,
    resolutionScale: 0.1,
    terrainShadows: Cesium.ShadowMode.ENABLED,
    infoBox: false,
    navigationHelpButton: false,
    contextOptions: {
      webgl: {
        alpha: false,
        antialias: true,
        preserveDrawingBuffer: true,
        failIfMajorPerformanceCaveat: false,
        depth: true,
        stencil: false,
        antialias: false,
      },
    },
  });

  CesiumViewer.camera.moveEnd.addEventListener(function () {
    if (updateRoi) {
      var rect = CesiumViewer.camera.computeViewRectangle();
      var east = String(Cesium.Math.toDegrees(rect.east));
      var south = String(Cesium.Math.toDegrees(rect.south));
      var west = String(Cesium.Math.toDegrees(rect.west));
      var north = String(Cesium.Math.toDegrees(rect.north));
      var coords = south + " " + west + " " + north + " " + east;

      window.CallNativeWithJSON(
        JSON.stringify({ func: "updateROI", args: [coords] })
      );
    }
  });

@Jack_Wickstrom

Thank you very much for sharing this issue with our community! I am currently checking in with the rest of our development team about this question. We should get back to you shortly :rocket:

-Sam

@Jack_Wickstrom

It seems like we have come across a bug in CesiumJS. The computeViewRectangle only takes the WGS84 Ellipsoid into consideration for the calculation. I just opened an issue on GitHub that you can follow. This issue is not currently a part of our CesiumJS roadmap, so I can’t provide a timeline for when it will be resolved. As always, we encourage community contributions and would be happy to review a PR that addresses this bug. It would also be great if you could take some time to add more details to the issue that I created.

In the meantime, it makes sense to begin considering possible workarounds. We have a helper function that does take terrain into account.

This is a private function. Viewer uses this function if you pass the flyTo/ZoomTo methods a rectangle. The geocoder uses it as well. I recommend that you experiment with this helper function. It will likely yield the desired result. Feel free to reach out if you have any additional questions or concerns!

-Sam

Thanks for the reply Sam. After some investigation, I’ve found a functioning workaround:

    if (updateRoi) {

      var topLeftPixel = new Cesium.Cartesian2();
      topLeftPixel.x = 0;
      topLeftPixel.y = 0;
      var topLeftRay = CesiumViewer.camera.getPickRay(topLeftPixel);
      var topLeftCartesian = CesiumViewer.scene.globe.pick(topLeftRay, CesiumViewer.scene);
      var topLeftCarto = Cesium.Cartographic.fromCartesian(topLeftCartesian);
      var north = Cesium.Math.toDegrees(topLeftCarto.latitude); //north
      var west = Cesium.Math.toDegrees(topLeftCarto.longitude); //west


      var canvas = document.getElementById("cesiumContainer");
      var botRightPixel = new Cesium.Cartesian2();
      botRightPixel.x = canvas.clientWidth;
      botRightPixel.y = canvas.clientHeight;
      var botRightRay = CesiumViewer.camera.getPickRay(botRightPixel);
      var botRightCartesian = CesiumViewer.scene.globe.pick(botRightRay, CesiumViewer.scene);
      var botRightCarto = Cesium.Cartographic.fromCartesian(botRightCartesian);
      var south = Cesium.Math.toDegrees(botRightCarto.latitude); //north
      var east = Cesium.Math.toDegrees(botRightCarto.longitude); //west

      var coords = south + " " + west + " " + north + " " + east;

      window.CallNativeWithJSON(
        JSON.stringify({ func: "updateROI", args: [coords] })
      );
    }

@Jack_Wickstrom

Thank you for sharing this with the community! I am happy to see that you have found a robust workaround.

-Sam