2

This is specifically a Google Earth API question, but any general geographical solution that can be adapted to it is fine, too.

I'm using the Google Earth API to dynamically put a placemark somewhere on the Earth. Like all placemarks, it has a fixed latitude and longitude. In this case, the altitude is basically always 0. After the placemark is placed, the user may fly the camera around somewhere else and the placemark may not be visible.

What I'd like to be able to do is have a button that would let you automatically look in the direction of the original placemark without changing the camera's position.

In Google Earth API, the user's viewport is represented by a Camera object, which has a latitude and longitude as well as a heading (left-to-right axis) and tilt (up-and-down axis).

So the question can be reduced to: if you have a fixed point on a globe at (lat1, lng1, alt1), how do you calculate the heading and tilt for a camera at (lat1, lng2, alt2) so that it is facing that position?

nucleon
  • 861
  • 6
  • 22

2 Answers2

1

Check out the 'bearing' section at this link http://www.movable-type.co.uk/scripts/latlong.html

Here is the javascript he suggests for calculating bearing (or heading).

var y = Math.sin(lng1-lng2) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(lng1-lng2);
var brng = Math.atan2(y, x).toDeg();
CramerTV
  • 1,376
  • 1
  • 16
  • 29
  • Thanks! This is helpful, though for some reason it still doesn't quite work. (There is a typo above — it should be lng2-lng1 in both places you have it as lng1-lng2.) I can't figure out if the error is in my code, or my approach, or something related to Google Earth. I get results that don't result in headings that don't line up correctly at all. What's odd is that pasting the same values in onto his interface gives me different values than his Javascript code. Frustrating! – nucleon Mar 01 '13 at 20:34
  • Here is what I'm currently using: var dLon = lng2-lng1; var y = Math.sin(dLon) * Math.cos(lat2); var x = Math.cos(lat1)*Math.sin(lat2) -Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon); var brng = Math.atan2(y, x).toDeg(); ...which ought to work. But using all decimal degrees gets me slightly wrong results each time. Hmm... – nucleon Mar 01 '13 at 20:35
  • @nucleon - you must covert the decimal degrees to radians to do the calculation. i.e. `lat2-lat1 * Math.PI / 180` – Fraser Mar 02 '13 at 05:30
  • Ah, brilliant, perfect! Just multiplying each of the lat/lng coordinates * Math.PI / 180 made it work perfectly. Thanks! – nucleon Mar 03 '13 at 15:50
1

Here's the full, final implementation for turning the current camera of arbitrary altitude towards another lat/lng point of arbitrary altitude. It isn't extremely beautiful (getting the lats and lngs twice because in one I'm using radians and another I'm not) but it works, and causes the Google Earth camera to face whatever it is you want it to face:

//calculate heading/bearing
var lat1 = parseFloat(camera.getLatitude())* Math.PI / 180;
var lng1 = parseFloat(camera.getLongitude())* Math.PI / 180;
var lat2 = parseFloat(target_lat)* Math.PI / 180;
var lng2 = parseFloat(target_lng)* Math.PI / 180;

var dLon = lng2-lng1;
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x).toDeg();

//calculate tilt
lat1 = parseFloat(camera.getLatitude());
lng1 = parseFloat(camera.getLongitude());
lat2 = parseFloat(target_lat);
lng2 = parseFloat(target_lng);
var camera_alt = camera.getAltitude();
var target_alt = your_target_altitude; //meters!

//this uses the distVincenty function and geo.js library here: http://www.movable-type.co.uk/scripts/latlong.html
var distance = parseFloat(distVincenty(Geo.parseDMS(lat1),Geo.parseDMS(lng1),Geo.parseDMS(lat2),Geo.parseDMS(lng2)));
var height_diff = (camera_alt-target_alt);

if(height_diff<0) { //target is above camera, invert equation
    height_diff = (target_alt-camera_alt);
    var tilt = Math.atan(height_diff/distance).toDeg()+90; //the +90 is because of the way Google Earth calculates tilt in such circumstances
} else {
    var tilt = Math.atan(distance/height_diff).toDeg();
}

camera.setTilt(tilt);
camera.setHeading(brng);
nucleon
  • 861
  • 6
  • 22