51

I need a way of comparing two CLLocationCoordinate2D's however when I tried using == it wouldn't work. Please can someone help me out with the best way of comparing them?

ZeMoon
  • 20,054
  • 5
  • 57
  • 98

13 Answers13

59

Either the generic approach for comparing two instances of any given struct type:

memcmp(&cllc2d1, &second_cllc2d, sizeof(CLLocationCoordinate2D))

or

cllc2d1.latitude == cllc2d2.latitude && cllc2d1.longitude == cllc2d2.longitude

should work, if you really want to be sure they're exactly equal. However, given that latitude and longitude are defined as doubles, you might really want to do a "close enough" comparison:

fabs(cllc2d1.latitude - cllc2d2.latitude) <= epsilon && fabs(cllc2d1.longitude - cllc2d2.longitude) <= epsilon

where epsilon is whatever level of error you want to accept.

Max Hudson
  • 9,961
  • 14
  • 57
  • 107
Glenn
  • 822
  • 6
  • 9
  • 5
    The first might be considered sketchy, since you're making the assumption that the struct is packed without any bytes between the members. – joerick Apr 17 '12 at 21:14
  • 1
    Good point. If you zero the entire struct when it's created/allocated then the memcmp approach is safe even for non-packed structs, but otherwise, use with caution. – Glenn Apr 18 '12 at 16:38
  • 1
    @Glenn can you please fix abs to fabs and < to <=. – Shmidt Aug 11 '12 at 12:58
  • what do you mean by zero the entire struct? And what do you mean that struct is packed without any bytes between the members? you mean it may not even be the case>? – user4951 Nov 04 '12 at 14:26
  • I'm having trouble getting this to work for exactly equal locations and can't figure out what I'm doing wrong. See my question at http://stackoverflow.com/questions/18200609/cant-compare-two-cllocationcoordinate2ds-negative-zero – devios1 Aug 13 '13 at 04:20
  • The first approach is a completely unreliable approach. The second is difficult to use correctly. Why not just calculate the distance and compare the result with a minimum value where two locations are considered "equal"? – CouchDeveloper Jun 16 '16 at 08:38
  • fyi, sizeof is now undefined – Frank Mancini Feb 19 '20 at 16:20
35

As a small addition to all these answers, it's quite handy to have the comparison defined as a preprocessor define:

#define CLCOORDINATES_EQUAL( coord1, coord2 ) (coord1.latitude == coord2.latitude && coord1.longitude == coord2.longitude)

or with epsilon:

#define CLCOORDINATE_EPSILON 0.005f
#define CLCOORDINATES_EQUAL2( coord1, coord2 ) (fabs(coord1.latitude - coord2.latitude) < CLCOORDINATE_EPSILON && fabs(coord1.longitude - coord2.longitude) < CLCOORDINATE_EPSILON)

This allows you to do a comparison as follows:

CLLocationCoordinate2D resultingCoordinate = ... a method call ...;
CLLocationCoordinate2D expectedCoordinate = CLLocationCoordinate2DMake(48.11, 11.12);

if(CLCOORDINATES_EQUAL( resultingCoordinate, expectedCoordinate)) {
    NSLog(@"equal");
} else {
    NSLog(@"not equal");
}

Another alternative is to use an inline method, if you don't like the preprocessor.

Klaas
  • 22,394
  • 11
  • 96
  • 107
  • 3
    Never compare floating point numbers with `==`. See answers where people use `epsilon`. – DanSkeel Nov 12 '13 at 08:46
  • @DanSkeel See my edited answer. Sometimes you have a use case where you want to compare exact coordinates. E.g. if the source for the coordinates is a place database and you only want to compare those. Then there is no need for a range for the equal comparison. So you might think about your down vote again. – Klaas Nov 12 '13 at 14:01
  • 1
    You didn't get my point. Imagine the case when `1 != 0.99999999999` ... It's general rule for any floating point comparison. [Read about](http://floating-point-gui.de/errors/comparison/) it. This rule is old as a computer. – DanSkeel Nov 14 '13 at 08:33
  • 2
    I still don't get your point. If the source of your coordinates is e.g. a database and you don't do any computations with the coordinates it is just fine to use a regular == comparison. – Klaas Nov 14 '13 at 20:20
  • But some one computed that coordinates. You mean that all that float coordinates were set manually by some person? Not a computer? – DanSkeel Nov 18 '13 at 07:43
  • Im my case I let the user choose a location and save the coordinates into my database. From that point on the coordinates are fixed and will never change. To to syncing between multiple devices I need the exact coordinates, because the user could create two locations that are very close to each other. – Klaas Nov 18 '13 at 11:18
  • 1
    Just a late clarification on the earlier comments here: the issue is *how* the coordinates are stored. Latitude and longitude are `Double` values by default. `Double` values are *rarely exact* in a computer. If the user enters a latitude, say, of `38.627003`, there's no guarantee that the computer will store exactly `38.627003`. A recent test printing a computer's interpretation to 15 decimal places returned a value of `38.627002716064453`! Now, if you retrieve and store the user's response as a `String`, *then* the result would be exact - but remember the *default* is `Double`. – leanne Oct 19 '18 at 19:21
24

A Swift extension:

import MapKit

extension CLLocationCoordinate2D: Equatable {}

public func ==(lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
    return (lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude)
}

[tested as of Xcode 7.3.1, Swift 2.2] [and, of course, still has the intrinsic danger of comparing floating point values, so you might want to consider using epsilon as mentioned in earlier answers]

leanne
  • 7,940
  • 48
  • 77
15

You can use the CLLocation class' distanceFromLocation: method. The return value is a CLLocationDistance, which is really just a double.

- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
Greg M. Krsak
  • 2,102
  • 1
  • 18
  • 28
11

In Swift 3, DBL_EPSILON is deprecated. Use Double.ulpOfOne.

extension CLLocationCoordinate2D {
    func isEqual(_ coord: CLLocationCoordinate2D) -> Bool {
        return (fabs(self.latitude - coord.latitude) < .ulpOfOne) && (fabs(self.longitude - coord.longitude) < .ulpOfOne)
    }
}
Ryan
  • 4,799
  • 1
  • 29
  • 56
9

You could define a function which feels pretty like coming from CoreLocation:

BOOL CLLocationCoordinateEqual(CLLocationCoordinate2D coordinate1, CLLocationCoordinate2D coordinate2) { return (fabs(coordinate1.latitude - coordinate2.latitude) <= DBL_EPSILON && fabs(coordinate1.longitude - coordinate2.longitude) <= DBL_EPSILON); }

leviathan
  • 11,080
  • 5
  • 42
  • 40
Albert Schulz
  • 339
  • 3
  • 6
  • upvoted as this is the most maintainable solution IMO – mvanallen Mar 06 '15 at 16:37
  • Good solution. Here is the complete method just for new bee. -(BOOL)compairLastSavedCoordinates:(CLLocationCoordinate2D )coordinate1 withNewCoordinates:(CLLocationCoordinate2D)coordinate2 { return (coordinate1.latitude == coordinate2.latitude) && (coordinate1.longitude == coordinate2.longitude); } – iLearner May 25 '16 at 12:34
  • 2
    Never compare floating point numbers! If you do, it's unreliable. In practice, most comparisons from real locations generated by different sources will return `false` - even if they are pretty close to each other and can be considered "equal". – CouchDeveloper Jun 16 '16 at 08:30
6

Updated for Swift 5 based on leann's answer.

import CoreLocation

extension CLLocationCoordinate2D: Equatable {
    static public func ==(lhs: Self, rhs: Self) -> Bool {
        lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
    }
}
Adrian
  • 16,233
  • 18
  • 112
  • 180
2
CLLocationCoordinate2D c1, c2;
if (c1.latitude == c2.latitude && c1.longitude == c2.longitude)
{
    // ...
}

I'm not kidding. CLLocationCoordinate2D is a C struct, and there's no easy way to compare C structs, apart from comparing the individual members.

joerick
  • 16,078
  • 4
  • 53
  • 57
1

CLLocationCoordinate2D is a C struct, thus, you need to compare its fields:

CLLocationCoordinate2D coord1, coord2;
if (coord1.latitude == coord2.latitude && coord1.longitude == coord2.longitude) {
    // coordinates are equal
}

Note, CLLocationCoordinate2D fields are double, thus, you may get the same issues like with any other floating point comparison. Thus, I suggest to round a little bit values, e.g.:

CLLocationCoordinate2D coord1, coord2;
if (round(coord1.latitude * 1000.0) == round(coord2.latitude * 1000.0)
    && round(coord1.longitude * 1000.0) == round(coord2.longitude * 1000.0)) {
    // coordinates are equal
}

Yes, this, code is slower, but this may help you to avoid problems caused by not so perfect precision of floating point numbers.

Aleksejs Mjaliks
  • 8,647
  • 6
  • 38
  • 44
1

You can wrap CLLocationCoordinate into an NSValue using this function + (NSValue *)valueWithMKCoordinate:(CLLocationCoordinate2D)coordinate

And then use isEqualToValue to compare.

Quote from Apple doc of isEqualToValue function:

The NSValue class compares the type and contents of each value object to determine equality.

Ref: Apple doc of NSValue

Raymond
  • 1,172
  • 16
  • 20
0

We can convert latitude and longitude to NSString and do a string compare.

+ (BOOL)isEqualWithCoordinate:(CLLocationCoordinate2D)location1 withAnotherCoordinate:(CLLocationCoordinate2D)location2{
NSString *locationString1 = [NSString stringWithFormat:@"%g, %g", location1.latitude, location1.longitude];
NSString *locationString2 = [NSString stringWithFormat:@"%g, %g", location2.latitude, location2.longitude];

if ([locationString1 isEqualToString:locationString2]) {
    return YES;
}else{
    return NO;
}

}

Since %g will convert truncate decimal number to 4 digit.

Saif
  • 812
  • 1
  • 9
  • 18
0

You have to use some tolerance value to handle Double comparison, with trial and error I found that 0.01 is an acceptable value.

import MapKit

extension CLLocationCoordinate2D: Equatable {
  public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
    let tolerance = 0.01
    return abs(lhs.latitude - rhs.latitude) < tolerance &&
    abs(lhs.longitude - rhs.longitude) < tolerance
  }
}
FarouK
  • 740
  • 6
  • 17
-1

Swift 5.3 way

I've created a gist

extension CLLocationCoordinate2D: Equatable {
    public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
        let numbersAfterCommaAccuracy: Double = 4
        let ratio = numbersAfterCommaAccuracy * 10
        let isLatitudeEqual = ((lhs.latitude - rhs.latitude) * ratio).rounded(.down) == 0
        let isLongitudeEqual = ((lhs.latitude - rhs.latitude) * ratio).rounded(.down) == 0
        return isLatitudeEqual && isLongitudeEqual
    }
}
Nike Kov
  • 12,630
  • 8
  • 75
  • 122