Creating a camera flyover effect

Hyper,

Ok I need your help again. I thought everything was great until I started creating more complex paths or changing pitch. Then I realized my move wasn’t working properly. I think this is because it is a factor of the camera.direction. For instance, if I face the camera downward at 90 degrees toward the earth, the camera does not move. I think what I need is to incrementally rotate along the Great Circle between coordinates. This way I can move the camera between points independently of how I set the camera heading, pitch and roll much like a camera on a satellite orbiting the earth. But I just don’t have a handle on all this spatial math yet and my brain is getting tired. Perhaps you can give me another hand here.

I want to move from Point A to Point B where A and B are coordinates on the Earth surface. Along the way I want to be able to control the camera to look left/right, up/down and move it up and down the normal. I think I have a handle on how to implement the camera controls. However, I am unable to figure out how to rotate the camera along the Great Circle incrementally between A and B. As you have seen I have set my increments to be 1 meter or some multiple of that, so I think I need to compute an angle that is equal to an arc of that length as well as what axis on which to rotate. The EllipsoidGeodesic gives me the geodesic I need but I can’t figure out how to rotate along it.

I appreciate a little nudge here.

Thanks!

Joe

I know I said this earlier but moving exactly along a EllipsoidGeodesic is complicated: along latitude it’s circular, along longitude it’s elliptical. Move diagonally and it’s a hybrid of the two! As if that weren’t enough: as you move diagonally along a constant direction, heading changes because constant direction != constant heading except for special cases (specifically along the equator and along longitudes.)

If such exactness isn’t required moving along circular arcs works good enough (after all Earth’s semi-minor axis is 99.66% the length of its semi-major axis.) Actually aircraft don’t even fly along exact geodesics, they are affected by variations of the GeoID and atmospheric conditions. I’d start by converting both points A and B to Cartesian. Here’s some code that should help. To change radius normalize camera.position and move along that vector.

//declarations
var CC3=Cesium.Cartesian3;
var o=new CC3(0,0,0);
var oB=new CC3();var oBunit=new CC3();
var oA=new CC3();var oAunit=new CC3();
var rotater=new CC3();

//determine rotater, the axis you rotate around. +angle is from A to B
CC3.subtract(B,o,oB);
CC3.subtract(A,o,oA);
CC3.normalize(oB,oBunit);
CC3.normalize(oA,oAunit);
CC3.cross(oAunit,oBunit,rotater);

//determine angle amount to rotate
var adj = CC3.dot(oBunit,oAunit);
var angle = Math.acos(adj);

//determine radius change from A to B
var magB=CC3.magnitude(oB);
var magA=CC3.magnitude(oA);
var change = magB-magA;

``

If instead of knowing points A and B you know point A and the partial circumference you want move.

revolutions = partial_circumference / 2Math.PIr

radians = revolutions 2Math.PI

so

radians = partial_circumference / r

Thanks I appreciate this! I’ll see if I can make sense of it. :slight_smile: But regarding moving along EllipsoidGeodesics, you you did say this. But the paths I’m following are at most a few miles long with lots of variations, so I think approximating a circle will do just fine to simulate a drone flight. I’ll be sure to share the code when I’m finished.

Thanks!

Joe

Actually I think I may have had a misconception about geodesics. By definition a geodesic is: “of, relating to, or denoting the shortest possible line between two points on a sphere or other curved surface.”

That does not necessarily means a line of constant direction. For example, walking around the base of a hill could be less distance than walking over the hill with a constant direction. Since the Earth is slighly bulged near the Equator it’s shorter to walk slightly around that bulge than to walk directly over it. However that bulge is so slight there isn’t that much difference.

You can see how slight in Google Earth, use the measuring tool to measure from anti-meridian/equator to prime-meridian/equator. The geodesic line updates as you move the cursor. Notice that it doesn’t start curving around toward the poles until you get very close.

Interesting. Well, it works good enough for me. :slight_smile: And as promised, below is my finished code (for now). It works pretty well. You can control, height, pitch, roll, yaw or zoom as you move along the path and each transition will be smooth. The level of control and the speed of each transition can be managed by the number and distance of established points along the path since each transition is designed to use the entire time between points to make the transition. I have a couple small issues I’ll eventually get to. Zoom can throw me off the path, I assume because Zoom actually moves the position of the camera. I also had some problem with Yaw and it might be when combined with pitch. Somehow after applying the Yaw it gets the camera out of whack (like it’s roll stays skewed). Not sure what the issue is there. I created a beach run for a demo below. I threw in arbitrary and crude camera commands to show them off. It’s hard since I don’t have a way to relate the points I exported from Google Earth to it’s physical location after the fact so I can go in and add camera commands. Right now it’s trial and error. Maybe soon I’ll build my own editor to so that I can visually add camera commands at each point. :slight_smile:

One question I do have. I tried the Terrain Provider below (currently commented out), but it’s minimu height is higher than the default provider. Can I set that to be lower? Would it be viewer.scene.screenSpaceCameraController.minimumZoomDistance like you mentioned previously (about when I was scraping along the surface).?

Below is the code. I wrote a little routine that will convert the path string copied out of the Google Earth KML file into my custom objects for input. I pasted that routine first. You can just paste that into Sandcastle too and copy the output from the console.

Enjoy!

Joe

var input = “-117.3544594443053,33.19032879482207,0 -117.3529133632621,33.18967647907626,0 -117.3523860774533,33.188296775106,0 -117.352980795485,33.18733232134633,0 -117.3545952133418,33.18728432028313,0 -117.355771525032,33.1886704537332,0 -117.3557786891534,33.18994254100041,0 -117.3544594443053,33.19032879482207,0”;

var firstA = input.split(" ");

var secondA = ;

for (var i = 0; i < firstA.length; i++){

secondA[i] = firstA[i].split(",");

}

var cameraPositions = ‘{“cameraPositions”:[’;

for (var i = 1; i < secondA.length+1; i++){

cameraPositions = cameraPositions+’{“id”:’+i+’,“name”:"",“longitude”:’+secondA[i-1][0]+’,“latitude”:’+secondA[i-1][1]+’,“height”:100.0,“heading”:0.0,“pitch”:-15.0,“roll”:0.0,“yaw”:0.0,“zoom”:0.0},’

}

cameraPositions = cameraPositions+"]}";

console.log(cameraPositions);

It’s progressing very nicely! I’m surprised my code snippets actually worked, I never tested them. I’m not sure what you plan to do with zoom, unless you mean FOV zoom?

Some observations:

A geodesic in the air will be longer than it’s ‘shadow’ on the ground. As far as I can tell Cesium only calculates geodesic along the ground (height zero.) Since you move along circular arcs I’m not sure how useful the geodesic is. Circular arc distance is very easy to calculate at any radius. If the radius continuously changes on the circular arc you’d need to measure spiral distance for better accuracy.However for now I’d just replace geodesic with circular arc distance.

length = radians * radius

with an average radius

var radius = (magA+magB)/2;

You’re also going need to know heading at the end of the arc:

//determine ab in terms of Earth Fixed

var Wab = new CC3;

CC3.subtract(B,A,Wab);

CC3.normalize(Wab,Wab);

//calc ENU at point B in terms of Earth Fixed

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

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

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

CC3.cross(Wup,Weast,Wnorth);

//determine ab in terms of local ENU

var Lab=new CC3();

Lab.x=CC3.dot(Wab,Weast);

Lab.y=CC3.dot(Wab,Wnorth);

Lab.z=CC3.dot(Wab,Wup);

//determine heading

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

Thanks! It is looking pretty good and I appreciate your help. Your snippets work great out of the box! :slight_smile: My code can eventually for a good framework for refinement. While I wrote this for a specific project, I might pull it out into it’s own for reuse in other projects. I’ll give your circle code a shot.

This looks even better when I add a TerrainProvider. I used ‘//assets.agi.com/stk-terrain/world’ as I’ve seen in some examples. However, it seems the minimum height is higher when I set the camera position height to 0.0. Is there a way to set the minimum lower? Or I can adjust the heights in the path if TerrainProvider is in place but I still haven’t figured out how to determine the minimum height yet. I’m guessing it is some sort of average elevation? It would be nice to be able to fly over hills and down into valleys, etc. Ideally I could simply add the current elevation data to the specified path heights at each position. I’ll have to dig into the documentation or fourms to see how to get the elevation at a current position but maybe you can point it out if you already know.

Thanks!

Joe

Hyper,

Nevermind about how to get the position elevation from the terrain provider. I figured that out from the Sandcastle terrain example. Looks nice!

But I still don’t know how to adjust to a lower minimum? The elevations returned by the TerrainProvider seem to be relative the the current minimum as I’m -34 meters for positions over the ocean.

Joe

Height from viewer.scene.globe.getHeight is the terrain height at that lon/lat in meters from the reference ellipsoid.
Height from viewer.scene.camera.positionCartographic.height is the camera height in meters from the reference ellipsoid.

Be sure to check out https://groups.google.com/d/msg/cesium-dev/oT04Ly2pwMw/04DfVZ6ipGQJ for info regarding ellipsoid and MSL.

BTW where the surf meets the turf along the San Diego coast the terrain is typically 30-40 meters below the reference ellipsoid. Of course where the surf meets the turf along any ocean coastline is 0 meters sea level, but that’s not what Cesium is measuring. I usually set viewer.scene.screenSpaceCameraController.minimumZoomDistance = 2 allowing the camera to be within 2 meters of the terrain.

Also non-mountainous tiles can disappear when the camera sinks to negative heights.

That did it. When I set the minimumZoomDistance to 0 without a terrain provider and fly along the beach, my camera height tends to float just above zero in the single digits. When I add the terrain provider, the height goes up about 350 meters on average. So the TP seems to be adding about 350 meters to minimum flight height. So by setting the minimumZoomDistance to -300, I can get it to fly just a few meters above the surface and roughly follow the contours of the terrain if I set my points on my path at the various high and low points.

Thanks!

Joe

minimumZoomDistance is always in relation to the terrain surface, -300 actually allows you to bury the camera 300 meters under the surface! You can view the WGS84 Ellipsoid as a kind of terrain provider where the terrain exactly matches the ellipsoid.

Be sure to not confuse viewer.scene.camera.positionCartographic.height with height above terrain. Camera height above terrain is actually viewer.scene.camera.positionCartographic.height minus viewer.scene.globe.getHeight at the camera position.

Yes with WGS84 Ellipsoid viewer.scene.camera.positionCartographic.height happens to also equal height above terrain simply because the terrain is always zero distance from the ellipsoid.

Amazing work guys. I pasted it in and it works for me too!