views:

849

answers:

5

Hi

I get some GPS coordinates from Google Maps and I need to find the distance between them using Objective C. I have implemented the formula but I get results that are way to big.

I have tested the values from Google Maps by passing them back into Google Earth and a Geocoding service on the internet and everything checks out. Im now beginning to suspect that the cosine law demands I do some sort of conversion with the coordinates before I pass them in.

I did a similar implementation of the Haversine formula, but this also gave me to big results. I then switched to the cosine since it was easier to debug and I don't need very high precision.

Hope someone could shed a little light on this one, or use the code:)

- (CGFloat) calculateDistanceBetweenPoints:(CGPoint) origin andDestination:(CGPoint) destination {

//To convert kilometers to miles, divide by 1.609
// x = latitude 
// y = longitude

/* example:
 Dubai      : 25.248665, 55.352917 
 Amsterdam  : 52.309071, 4.763385
 Approx dist: 5,182.62 KM
 Calc. dist : 8,253.33
 */

CGFloat toRad           =   (M_PI / 180);
CGFloat R               =   6371.0f; //earth's mean radius in Km

CGFloat sinePart        =   sinf( origin.x * toRad ) * sinf( destination.x * toRad );
CGFloat cosinePart      =   cosf( origin.x * toRad ) * cosf( destination.x * toRad );
CGFloat deltaCosinePart =   cosf( ( destination.y - origin.y ) * toRad );

CGFloat delta           =   acosf( sinePart + cosinePart * deltaCosinePart) * R;

return delta;
}

Above calculated from links referenced here:stackoverflow question

+2  A: 

Have you considered using the method provided in CLLocation:

- (CLLocationDistance)getDistanceFrom:(const CLLocation *)location?

Johan Kool
Thanks JohanAfter having read the documentation that does sound pretty tempting:)I implemented it in three lines: CLLocation *originPos = [[CLLocation alloc] initWithLatitude:origin.x longitude:origin.y]; CLLocation *destinationPos = [[CLLocation alloc] initWithLatitude:(double)destination.x longitude:(double)destination.y]; CGFloat dist = [originPos getDistanceFrom:destinationPos];Strangely this gives me the exact same values?? It still insists that Amsterdam and Dubai are some 8,200 Km apart?So well, I did find out my error is something more general :/
RickiG
A: 

Well, if I understand the question correctly, you are not accounting for the curvature of the earth. The cosine law works on a plane, not on a sphere. For example: the North Pole and the South Pole are located at ~20,000 km apart if you fly, but only ~6000km if you dig a tunnel ;)

Regards, Ari

BeMeCollective
I believe his results are too big, not too small.
SteveJ
Thanks AriFor my purpose and limited need of precision I multiply the radius R of the earth at the end of the formula to account for the curvature.
RickiG
+2  A: 

The code might be alright, when i run it on your example data (minus a couple of decimal places) it returns 5168.3584

strainer
Thank you hic3456 for testing that out for me. I passed in altered coordinates and your answer made me go back and check again… please see my answer below.
RickiG
Youre very welcome RickiG - helped me get an account going :)+Respect to Johan and Mark's much more informative replies'
strainer
+1  A: 

There are a number of questions that might help, including:

Given the two positions, you create a spherical triangle corners A at Amsterdam, B at Dubai and C at the North Pole, with sides a = 90° - ϕAms, b = 90° - ϕDub, and angle C = Δλ = λDub - λAms. The required answer is the side c.

Using some material from my answer to SO 389211.

(This is a radically revised answer - my previous attempt used the wrong spherical triangle, and therefore got the wrong answer.)


ASCII art at its worst:

                   + C (North Pole)
                  /|
                b/ |
                /  |
(Amsterdam) A  +   | a
                \  |
                c\ |
                  \|
                   + B (Dubai)

The basic Cosine Law for Spherical Triangles is:

cos c = cos a . cos b + sin a . sin b . cos C

Noting that cos (90º - x) = sin x and sin (90º - x) = cos x, we can write:

cos c = sin ϕAms . sin ϕDub + cos ϕAms . cos ϕDub . cos Δλ

The angle c in radians is then converted to a distance by multiplying by the radius of the Earth.


Applying this to your data:

Dubai: ϕDub = 25.248665°N, λDub = 55.352917°E
Amsterdam: ϕAms = 52.309071°N, λAms = 4.763385°E

Δλ = 50.589532°

Working to 6 decimal places for the trigonometry:

cos c = 0.426548 × 0.791320 + 0.904465 × 0.611402 × 0.634872
      = 0.337536            + 0.351079
      = 0.688615

Whence:

c = 46.479426°
  =  0.811219 radians

Multiplying this by 6371 km as the nominal radius of the earth yields

c = 5168 km

Hence, for R = 6371 km, the distance is 0.811219 × 6371 = 5168 km (to 4 s.f.).

TrueKnowledge says it should be about 5155 km. The positional data it says it used is comparable to the values you specified, and Wikipedia confirms the radius you provided. This is reasonably close - redoing the calculation with identical coordinates and more digits in the calculation would yield a better answer, but close to this one.


Jonathan Leffler
ASCII Math Art, what is not to love:) I get this value, using the correct coordinates: 5168.279785, that is well within my error margin.
RickiG
This answer is wrong, as Jonathan himself points out. How did it get upvoted ?!
High Performance Mark
@High-Performance Mark: I've completely reworked the answer using a different (correct) spherical triangle and that gives an answer that is close enough to correct to be believable.
Jonathan Leffler
Hi Jonathan and thank you for doing this textbook example:) I believe that this post is now complete with code and theory. I hope you haven't lost too much sleep about this since January:)
RickiG
@RickiG: lost sleep - no. But it did irk me that I could see I'd done something wrong but hadn't worked out what...and then, eventually, I realized where I'd gone wrong. Thanks.
Jonathan Leffler
+1  A: 

Guys Im so sorry :( and have a red face. I do some other calculations on these coordinates. I calculated their (x, y) position so they position the city correctly on a custom map I drew. These coordinates are calculated as such:

- (CGPoint) translateToPixelsFromLatitude:(CGFloat) latitude andLongitude:(CGFloat) longitude {

CGPoint position    = CGPointMake(0, 0);
CGFloat mapWidth    = 300.0f;
CGFloat mapHeight   = 200.0f;

CGFloat offsetX     = 5.0f;
CGFloat offsetY     = 35.0f;

position.x = (((180 + longitude) / 360) * mapWidth) + offsetX;
position.y = (mapHeight - (((90 + latitude) / 180) * mapHeight)) + offsetY;

return position;

}

By some, should not code in the middle of the night and then head over to SO to get help from busy people, accident I pass the transformed coordinates to the method.

I found this out when I noticed that when geocoding different cities I got very similar results and then I read hic3456's post and suddenly it made sense.

I log and test the coordinates and distance from a separate class so everything got logged before the wrong data were passed to the method.

Now I really hope someone other than me will benefit from this.

Sorry again and thanks to every one of you.

RickiG