How can I detect if an MKPolyline intersects itself? I tried researching this but only found problems that has two or more lines. How can I detect if I only have one line/one stroke? I want to detect it after the user releases the touch.
I currently have this code in touchEnded function.
CGPoint location = [touch locationInView:self.mapView];
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:location toCoordinateFromView:self.mapView];
[self.coordinates addObject:[NSValue valueWithMKCoordinate:coordinate]];
NSInteger numberOfPoints = [self.coordinates count];
if(numberOfPoints > 2)
{
[self setLineLength:[self getLengthArea]];
if([self lineLength] < 401)
{
if (numberOfPoints > 2)
{
CLLocationCoordinate2D points[numberOfPoints];
for (NSInteger i = 0; i < numberOfPoints; i++) {
points[i] = [self.coordinates[i] MKCoordinateValue];
}
[self.mapView addOverlay:[MKPolyline polylineWithCoordinates:points count:numberOfPoints]];
}
PCAnnotation *ann = [[PCAnnotation alloc] init];
[ann setCoordinate:coordinate];
ann.title = #"End";
[self.mapView addAnnotation:ann];
}
else
{
NSArray *overlayItems = [self.mapView overlays];
NSArray *annotations = [self.mapView annotations];
[self.mapView removeOverlays:overlayItems];
[self.mapView removeAnnotations:annotations];
}
}
MKPolyline inherits form MKMultiPoint
which has a - (MKMapPoint *)points; method,
You could try to check for intersections between all line segments.
"The points are connected end-to-end in the order they are provided."
So you can make your own line segments between each 2 points,
and after you have an Array of line segments you can check for their intersections.
Here is a C++ code snippet for checking the intersections:
It can be easily translated to Objective-C and whatever else.
public static bool LineSegmentsCross(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
{
float denominator = ((b.X - a.X) * (d.Y - c.Y)) - ((b.Y - a.Y) * (d.X - c.X));
if (denominator == 0)
{
return false;
}
float numerator1 = ((a.Y - c.Y) * (d.X - c.X)) - ((a.X - c.X) * (d.Y - c.Y));
float numerator2 = ((a.Y - c.Y) * (b.X - a.X)) - ((a.X - c.X) * (b.Y - a.Y));
if (numerator1 == 0 || numerator2 == 0)
{
return false;
}
float r = numerator1 / denominator;
float s = numerator2 / denominator;
return (r > 0 && r < 1) && (s > 0 && s < 1);
}
Related
I am using google map ios sdk, My problem is i have to get coordinates for every 1000 meter on a poly line in map. now i am able to get number of location in given path and able to accesses them using the following code snip.
-(NSMutableArray*)getCoordinates {
pathCoordinatesArray = [[NSMutableArray alloc]init];
GMSMutablePath *path = [VTDirectionManager getPath];
NSLog(#"count %d",path.count);
for (int i=0 ; i<path.count; i++) {
if (i+1 > path.count) {
return pathCoordinatesArray;
}
for (int j = i+1; j<path.count; j++) {
CLLocationCoordinate2D sourceCoordinate = [path coordinateAtIndex:i];
CLLocation *sourceLocation = [[CLLocation alloc]initWithLatitude:sourceCoordinate.latitude longitude:sourceCoordinate.longitude];
CLLocationCoordinate2D destinationCoordinate = [path coordinateAtIndex:j];
CLLocation *destinationLocation = [[CLLocation alloc]initWithLatitude:destinationCoordinate.latitude longitude:destinationCoordinate.longitude];
BOOL check ;
check = [self checkDistanceForSource:sourceLocation andDestination:destinationLocation];
//jump to next 1000 distance position
if (check) {
i = j;
}
}
}
return pathCoordinatesArray;
}
-(BOOL)checkDistanceForSource:(CLLocation*)source andDestination:(CLLocation*)destination {
CLLocationDistance distance = [source distanceFromLocation:destination];
if (distance > 1000) {
CLLocation *location = [[CLLocation alloc] initWithLatitude:destination.coordinate.latitude longitude:destination.coordinate.longitude];
[pathCoordinatesArray addObject:location];
return YES;
}
return NO;
}
Suppose if i have 5000 meter distance path , then i have to get 5 coordinates ,each at 1000 meters position sequentially.
i think it is wrong code . Suggest me with optimized code
see the image each points are 1000 metered distance .
Please change your loop code like this
for (int i=0 ; i<path.count; i++) {
if (pathCoordinatesArray.count == 0) {
CLLocationCoordinate2D temp2d = [path coordinateAtIndex:i];
CLLocation *tempLoc = [[CLLocation alloc]initWithLatitude:temp2d.latitude longitude:temp2d.longitude];
[pathCoordinatesArray addObject:tempLoc];
}
CLLocationCoordinate2D destinationCoordinate = [path coordinateAtIndex:i];
CLLocation *destinationLocation = [[CLLocation alloc]initWithLatitude:destinationCoordinate.latitude longitude:destinationCoordinate.longitude];
[self checkDistanceForSource:[pathCoordinatesArray lastObject] andDestination:destinationLocation];
}
}
Basically, I want to draw a Polyline with this code. As you can see, I have the Overlay method ready, I only need to know how to call MKPolyline in the (void)viewDidLoad
[UPDATE] OK so I managed to draw Polylines. However, the lines don't make sense, they connect some pins with no order and then 4 of the pins release a line directed to the north west of the map, I need to know how to make it line one pin to another in an ordered manner.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *csvFilePath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"csv"];
NSString *dataStr = [NSString stringWithContentsOfFile:csvFilePath encoding:NSUTF8StringEncoding error:nil];
NSArray* allLinedStrings = [dataStr componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
MKMapPoint northEastPoint;
MKMapPoint southWestPoint;
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * allLinedStrings.count);
for(int idx = 0; idx < allLinedStrings.count; idx++)
{
NSString* currentPointString = [allLinedStrings objectAtIndex:idx];
NSArray* infos = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#","]];
if ([infos count] > 1)
{
NSString * latitude = [infos objectAtIndex:1];
NSString * longitude = [infos objectAtIndex:2];
NSString * Description =[infos objectAtIndex:3];
NSString * address = [infos objectAtIndex:4];
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
Location *annotation = [[Location alloc] initWithName:Description address:address coordinate:coordinate] ;
[mapview addAnnotation:annotation];
MKMapPoint point = MKMapPointForCoordinate(coordinate);
//
// adjust the bounding box
//
// if it is the first point, just use them, since we have nothing to compare to yet.
if (idx == 0) {
northEastPoint = point;
southWestPoint = point;
}
else
{
if (point.x > northEastPoint.x)
northEastPoint.x = point.x;
if(point.y > northEastPoint.y)
northEastPoint.y = point.y;
if (point.x < southWestPoint.x)
southWestPoint.x = point.x;
if (point.y < southWestPoint.y)
southWestPoint.y = point.y;
}
pointArr[idx] = point;
}
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:allLinedStrings.count];
_routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
[self.mapview addOverlay:self.routeLine];
}
}
}
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView
rendererForOverlay:(id < MKOverlay >)overlay
{
MKPolylineRenderer *renderer =
[[MKPolylineRenderer alloc] initWithPolyline:overlay];
renderer.strokeColor = [[UIColor orangeColor] colorWithAlphaComponent:1];
renderer.lineWidth = 6.0;
renderer.lineDashPattern = #[#2, #10];
renderer.alpha = 0.5;
return renderer;
}
Also, my csv file if its of any help
01,51.751782,-0.238992, Location 1, 1st Stop
02,51.815020,-0.200418, Location 2, 2nd Stop
03,51.755462,-0.340392, Location 3, 3rd Stop
04,51.660507,-0.389374, Location 4, 4th Stop
05,51.798323,-0.081622, Location 5, 5th Stop
An overlay is being added as each coordinate is added to the C array (before all the rest of the coordinates in the array are set). This causes the lines going to the northwest. Create and add the overlay after the loop.
Here's a summary of the current code with just the relevant parts pointed out:
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * allLinedStrings.count);
for(int idx = 0; idx < allLinedStrings.count; idx++)
{
//some code that gets "infos" (current coordinate's data)...
if ([infos count] > 1)
{
//some code that creates and adds an annotation...
MKMapPoint point = MKMapPointForCoordinate(coordinate);
//some code that calculates the "bounding box" (irrelevant)...
pointArr[idx] = point;
}
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:allLinedStrings.count];
//_routeRect = ... (irrelevant)
[self.mapview addOverlay:self.routeLine];
} //end of for-loop
Notice that the addOverlay call is inside the for-loop which means a polyline overlay is added for each coordinate.
But at each iteration, the pointArr array has not been fully populated yet:
When pointArr[0] is set to location 1, pointArr[1] to pointArr[4] are not set yet and contain either zero or random values.
When pointArr[1] is set to location 2, pointArr[2] to pointArr[4] are not set yet and contain either zero or random values, etc...
With the pointArr partially set like this at each coordinate/iteration, the polyline is being added resulting in the random lines going into the northwest.
Instead, create and add the polyline overlay once and after the for-loop and after pointArr is fully populated:
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * allLinedStrings.count);
for(int idx = 0; idx < allLinedStrings.count; idx++)
{
//some code that gets "infos" (current coordinate's data)...
if ([infos count] > 1)
{
//some code that creates and adds an annotation...
MKMapPoint point = MKMapPointForCoordinate(coordinate);
//some code that calculates the "bounding box" (irrelevant)...
pointArr[idx] = point;
}
//_routeRect = ... (irrelevant)
} //end of for-loop
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:allLinedStrings.count];
[self.mapview addOverlay:self.routeLine];
A few other points:
This malloc is technically wrong:
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * allLinedStrings.count);
It's defining an array of MKMapPoint structs but using the size of CLLocationCoordinate2D structs. Since you are intending to add MKMapPoint structs, you should use that struct's size instead:
MKMapPoint* pointArr = malloc(sizeof(MKMapPoint) * allLinedStrings.count);
The reason it still "works" the wrong way is because MKMapPoint and CLLocationCoordinate2D happen to be the same size.
You are allocating the C array with malloc but not freeing the memory. After pointArr is no longer needed, you should call free:
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:allLinedStrings.count];
[self.mapview addOverlay:self.routeLine];
free(pointArr);
Just FYI: Since you have CLLocationCoordinate2D coordinate values to begin with, you can just create the polyline using polylineWithCoordinates and save the trouble of converting them to MKMapPoints.
The bounding box calculation seems more complex than necessary. It's simpler to initialize the MKMapRect to MKMapRectNull and then add each annotation's MKMapPoint to it using MKMapRectUnion. See this answer for an example. Even simpler than that is to just call [self.mapview showAnnotations:self.mapview.annotations animated:YES]; after all the annotations are added.
How to draw an arrow between two points on the map?
I try calc latitude and longitude, but something goes wrong.
Best regards, Max
This is my code and result picture
int currpoints1 = 2;
NSLog(#"!!!!!!!!%d ",numPoints);
while(currpoints1 < numPoints)
{
TrackPoint* current = nil;
CLLocationCoordinate2D* coordsArrrow = malloc((3) * sizeof(CLLocationCoordinate2D));
for (int i =currpoints1, j=0; i < numPoints; i=i+1, j++)
{
current = [cashpoints objectAtIndex:i];
coordsArrrow[j] = current.coordinate;
if (i % 2 !=0) {
int Gug = 30;
int ug;
float bx, by, ex, ey;
bx = coordsArrrow[0].latitude;by = coordsArrrow[0].longitude;
ex = coordsArrrow[1].latitude;ey = coordsArrrow[1].longitude;
float Lstr = sqrt((ex-ey)*(ex-ey)+(bx-by)*(bx-by));
ug = [self RetGradW:(abs(coordsArrrow[1].latitude-coordsArrrow[0].latitude)) height:abs(coordsArrrow[1].longitude-coordsArrrow[0].longitude)];
ug = ug - Gug;
coordsArrrow[0].latitude = ex;
coordsArrrow[0].longitude = ey;
coordsArrrow[1].latitude = ex+Lstr*cos(ug*M_PI/180);
coordsArrrow[1].longitude = ey+Lstr*sin(ug*M_PI/180);
ug=ug+2*Gug;
coordsArrrow[2].latitude = ex+Lstr*cos(ug*M_PI/180);
coordsArrrow[2].longitude = ey+Lstr*sin(ug*M_PI/180);
MKPolyline *points = [MKPolyline polylineWithCoordinates:coordsArrrow count:3];
points.subtitle = #"arrow";
[map addOverlay:points];
break;
}
}
free(coordsArrrow);
currpoints1 = currpoints1 +14;
}
Oh guys. I change other way. Make annotation and rotate with direction between two point.
metods :
calc direction:
int currpoints1 = 2;
NSLog(#"!!!!!!!!%d ",numPoints);
while(currpoints1 < numPoints)
{
TrackPoint* current = nil;
CLLocationCoordinate2D* coordsArrrow = malloc((2) * sizeof(CLLocationCoordinate2D));
for (int i =currpoints1, j=0; i < numPoints; i=i+1, j++)
{
current = [cashpoints objectAtIndex:i];
coordsArrrow[j] = current.coordinate;
if (i % 2 !=0) {
DirectAnnotation *placemark=[[DirectAnnotation alloc] initWithCoordinate: coordsArrrow[0]];
CLLocationCoordinate2D coord1 = coordsArrrow[0];
CLLocationCoordinate2D coord2 = coordsArrrow[1];
CLLocationDegrees deltaLong = coord2.longitude - coord1.longitude;
CLLocationDegrees yComponent = sin(deltaLong) * cos(coord2.latitude);
CLLocationDegrees xComponent = (cos(coord1.latitude) * sin(coord2.latitude)) - (sin(coord1.latitude) * cos(coord2.latitude) * cos(deltaLong));
CLLocationDegrees radians = atan2(yComponent, xComponent);
CLLocationDegrees degrees = radiansToDegrees(radians) + 360;
self.dir = fmod(degrees, 360);
NSLog(#"%f,%f %f,%f",coordsArrrow[0].latitude,coordsArrrow[0].longitude,coordsArrrow[1].latitude,coordsArrrow[1].longitude);
[self.map addAnnotation:placemark];
break;
}
}
free(coordsArrrow);
currpoints1 = currpoints1 +14;
}
and
annotation:
- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation{
if ([annotation isKindOfClass:[DirectAnnotation class]]) {
arrow=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"parkingloc"];
arrow.image = [UIImage imageNamed:#"userLocationCompass.png"];
CGAffineTransform transform = CGAffineTransformMakeRotation(degreesToRadians(dir));
arrow.transform = transform;
return arrow;
}else {
return nil;
}}
But i have some problem
before zoom:
after zoom :
direction is confused.
Your arrows change the angle because you don't reuse them in viewForAnnotation correctly.
Just make some unique id for each arrow like this in viewForAnnotation:
routeAnnotationView = (MKAnnotationView *)[lmapView dequeueReusableAnnotationViewWithIdentifier:[NSString stringWithFormat:#"%f%f",annotation.coordinate.latitude, annotation.coordinate.longitude]];
if (!routeAnnotationView)
{
routeAnnotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:[NSString stringWithFormat:#"%f%f",annotation.coordinate.latitude, annotation.coordinate.longitude]];
UIImage *image = [self rotateImage:[UIImage imageNamed:#"arrpix11.png"] onDegrees:RADIANS_TO_DEGREES(curAngle)+90];
routeAnnotationView.image = image;
}
else
{
routeAnnotationView.annotation = annotation;
}
routeAnnotationView.canShowCallout = NO;
Use this for draw path between two location in Map.
http://code.google.com/p/ashiphone/downloads/detail?name=MapWithRoutes.zip&can=2&q=
I have a loop statement that displays my annotations on the mapView from an array. How do I check to see if any of the coordinates in the array are 0,0, and if so, remove/dont plot them?
Thanks.
Code:
CLLocationCoordinate2D maxCoord = {45.60250f,-122.39181f};
CLLocationCoordinate2D minCoord = {45.35697f,-123.12789f};
NSArray *callsArray = [xmlParser calls];
for (JointCAD *call in callsArray) {
NSString *callnumber = [call.callnumber stringByAppendingFormat:#". "];
NSString *callandnumber = [callnumber stringByAppendingString:call.currentCallType];
CLLocationCoordinate2D newCoord = { [call.latitude doubleValue], [call.longitude doubleValue]};
if ([call.longitude doubleValue] > maxCoord.longitude)
{
maxCoord.longitude = [call.longitude doubleValue];
}
if ([call.latitude doubleValue] > maxCoord.latitude)
{
maxCoord.latitude = [call.latitude doubleValue];
}
if ([call.longitude doubleValue] < minCoord.longitude)
{
minCoord.longitude = [call.longitude doubleValue];
}
if ([call.latitude doubleValue] < minCoord.latitude)
{
minCoord.latitude = [call.latitude doubleValue];
}
Annotation *ann = [[Annotation alloc] init];
ann.title = callandnumber;
ann.subtitle = [call location];
ann.coordinate = newCoord;
[mapView addAnnotation:ann];
}
MKCoordinateRegion region = {{0.0f, 0.0f}, {0.0f, 0.0f}};
region.center.longitude = (minCoord.longitude + maxCoord.longitude) / 2.0;
region.center.latitude = (minCoord.latitude + maxCoord.latitude) / 2.0;
region.span.longitudeDelta = (maxCoord.longitude - minCoord.longitude) * 1.1;
region.span.latitudeDelta = (maxCoord.latitude - minCoord.latitude) * 1.1;
[mapView regionThatFits:region];
[self.mapView setRegion:region animated:YES];
[self setRefreshState:#"Finished"];
Hm why don't you just add a continue in your for loop when the coordinates are equal 0?
Before creating the Annotation object just add a simple
if(newCoord.latitude == 0 && newCoord.longitude == 0) continue;
continue simply skips to the next iteration of a loop.
I have a mapview that has a lot of annotations, most of them are very close to each other. What I want to do is similar to the Photo app on iOS where annotations are grouped when they are too close to each other and whenever you zoom out, they un grouped if they are too far apart.
I have seen this question already but the answer given isn't really what I was looking for.
I'm looking for either a library or a algorithm that I could implement myself.
One of Apple's WWDC 2011 session videos shows how to do exactly this. Go to https://developer.apple.com/videos/wwdc/2011/ (must be registered developer) and scroll to the video titled "Visualizing Information Geographically with MapKit". The basic idea is to use an offscreen map view to hold all of your annotations, and copy them to the onscreen map view as needed, making sure that you're not trying to show too many at once. It even does the nifty animation with the annotations as you zoom.
Have a look here in order to see the full answer. It contains both implementations for MapKit and Google Maps. The code is inspired by WWDC 2011 video and works very well in my app.
I post the code for MapKit here anyway but there are a few useful remarks in my other answer.
- (void)didZoom:(UIGestureRecognizer*)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
[self updateVisibleAnnotations];
}
}
- (void)updateVisibleAnnotations {
static float marginFactor = 2.0f;
static float bucketSize = 50.0f;
MKMapRect visibleMapRect = [self.mapView visibleMapRect];
MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height);
CLLocationCoordinate2D leftCoordinate = [self.mapView convertPoint:CGPointZero toCoordinateFromView:self.view];
CLLocationCoordinate2D rightCoordinate = [self.mapView convertPoint:CGPointMake(bucketSize, 0) toCoordinateFromView:self.view];
double gridSize = MKMapPointForCoordinate(rightCoordinate).x - MKMapPointForCoordinate(leftCoordinate).x;
MKMapRect gridMapRect = MKMapRectMake(0, 0, gridSize, gridSize);
double startX = floor(MKMapRectGetMinX(adjustedVisibleMapRect) / gridSize) * gridSize;
double startY = floor(MKMapRectGetMinY(adjustedVisibleMapRect) / gridSize) * gridSize;
double endX = floor(MKMapRectGetMaxX(adjustedVisibleMapRect) / gridSize) * gridSize;
double endY = floor(MKMapRectGetMaxY(adjustedVisibleMapRect) / gridSize) * gridSize;
gridMapRect.origin.y = startY;
while(MKMapRectGetMinY(gridMapRect) <= endY) {
gridMapRect.origin.x = startX;
while (MKMapRectGetMinX(gridMapRect) <= endX) {
NSSet *allAnnotationsInBucket = [self.allAnnotationMapView annotationsInMapRect:gridMapRect];
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem) {
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
}
return shouldBeMerged;
}] mutableCopy];
NSSet *notMergedAnnotationsInBucket = [allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem) {
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
}
return isPointMapItem && !shouldBeMerged;
}];
for (PointMapItem *item in notMergedAnnotationsInBucket) {
[self.mapView addAnnotation:item];
}
if(filteredAnnotationsInBucket.count > 0) {
PointMapItem *annotationForGrid = (PointMapItem *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket];
[filteredAnnotationsInBucket removeObject:annotationForGrid];
annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects];
[self.mapView addAnnotation:annotationForGrid];
//force reload of the image because it's not done if annotationForGrid is already present in the bucket!!
MKAnnotationView* annotationView = [self.mapView viewForAnnotation:annotationForGrid];
NSString *imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 2, 8, 8)];
[countLabel setFont:[UIFont fontWithName:POINT_FONT_NAME size:10]];
[countLabel setTextColor:[UIColor whiteColor]];
[annotationView addSubview:countLabel];
imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
annotationView.image = [UIImage imageNamed:imageName];
if (filteredAnnotationsInBucket.count > 0){
[self.mapView deselectAnnotation:annotationForGrid animated:NO];
}
for (PointMapItem *annotation in filteredAnnotationsInBucket) {
[self.mapView deselectAnnotation:annotation animated:NO];
annotation.clusterAnnotation = annotationForGrid;
annotation.containedAnnotations = nil;
if ([visibleAnnotationsInBucket containsObject:annotation]) {
CLLocationCoordinate2D actualCoordinate = annotation.coordinate;
[UIView animateWithDuration:0.3 animations:^{
annotation.coordinate = annotation.clusterAnnotation.coordinate;
} completion:^(BOOL finished) {
annotation.coordinate = actualCoordinate;
[self.mapView removeAnnotation:annotation];
}];
}
}
}
gridMapRect.origin.x += gridSize;
}
gridMapRect.origin.y += gridSize;
}
}
- (id<MKAnnotation>)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations {
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSSet *annotationsForGridSet = [annotations objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL returnValue = ([visibleAnnotationsInBucket containsObject:obj]);
if (returnValue) {
*stop = YES;
}
return returnValue;
}];
if (annotationsForGridSet.count != 0) {
return [annotationsForGridSet anyObject];
}
MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMinX(gridMapRect), MKMapRectGetMidY(gridMapRect));
NSArray *sortedAnnotations = [[annotations allObjects] sortedArrayUsingComparator:^(id obj1, id obj2) {
MKMapPoint mapPoint1 = MKMapPointForCoordinate(((id<MKAnnotation>)obj1).coordinate);
MKMapPoint mapPoint2 = MKMapPointForCoordinate(((id<MKAnnotation>)obj2).coordinate);
CLLocationDistance distance1 = MKMetersBetweenMapPoints(mapPoint1, centerMapPoint);
CLLocationDistance distance2 = MKMetersBetweenMapPoints(mapPoint2, centerMapPoint);
if (distance1 < distance2) {
return NSOrderedAscending;
}
else if (distance1 > distance2) {
return NSOrderedDescending;
}
return NSOrderedSame;
}];
return [sortedAnnotations objectAtIndex:0];
}