adjust camera to look in the direction of the movement of a moving entity

Hi,
I just started developing with cesium. I fiddled around with a lot of examples and tutorials, but can't find a solution for my problem. The interpolation sandcastle example I used as a starting point. My entity also follows a given path (position list with time values), just like computeCirclularFlight in the example do. Now I want to adjust the camera to stay behind the moving entity. To demonstrate it just replace the "Sandcastle.addToolbarButton('View Aircraft', function() " function in the sandcastle demo (http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Interpolation.html&label=Showcases) with the following code snippet:

Sandcastle.addToolbarButton('View Aircraft', function() {
    viewer.trackedEntity = undefined;
    viewer.clock.onTick.addEventListener(function (clock) {
        var direction = entity.orientation.getValue(clock.currentTime);
        var angle = Cesium.Quaternion.computeAngle(direction);
        var pitch = -Cesium.Math.toRadians(10.0);
        var range = Cesium.Cartesian3.magnitude(new Cesium.Cartesian3(30.0, 0.0, 10.0));
        viewer.scene.camera.lookAt(entity.position.getValue(clock.currentTime), new Cesium.HeadingPitchRange(angle, pitch, range));
    });
});

Unfortunately the camera doesn't stay behind the aircraft. Is the entity.orientation approach right or is there any other property to get the movement direction of an entity?

Can anyone help me with this problem?

Thanks a lot, Matthias

Adding in some debugging logs
Sandcastle.addToolbarButton(‘View Aircraft’, function() {

viewer.trackedEntity = undefined;

viewer.clock.onTick.addEventListener(function (clock) {

var direction = entity.orientation.getValue(clock.currentTime);

//console.log("d "+direction);

var angle = Cesium.Quaternion.computeAngle(direction);

console.log("a "+angle/Math.PI*180);

var pitch = -Cesium.Math.toRadians(10.0);

var range = Cesium.Cartesian3.magnitude(new Cesium.Cartesian3(30.0, 0.0, 10.0));

var offset = new Cesium.HeadingPitchRange(angle, pitch, range);

console.log("h "+offset.heading/Math.PI*180);

//console.log("p "+offset.pitch/Math.PI*180);

//console.log("r "+offset.range);

viewer.scene.camera.lookAt(entity.position.getValue(clock.currentTime), offset);

console.log("ch "+viewer.scene.camera.heading/Math.PI*180);

});

});

``

It is heading in the direction you computed, but I don’t think you got the proper heading from the Quaternion.

I think someone made a pull request to put those wiki equations into Cesium, but I don’t think it’s in master yet.

Take a look at https://github.com/AnalyticalGraphicsInc/cesium/blob/1.9/Source/DataSources/VelocityOrientationProperty.js#L147

What it does is take 2 points that are close together in time using property.getValue then subtracts them to obtain a velocity vector, the normalizes to get just the direction of travel. From that vector you can get heading.

(deleted last message as I’ve improved the sandcastle program)

Add this button to http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Interpolation.html&label=Showcases
This is still using lookAt function which messes with the camera’s transform.

//Add button to View Aircraft at a Fixed Angle relative to aircraft

Sandcastle.addToolbarButton(‘View Aircraft Fixed Angle’, function() {

viewer.trackedEntity = undefined;

viewer.clock.onTick.addEventListener(function (clock) {

//get 2 positions close together timewise

var CC3=Cesium.Cartesian3;

var position1 = entity.position.getValue(clock.currentTime, new CC3());

var position2 = entity.position.getValue(Cesium.JulianDate.addSeconds(clock.currentTime, 1/60, new Cesium.JulianDate()), new CC3());

//velocity in terms of Earth Fixed

var Wvelocity = CC3.subtract(position2, position1, new CC3());

CC3.normalize(Wvelocity, Wvelocity);

var Wup = new CC3();var Weast = new CC3();var Wnorth = new CC3();

Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(position1, Wup);

CC3.cross({x:0,y:0,z:1},Wup,Weast);

CC3.cross(Wup,Weast,Wnorth);

//velocity in terms of local ENU

var Lvelocity=new CC3();

Lvelocity.x=CC3.dot(Wvelocity,Weast);

Lvelocity.y=CC3.dot(Wvelocity,Wnorth);

Lvelocity.z=CC3.dot(Wvelocity,Wup);

//angle of travel

var Lup = new CC3(0,0,1);var Least = new CC3(1,0,0);var Lnorth = new CC3(0,1,0);

var x = CC3.dot(Lvelocity,Least);

var y = CC3.dot(Lvelocity,Lnorth);

var z = CC3.dot(Lvelocity,Lup);

var angle = Math.atan2(x,y);//math: y b4 x, heading: x b4 y

var pitch = Math.asin(z);//make sure Lvelocity is unitized

//angles offsets

angle+=0/180*Math.PI;

pitch+=-20/180*Math.PI;

var range = 80;

var offset = new Cesium.HeadingPitchRange(angle, pitch, range);

viewer.scene.camera.lookAt(entity.position.getValue(clock.currentTime), offset);

}); //end event listener

}); //end button

``

I could modify this to not use lookAt possibly making it simpler.

Sometimes it crashes at the end of the route, to fix if position2 is undefined you can get a position backward in time, subtract, then negate.

(deleted previous post to update SandCastle program to a new version)

With this when you’re looking at the airplane from the side and the plane pitches your view will roll with the pitch, it’s like having the airplane mounted with a GoPro camera!
//Add button to View Aircraft at a Fixed Angle relative to aircraft

Sandcastle.addToolbarButton(‘View Aircraft w/Aircraft transform’, function() {

viewer.trackedEntity = undefined;

viewer.clock.onTick.addEventListener(function (clock) {

//get 2 positions close together timewise

var CC3=Cesium.Cartesian3;

var position1 = entity.position.getValue(clock.currentTime, new CC3());

var position2 = entity.position.getValue(Cesium.JulianDate.addSeconds(clock.currentTime, 1/600, new Cesium.JulianDate()), new CC3());

//velocity in terms of Earth Fixed

var Wvelocity = CC3.subtract(position2, position1, new CC3());

CC3.normalize(Wvelocity, Wvelocity);

//ENU in terms of Earth Fixed

var Wup = new CC3();var Weast = new CC3();var Wnorth = new CC3();

Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(position1, Wup);

CC3.cross({x:0,y:0,z:1},Wup,Weast);

CC3.cross(Wup,Weast,Wnorth);

//aircraft transform in terms of Earth Fixed

var Aright = new CC3();var Adir = new CC3();var Aup = new CC3();

Adir = CC3.clone(Wvelocity);

CC3.cross(Wvelocity,Wup,Aright);

CC3.cross(Aright,Adir,Aup);

var transform;

/* this approximation causes drifting, so just tap into private primitives

transform = new Cesium.Matrix4

(

Aright.x,Adir.x,Aup.x,position1.x,

Aright.y,Adir.y,Aup.y,position1.y,

Aright.z,Adir.z,Aup.z,position1.z,

0,0,0,1

);

*/

transform = viewer.scene.primitives._primitives[0].modelMatrix.clone();

//transform set

var offset = viewer.scene.camera.position.clone();

viewer.scene.camera.lookAtTransform(transform,offset);

}); //end event listener

}); //end button

``

Your "View Aircraft Fixed Angle" button code works like a charm! Also your hint to the VelocityOrientationProperty.getValue function is very interesting! Thanks a lot for your help!

I have used this code and got a working tacker running.

I added a simply switch to enable and disable the tracking, but after viewer.scene.camera.lookAt has been used, the camera cannot be moved with mouse input anymore.

in the callback I have done

if (trackCar) {

… your code
}

``

and with button click i toggle trackCar.

Any suggestion to restoring the camera to default user inputs such the user can move the map with the mouse again.

I believe viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); sets the camera to a sort of default orientation.
Not sure if it will help but maybe worth a try.

Willem

Thanks. That worked .

Hi Hyper Sonic,

I tried to copy your code to the Cesium Sandcastle. The new button 'View Aircraft w/Aircraft transform' is added, but when I clicked on it, I got this runtime error:

An error occurred while rendering. Rendering has stopped.
undefined
TypeError: Cannot read property 'clone' of undefined
TypeError: Cannot read property 'clone' of undefined
    at <anonymous>:171:64
    at Event.raiseEvent (https://cesiumjs.org/Cesium/Build/CesiumUnminified/Cesium.js:3113:30)
    at Clock.tick (https://cesiumjs.org/Cesium/Build/CesiumUnminified/Cesium.js:45308:21)
    at CesiumWidget.render (https://cesiumjs.org/Cesium/Build/CesiumUnminified/Cesium.js:251951:43)
    at render (https://cesiumjs.org/Cesium/Build/CesiumUnminified/Cesium.js:251333:32)

Please help!
Thanks!

I think the easiest way is to use trackedEntity, but this thread also has a Sandcastle example of computing a direction and having the camera look at that direction, through the Entity:

https://groups.google.com/d/msg/cesium-dev/5lHlHWa4aJA/-aoZmaqBBQAJ