Move Camera East/West while focused on entity

Hello,

I’m having trouble finding the proper terms to search for to solve this particular problem, so apologies if this has been answered before.

I’m able to use the Camera tutorial (Cesium Sandcastle) to implement keybinds that pan the camera. What I’d like to do is alter these to always go North/South/East/West regardless of camera orientation or focus. Is this possible? The closest I’ve been able to achieve is still relative to the object in focus and not the Earth.

If you want to move camera on the orthogonal coordinate system, you can simply add camera position with a vector. Here is one way to do it:

camera.position = Cesium.Cartesian3.add(camera.position, new Cesium.Cartesian3(0, 0, moveRate), camera.position);

I modified the “right” flag in the example you posted in the sandcastle example below. You may scroll down to the very bottom of the code snippet to see where I changed. I did not bother to find the exact vector for “right”, but you can definetly use whatever 3D vector you want to replace the
new Cesium.Cartesian3(0, 0, moveRate)
And you will be able to see that pressing d always moves the camera to the same direction (in the example the direction would be (0, 0, 1)), regardless the facing of the camera.

Now, if I understand the title correctly, you also want to keep the camera looking at an entity while moving on arbitrary directions. This is a bit more work; as far as I know, there is no simple build-in method. The build-in lookAt take a center and an offset vector, and does not really allow you to move the camera with arbitrary postion directly at the same time; but you may use the following function as I did:

  // Author: Omar
  function lookAt(camera, point) {
    let direction = Cesium.Cartesian3.subtract(
      point,
      camera.position,
      new Cesium.Cartesian3()
    );
    direction = Cesium.Cartesian3.normalize(direction, direction);
    camera.direction = direction;

    // get an "approximate" up vector, which in this case we want to be something like the geodetic surface normal.
    const approxUp = Cesium.Cartesian3.normalize(
      camera.position,
      new Cesium.Cartesian3()
    );

    // cross viewdir with approxUp to get a right normal
    let right = Cesium.Cartesian3.cross(
      direction,
      approxUp,
      new Cesium.Cartesian3()
    );

    Cesium.Cartesian3.normalize(right, right);
    camera.right = right;

    // cross right with view dir to get an orthonormal up
    const up = Cesium.Cartesian3.cross(
      right,
      direction,
      new Cesium.Cartesian3()
    );
    camera.up = up;
  }

I am not the original author of the code above; the original author is @omar, and you can find the original discussion here.

The code is very simple and straight forward if you have some basic graphic knowledge; one thing you might need to consider is that if your camera direction is very close to parallel to the “approxUp” vector (namely when the entity, the camera and the earth are on the same line), which I expect to be a rare situation and should simply be taken care by halting the function. Another thing is that since you are already moving on orthogonal directions, you probably do not care about the earth postion at all. In which case you may need to replace the “approxUp” vector with some fixed “universalUp” vector. I did not actually try this though. With thses being taken care of, you should hopefully get something similar to the lookAt() function of three.js.

If you do not have a point from the entity you want to focus, here is a solution. You can first get its bounding sphere with following code:

let b = new Cesium.BoundingSphere()
viewer.dataSourceDisplay.getBoundingSphere(entity, false, b)

Notice this getBoundingSphere function is not well documented since it was initially a private function; it may not write the bounding sphere to the object you passed in right away, and neither does it return a promise. I’ve suffered through this and you may look at this.

Now you have the bounding sphere, the center point is just b.center, passing this center point (or whatever Cartesian3 point you want to focus on) and your camera to your version of lookAt() function should set the camera to look at the desired point. All you need to do is just call this function every time after the camera is moved to a new position.

However, with the method above, if the step size for translation is relatively large, you would see a lot of glitch when the camera is turning. If you want a smooth turning, you might want to take a look at eventListeners that trigger on every frame, for example, Cesium.Clock.onTick event.

I appreciate the info! That sandcastle example is an excellent primer for me to start working with.

I don’t want to necessarily keep the object focused, but what I’d like to do is move East/West relative to the globe. So I’d like to be able to zoom on an object and then move East or West on keypress regardless of current camera orientation, which your example shows is definitely possible.

I think I’m making progress on this. I’m attempting to use:

  const cameraPosLLA = camera.positionCartographic;
  if (flags.moveRight) {
      camera.position = Cesium.Cartesian3.fromRadians(cameraPosLLA.longitude+0.01,cameraPosLLA.latitude, cameraPosLLA.height);
  }

This is working relatively well in sandcastle to produce the effect I was hoping for. Going to try to integrate it into our project. Thanks @ Xiaoyang_Guo for your help!!

Great! I did undersand you question wrong. I just started my journey in GIS field recently so I haven’t got use to the Cartographic system yet, as a result I was thinking about moving along an axis in the orthogonal coordinate system. When I saw “North/South/East/West ”, my brain kind of converted them to “x/y/z directions” autometically XD. Anyway, glad my mislanded answer helped you : )