0

I'm writing an AR application in which the user sees a ship from the perspective of the captain of the ship like this enter image description here

I want to use coordinates of other ships (like the one on the left) to draw the ships on top of the image using Canvas. The problem I'm having is mapping the latitude, longitude to Canvas points.

I've read about the Haversine and Bearing formulas but I can't figure out how to use them to do what I want. I know the latitude and longitude of the ship the user is looking from and tried using them to calculate the Canvas points of the other ship, but I can't get it to work. Any ideas?

Jaimy
  • 517
  • 4
  • 20
  • Possible duplicate of https://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates – Oliver Jenkins Jun 08 '17 at 09:39

1 Answers1

3

Finding real world object in camera's view.

This is the most simplistic answer possible, as there are many unknowns that will affect the result.

The problem

The image illustrates the problem as I understand it. enter image description here

We have 3 ships (the minimum required to find a solution) marked with a red A,B,C. We know the latitude and longitude of each ship. As they are close there is no need to correct for any distortion due to converging longitudes.

The green lines represent the camera's view, the image is projected via a lens onto the CCD (green line above A) The twp gray lines then are projected onto the camera's screen below the ship.

Triangulation and triangles.

From the longitudes we can find the distance from each ship to the other, giving us the lengths of the sides of a triangle.

var ships = {
    A : { lat : ?, long : ? },
    B : { lat : ?, long : ? },
    C : { lat : ?, long : ? },
}
var AB = Math.hypot(ships.A.lat - ships.B.lat, ships.A.long - ships.B.long);
var BC = Math.hypot(ships.C.lat - ships.B.lat, ships.C.long - ships.B.long);
var CA = Math.hypot(ships.A.lat - ships.C.lat, ships.A.long - ships.C.long);

The inset triangle shows how to find the angle of a corner given the lengths of the sides.

For this we are after the angle pheta

var pheta = Math.acos(BC * BC - AB * AB + CA * CA) / (-2 * AB * CA));

The camera's field of view.

Now we need an extra bit of information. That is the field of view of the camera (in image as the green lines and red FOV). You will have to find those details for the camera you are using.

A typical phone camera has a focal length (fl) from 24-35mm equivalent and knowing the CCD size in relative terms you can find the FOV with var FOV = 2 * Math.atan((CCD.width / 2) / fl) But this is problematic as a modern phone is under 10mm thick and the CCD is tiny, what are the actual dimensions?

There is a long and complicated process you can use to determine the FOV without knowing the internal dimensions of the camera, but easy to look up the phone specs.

For now let's assume the camera has a FOV of 67deg (1.17radians). If the camera has a resolution of 1280 and we ignore lens distortions, and keep the camera vertical and at the same level as the targets , we can calculate the distance in pixels between the two ships via the angle between them.

var FOV = 1.17; // radians the Field of View of the camera
var pixels = (1.17 / 1280) * pheta; // 1280 is horizontal resolution of display

So now we have the distance in pixels between the two ships on the camera. Assuming that they fit on the camera we are missing one more important bit of information.

Bearings

We need to know which way the camera is pointing as a bearing. Only when we have that can we find the ships. So lets assume the GPS on the phone gives you the live bearing. What we need is the bearing to one of the ships.

I had to dig deep into my archives to find this, It had no source or referance so can only provide as is. All it had is Haversin so assuming that is the method used.

After some further digging I found a referance to what is likely the original source code that this was derived from Calculate distance, bearing and more between Latitude/Longitude points

function deg2rad(angle) {  return angle * 0.017453292519943295 }
function rad2deg(angle) {  return angle / 0.017453292519943295 }
//return bearing in radians.
function getBearing(lat1,lon1,lat2,lon2){
    var earth = 6371e3; 
    var lat1 = lat1.toRadians();
    var lat2 = lat2.toRadians();
    var lon1 = lon1.toRadians();
    var lon2 = lon2.toRadians();
    var latD = lat2-lat1;
    var lonD = lon2-lon1;
    var a = Math.sin(latD / 2) * Math.sin(latD / 2) +  Math.cos(lat1 ) * Math.cos(lat2) *  Math.sin(lonD / 2) * Math.sin(lonD / 2);
    var c = 2 * Math.atan2( Math.sqrt(a),  Math.sqrt(1-a) );
    return earth * c;
}

So now you can get the bearing from you to one of the ships. Use that and your bearing to find the difference in angle between you and it.

   var yourBearing = ?; // in radians
   var shipBBearing = getBearing(ships.A.lat, ships.A.long, ships.B.lat, ships.B.long);

Now get the differance in angle

   var shipBAt = yourBearing - shipBBearing

Knowing the pixel FOV

   var shipBPixelsFromCenter = (FOV / 1280) * shipBAt;

   var shipBXpos = 1280 / 2 - shipBPixelsFromCenter;
   // and from above the dif in pixels between ships is
   var shipCXpos = shipBXpos + pixels.

And done.

Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • Gonna try this and will let you know asap, but for this moment I want to say thanks for taking the time to give such an elaborate answer! Much appreciated! – Jaimy Jun 08 '17 at 13:58
  • @Jaimy It was a little rushed at the end and did not proofread it, but back now so will give it a once over to make sure I got everything correct. – Blindman67 Jun 08 '17 at 14:05
  • Quick update. So far I'm getting X-positions that seem to make sense. Just need to tweak some values to my particular case. Thanks again for the great and detailed answer! Really appreciate it. – Jaimy Jun 08 '17 at 18:23