Top Rated Plus on Upwork with a 100% Job Success ScoreView on Upwork
retzdev logo
logo
tech

Point CesiumJS Camera in Direction of Entity with "viewFrom" Property

by Jarrett Retz

December 8th, 2023 cesium frontend maps programming web development

Introduction

I ran into a problem while working on a CesiumJS application that ended up having a fairly common solution. However, before I could discover the solution, I spent hours stumbling through unsuccessful attempts.

Problem

I wanted to point the camera at an Entity and in a specific direction using the viewFrom property in the Entity API.

Other times when I've wanted this kind of behavior with the camera I was able to use the lookAt function in the Camera API. The lookAt function takes a HeadingPitchRange object. Unfortunately, the viewFrom property appears to only accept a Cartesian3 object as an offset from the origin Entity.

Anyway, because I already had a function calculating the heading between two points (Entity's), that wasn't a problem. The problem was offsetting the camera from the origin Entity using the heading.

Vector Subtraction

Searching online, and in the Cesium community pages, I found a couple of answers suggesting subtracting the target point from the origin point to give me a vector (Cartesian3 objects have an x, y, and z component) point from the origin to the target.

From there, I could scale the vector, negate the direction, and get a point along the vector on the opposite side of the origin from the target. This new point would become my "camera position".

Having P1 (my origin) and P2 (desired camera position) I could subtract P1 from P2 (P2 - P1) to get me the viewFrom offset.

For reasons that I don't understand—or looked into—this didn't work.

Heading

The Cartesian3 coordinate system works in Cesium using an x and y component, which make up two out of the three components in a Cartesian3 object.

The heading is the "slope" of the line derived from the x and y components but represented in radians. For example, an entity with a heading of 0.785 radians equals 45 deg.

Therefore, if I have the heading of an entity I should be able to represent it using a Cartesian3 (yes, obviously I can hear the smarties saying).

The x and y components that make up the angle are the sine and cosine of the angle. They are the same x and y components we need in the Cartesian3 object used for the entities viewFrom property.

Solution

To place the camera behind an origin point and point it toward a target:

  • Create two Cartesian3 or Cartographic points
  • Calculate the heading from the target point to the origin point (this is the opposite of the direction you want the camera to point)
  • Calculate the sine and cosine of the heading
  • Place the values into a Cartesian3 object with your desired vertical offset (z-component)

Code

// adapted from original https://sandcastle.cesium.com/index.html?#c=zVVdb9owFP0rFi8JUmRCO1UrpdVWxtpKbHSUUVXLHkxiwJpjR7YDpFX/+2xsCAGmlZdpkSrV177nnnvuB3MkwJzgBRbgEjC8AB0sSZ7C0crme/Hq2OFMIcKw8OoXEYvYXHtxQaaEVb06SCj9H2KncCJ4+glPBcbSb54F4J3+Ow3D0AAYd4XEFKs3usP3BgCeBODMQUSs0QB9SyHjhCmNuqIMMVNEESwhShL/JWJAfxmX2sZZy7EO1mbtCFrAvTJfzCkXrQ0hc4K9u5vb4XXvezco32VkiekDecYtcBJu2XmuqBaqcwDm8fZu2N1/+kgSNdMo9uLVPaBojGmFmcJL1QKezdnbpdKfTCTW9y9g2QJhAArDa4NmvjkWisSIWoANt1HFDIf9e0ckYq8boYe2WMcIbet7nNA3g27361O31+s//g9S26z/sdR61DCQM57TxMmmOPCsmh4wYwE828SeHaSECBwbzfUs7c+RzMdKoFj5rh7rATg8dn7d8NhHYVykiOoS+JtoQRm43AmCK2QsX5ASZFkSGgrE5ESDSFh98lnnc+96ZoQpj4kq/DXFSgAHfx6WqBbC7oqBg33yy8sZVHyAEp2B9M/D7dTWnmlOFclo4VdZBTZSsJOPy/PoNaMP+jWyN47BtxwpLJg2VejbQH7ZSlUG1l53uHGhmzrBotK7FLOpafKmXpTb06J4ZrTIpenZ0jzmSunw7ua06pMajgTRP+1DuCBq9pFmM+SH8H39QDePiMxXjVMWc6drB6g42Ljr0lwXD3pkkNjuvFVu4ZE9bOq011k6evX0hhpTIzuuiL4uvJbwx6EYP7dEXdgl1Py7zqsttiuqo/bMeTrk/g7R+kUtqLWlKii+so4fSJpxoUAuqA9hQ+E0ozqabIzz+BdWMJbS4LYba6d2QuaAJJdRbeenP6qBmCIp9c0kp6uFHNWu2g39vuJGuW4mNu3rJUhRYZ7Mmlc9a4QQthv6uO+lOKdjJLYQfwM

var viewer = new Cesium.Viewer('cesiumContainer');

var origin = new Cesium.Cartesian3.fromDegrees(16, 46, 3000);
var target = new Cesium.Cartesian3.fromDegrees(16.8, 46.2, 3000);

// Origin point
var originEntity = viewer.entities.add({
    position: origin,
    point : {
        color: Cesium.Color.LIGHTBLUE,
        pixelSize: 20,
        outlineColor: Cesium.Color.WHITE,
        outlineWidth: 2
    },
    label: {
        text: 'Origin',
        pixelOffset: { x: 0, y: 20 },
        verticalOrigin: Cesium.VerticalOrigin.TOP
    }
});

// Target point
viewer.entities.add({
    position: target,
    point : {
        color: Cesium.Color.GREENYELLOW,
        pixelSize: 20,
        outlineColor: Cesium.Color.WHITE,
        outlineWidth: 2
    },
    label: {
        text: 'Target',
        pixelOffset: { x: 0, y: 20 },
        verticalOrigin: Cesium.VerticalOrigin.TOP
    }
});

// Heading
var cartoOrigin = Cesium.Cartographic.fromCartesian(origin);
var cartoTarget = Cesium.Cartographic.fromCartesian(target);

// https://stackoverflow.com/a/52079217
var heading = Math.atan2(
			Math.sin(cartoOrigin.longitude - cartoTarget.longitude) * Math.cos(cartoOrigin.latitude),
			Math.cos(cartoTarget.latitude) * Math.sin(cartoOrigin.latitude) -
				Math.sin(cartoTarget.latitude) * Math.cos(cartoOrigin.latitude) * Math.cos(cartoOrigin.longitude - cartoTarget.longitude)
		);

originEntity.viewFrom = new Cesium.Cartesian3(Math.sin(heading), Math.cos(heading), 0.1);

viewer.trackedEntity = originEntity;

Conclusion

The camera angle is not perfectly lined up between the origin and the target. Frankly, I don't know why. The solution worked great for my use case and seemed to align the camera precisely.

Below are two links to discussions and code I adapted for the example:

  • https://community.cesium.com/t/how-to-get-heading-pitch-and-roll-from-the-two-points/7243/6
  • https://stackoverflow.com/a/52079217

Thanks for reading!

Jarrett Retz

Jarrett Retz is a freelance web application developer and blogger based out of Spokane, WA.

jarrett@retz.dev

Subscribe to get instant updates

Contact

jarrett@retz.dev

Legal

Any code contained in the articles on this site is released under the MIT license. Copyright 2024. Jarrett Retz Tech Services L.L.C. All Rights Reserved.