I have a MKMapView, and I would like to know how I can find the nearest 5 annotations to the user, and only display them on the MKMapView.
My code currently is:
- (void)loadFiveAnnotations {
NSString *string = [[NSString alloc] initWithContentsOfURL:url];
string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
NSArray *chunks = [string componentsSeparatedByString:@";"];
NSArray *keys = [NSArray arrayWithObjects:@"type", @"name", @"str开发者_开发技巧eet", @"address1", @"address2", @"town", @"county", @"postcode", @"number", @"coffeeclub", @"latlong", nil];
// max should be a multiple of 12 (number of elements in keys array)
NSUInteger max = [chunks count] - ([chunks count] % [keys count]);
NSUInteger i = 0;
while (i < max)
{
NSArray *subarray = [chunks subarrayWithRange:NSMakeRange(i, [keys count])];
NSDictionary *dict = [[NSDictionary alloc] initWithObjects:subarray forKeys:keys];
// do something with dict
NSArray *latlong = [[dict objectForKey:@"latlong"] componentsSeparatedByString:@","];
NSString *latitude = [[latlong objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *longitude = [[latlong objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
CLLocationDegrees lat = [latitude floatValue];
CLLocationDegrees longi = [longitude floatValue];
Annotation *annotation = [[Annotation alloc] initWithCoordinate:CLLocationCoordinate2DMake(lat, longi)];
annotation.title = [dict objectForKey:@"name"];
annotation.subtitle = [NSString stringWithFormat:@"%@, %@, %@",[dict objectForKey:@"street"],[dict objectForKey:@"county"], [dict objectForKey:@"postcode"]];
[mapView addAnnotation:annotation];
[dict release];
i += [keys count];
}
}
A long answer, already mostly written when Stephen Poletto posted and containing example code on how to use the built-in methods for sorting an array, so I though it was still worth posting though the essential answer is the same (ie, "pick the five closest for yourself, pass only those on"):
You're going to need to sort your annotations by distance for yourself, and submit only the closest five to the MKMapView. If you have two CLLocations then you can get the distance between them using the distanceFromLocation: method (which was getDistanceFrom: prior to iOS 3.2; that name is now deprecated).
So, for example, supposing your Annotation class had a method 'setReferenceLocation:' to which you pass a CLLocation and a getter 'distanceFromReferenceLocation' which returns the distance between the two, you could do:
// create and populate an array containing all potential annotations
NSMutableArray *allPotentialAnnotations = [NSMutableArray array];
for(all potential annotations)
{
Annotation *annotation = [[Annotation alloc]
initWithCoordinate:...whatever...];
[allPotentialAnnotations addObject:annotation];
[annotation release];
}
// set the user's current location as the reference location
[allPotentialAnnotations
makeObjectsPerformSelector:@selector(setReferenceLocation:)
withObject:mapView.userLocation.location];
// sort the array based on distance from the reference location, by
// utilising the getter for 'distanceFromReferenceLocation' defined
// on each annotation (note that the factory method on NSSortDescriptor
// was introduced in iOS 4.0; use an explicit alloc, init, autorelease
// if you're aiming earlier)
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor
sortDescriptorWithKey:@"distanceFromReferenceLocation"
ascending:YES];
[allPotentialAnnotations sortUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]];
// remove extra annotations if there are more than five
if([allPotentialAnnotations count] > 5)
{
[allPotentialAnnotations
removeObjectsInRange:NSMakeRange(5,
[allPotentialAnnotations count] - 5)];
}
// and, finally, pass on to the MKMapView
[mapView addAnnotations:allPotentialAnnotations];
Depending on where you're loading from, you made need to create a local store (in memory or on disk) for annotations and select the five nearest whenever the user moves. Either register yourself as a CLLocationManager delegate or key-value observe on the map view's userLocation property. If you have quite a lot of potential annotations then sorting all of them is a bit wasteful and you'd be better advised to use a quadtree or a kd-tree.
First you'll need to grab the user's current location. You can build a CLLocationManager and register yourself as the delegate for location updates as follows:
locationManager = [[[CLLocationManager alloc] init] autorelease];
[locationManager setDelegate:self];
[locationManager startUpdatingLocation];
After setting yourself as the delegate, you'll receive the following callback:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
Now that you have the user's location (newLocation), you can find the five closest annotations. There is a handy method in CoreLocation:
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
As you're iterating through your annotations, just store the five nearest locations. You can build a CLLocation out of the 'lat' and 'longi' variables you have using:
- (id)initWithLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude
Hope this helps!
精彩评论