views:

118

answers:

1

Hello,

I'm working on the app that uses CoreData. There is location entity that holds latitude and longitude values. I'd like to fetch those entities sorted by distance to the user's location. I tried to set sort descriptor to distance formula sqrt ((x1 - x2)^2 + (y1 - y2)^2) but it fails with exception "... keypath ... not found in entity".

NSString *distanceFormula = [NSString stringWithFormat:@"sqrt(((latitude - %f) * (latitude - %f)) + ((longitude - %f) * (longitude - %f)))", 
                            location.coordinate.latitude, 
                            location.coordinate.latitude, 
                            location.coordinate.longitude, 
                            location.coordinate.longitude];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:distanceFormula ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSError *error;
NSArray *result = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];

I'd like to fetch already sorted objects rather then fetch them all and then sort in the code.

Any tips appreciated.

A: 

NSSortDescriptor should be initialised with a key string for a property of the object, not a query string. It means you should implement your distance formula as a method of your object.

After doing so, it doesn't really matter if you sort before or after fetching:

NSArray *result = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
result = [result sortedArrayUsingSelector:@"compareDistance"];

Another point. Your distance formula doesn't work correctly, as lat. and long. don't have the same scale, unless you're on the equator. Use this:

double latDiff = lat1-lat2;
double longDiff = (long1-long2)*cos(lat1); //cos() assumes lat1 is in radians
double distance = sqrt(latDiff*latDiff+longDiff*longDiff);

If the distance is more than a few hundred kilometers, you need Spherical cosines law:

// assuming angles in radian,
double separation = acos( sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(long1-long2) );
double distance = separation*earthRadius;
Mo
Can you post anyway the formula for long distances? Thank you in advance.
unforgiven
Thanks unforgiven. I added the formula in the answer.
Mo