how to draw an arrow between two points on the map? MapKit - ios

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=

Related

MKPolyline detect self-intersecting line OBJECTIVE C

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);
}

Remove specifc pin from map Google Maps xcode

I have an array which stores data from webservice. The data contains multiple locations.
The array updates when the map center is moved. It shows pins based on map center. What i want to do is to remove those old pins that i have allotted on the map without removing all the markers and if new pins are stored, allot them without adding all of them again.
Thanks in advance.
Here is my code.
if ([tempOLDArr count]>0) {
for (int i = 0; i<[appdelegate.eventsArray count]; i++) {
eventsWebService *temp = [appdelegate.eventsArray objectAtIndex:i];
if (i<[tempOLDArr count]) {
if ([temp.eventID isEqualToString:[[tempOLDArr objectAtIndex:i]eventID]]) {
}
else {
[tempNEWArr addObject:[appdelegate.eventsArray objectAtIndex:i] ];
}
}
else if(i>=[tempOLDArr count]){
[tempNEWArr addObject:[appdelegate.eventsArray objectAtIndex:i] ];
}
}
if ([tempNEWArr count]>0) {
[self creatingAnnotation:tempNEWArr];
}
}
else {
tempOLDArr = appdelegate.eventsArray;
[self creatingAnnotation:tempOLDArr];
}
And here is my code for plotting those markers
-(void)creatingAnnotation:(NSArray *) eventsArr{
for(int i=0;i<[eventsArr count];i++)
{
HomeEventLat = [[appdelegate.eventsArray objectAtIndex:i]eventLat];
HomeEventlong = [[appdelegate.eventsArray objectAtIndex:i]eventLong];
double pickuplat = [HomeEventLat doubleValue];
double pickuplng = [HomeEventlong doubleValue];
CLLocation * loca=[[CLLocation alloc]initWithLatitude:pickuplat longitude:pickuplng];
CLLocationCoordinate2D coordi=loca.coordinate;
eventMarker = [[CustomGoogleMarker alloc]init];
eventMarker.position = CLLocationCoordinate2DMake(pickuplat, pickuplng);
eventMarker = [CustomGoogleMarker markerWithPosition:coordi];
eventMarker.snippet = NO;
eventMarker.icon = [UIImage imageNamed:#"map-pin-icon"];
eventMarker.tappable = YES;
eventMarker.marker_description = [[appdelegate.eventsArray objectAtIndex:i]eventDetail];
eventMarker.marker_distance = [[appdelegate.eventsArray objectAtIndex:i]eventdistance];
eventMarker.markerID = [[appdelegate.eventsArray objectAtIndex:i]eventID];
eventMarker.marker_joinedNo = [[appdelegate.eventsArray objectAtIndex:i]eventJoinedNo];
eventMarker.marker_price = [[appdelegate.eventsArray objectAtIndex:i]eventPrice];
eventMarker.marker_commentsNo = [[appdelegate.eventsArray objectAtIndex:i]eventCommentsNo];
eventMarker.marker_address = [[appdelegate.eventsArray objectAtIndex:i]eventAddress];
eventMarker.ID = [NSString stringWithFormat:#"%d",i];
// eventMarker.appearAnimation = kGMSMarkerAnimationPop;
NSString *markerLabels = [[appdelegate.eventsArray objectAtIndex:i]labelsList];
NSLog(#"%#",markerLabels);
eventMarker.marker_label = markerLabels;
eventMarker.marker_username = [[appdelegate.eventsArray objectAtIndex:i]user];
eventMarker.marker_time = [[appdelegate.eventsArray objectAtIndex:i]eventTime];
eventMarker.marker_image = [[appdelegate.eventsArray objectAtIndex:i]eventimage];
// eventMarker.title = [[appdelegate.eventsArray objectAtIndex:i]eventsID];
//
// [self.mapView clear];
eventMarker.map = self.mapView;
//
//
[markerDetail addObject:[appdelegate.eventsArray objectAtIndex:i]];
}
[_mapView animateWithCameraUpdate:[GMSCameraUpdate fitBounds:bounds withPadding:10]];
}

How to add Coordinates to CLLocationCoordinate2D from Dynamic NSMutableArray?

I have a MapView in which I would like to add annotations and a route from defined coordinates that I add from a Textfield and store in an NSMutableArray.
Now I'm able to show the route from multiple coordinates but only when I insert them in my code as follow :
-(void)loadMap{
int Coordinates;
//MAP
CLLocationCoordinate2D coordinateArray[Coordinates];
coordinateArray[0] = CLLocationCoordinate2DMake(LatA, LongA);
coordinateArray[1] = CLLocationCoordinate2DMake(LatB, LongB);
coordinateArray[2] = CLLocationCoordinate2DMake(LatC, LongC);
coordinateArray[3] = CLLocationCoordinate2DMake(LatD, LongD);
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:Coordinates];
[MapViewHome setVisibleMapRect:[self.routeLine boundingMapRect]]; //If you want the route to be visible
[MapViewHome addOverlay:self.routeLine];
MapViewHome.mapType = MKMapTypeHybrid;
[self zoomToFitMapAnnotations:MapViewHome];
}
To Add Annotations I do this:
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if(overlay == self.routeLine)
{
if(nil == self.routeLineView)
{
self.routeLineView = [[MKPolylineView alloc] initWithPolyline:self.routeLine];
self.routeLineView.fillColor = [UIColor purpleColor];
self.routeLineView.strokeColor = [UIColor purpleColor];
self.routeLineView.lineWidth = 5;
}
return self.routeLineView;
}
return nil;
}
-(void)AddAnotations{
DeparturePoint = [[MKPointAnnotation alloc] init];
DeparturePoint.coordinate = CLLocationCoordinate2DMake(LatA, LongA);
DeparturePoint.title = [NSString stringWithFormat:#"A"];
[MapViewHome addAnnotation:DeparturePoint];
ArrivalPoint = [[MKPointAnnotation alloc] init];
ArrivalPoint.coordinate = CLLocationCoordinate2DMake(LatB, LongB);
ArrivalPoint.title = [NSString stringWithFormat:#"B"];
[MapViewHome addAnnotation:ArrivalPoint];
C = [[MKPointAnnotation alloc] init];
C.coordinate = CLLocationCoordinate2DMake(LatC, LongC);
C.title = [NSString stringWithFormat:#"C"];
[MapViewHome addAnnotation:C];
D = [[MKPointAnnotation alloc] init];
D.coordinate = CLLocationCoordinate2DMake(LatD, LongD);
D.title = [NSString stringWithFormat:#"D"];
[MapViewHome addAnnotation:D];
}
NOW I would like to get Insert my Dynamic NSMutableArray in the LoadMap function in order to refresh the mapView and get a longer Route ! Any Idea ?
Here's the first solution I could think of...
First we wrap the CLLocationCoordinate2D into an object. For this I've made a wrapper class called KBLocationWrapper. Here's the interface:
#interface KBLocationWrapper : NSObject
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#end
Next generate the NSMutableArray ...
NSMutableArray *locationCoordinatesArray = [NSMutableArray array];
Then add each coordinate to the array via the object wrapper...
KBLocationWrapper *locationWrapper = [[KBLocationWrapper alloc] init];
locationWrapper.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
[locationCoordinatesArray addObject:locationWrapper];
Finally, figure out how you're going to get the locationCoordinatesArray into the -loadMap method, and then loop through each object and map the coordinate property to its respective place in coordinateArray... (I would write a separate method for this functionality, but for demonstration purposes it's going straight into -loadMap)
-(void)loadMap{
....
int Coordinates = (int)[locationCoordinatesArray count];
CLLocationCoordinate2D coordinateArray[Coordinates];
// loop through coordinates
for (int i = 0; i < Coordinates; ++i) {
// write data from the CLLocationCoordinate2D stored in the wrapper
// to the primitive data array 'coordinateArray'
coordinateArray[i] = [locationCoordinatesArray[i] coordinate];
}
// then generate the routeLine.
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:Coordinates];
...
}
I add an object to NSMutableArray "NSLat" when i Unwind from another ViewController:
- (IBAction)UnwindPoiint:(UIStoryboardSegue *)segue {
float latitude;
float longitude;
NSString*PointName;
NSString*coordinates;
AddPointViewController *messageViewController = segue.sourceViewController;
PointName = messageViewController.PointBack;
latitude = [messageViewController.PointLatitude floatValue];
longitude = [messageViewController.PointLongitude floatValue];
KBLocationWrapper *locationWrapper = [[KBLocationWrapper alloc] init];
locationWrapper.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
[NSLat addObject:locationWrapper];
coordinates = [NSString stringWithFormat:#"%f,%f,%#",latitude,longitude,PointName];
// [NSLat addObject:coordinates];
[self AddAnotations];
[self loadMap];
[FlightLogTable reloadData];
NSLog(#"%#",coordinates);
NSLog(#"%lu",(unsigned long)NSLat.count);
}
And I add Annotations & load Map as filed :
-(void)AddAnotations{
for(int idx = 0; idx < NSLat.count; idx++)
{
// break the string down even further to latitude and longitude fields.
NSString* currentPointString = [NSLat objectAtIndex:idx];
NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#","]];
CLLocationDegrees latitude = [[latLonArr objectAtIndex:0] doubleValue];
CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
NSString*Name = [NSString stringWithFormat:#"%#",[latLonArr objectAtIndex:2]];
DeparturePoint = [[MKPointAnnotation alloc] init];
DeparturePoint.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
DeparturePoint.title = Name;
[MapViewHome addAnnotation:DeparturePoint];
}
[self loadMap];
}
-(void)zoomToFitMapAnnotations:(MKMapView*)mapView
{
if([mapView.annotations count] == 0)
return;
CLLocationCoordinate2D topLeftCoord;
topLeftCoord.latitude = -90;
topLeftCoord.longitude = 180;
CLLocationCoordinate2D bottomRightCoord;
bottomRightCoord.latitude = 90;
bottomRightCoord.longitude = -180;
for(MKPointAnnotation*annotation in MapViewHome.annotations)
{
topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude);
topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude);
bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude);
bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude);
}
MKCoordinateRegion region;
region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5;
region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5;
region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 2; // Add a little extra space on the sides
region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 2; // Add a little extra space on the sides
region = [mapView regionThatFits:region];
[MapViewHome setRegion:region animated:YES];
}
-(void)loadMap{
int Coordinates = (int)[NSLat count];
CLLocationCoordinate2D coordinateArray[Coordinates];
// loop through coordinates
for (int i = 0; i < Coordinates; ++i) {
// write data from the CLLocationCoordinate2D stored in the wrapper
// to the primitive data array 'coordinateArray'
coordinateArray[i] = [NSLat[i] coordinate];
}
// then generate the routeLine.
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:Coordinates];
[MapViewHome setVisibleMapRect:[self.routeLine boundingMapRect]]; //If you want the route to be visible
[MapViewHome addOverlay:self.routeLine];
MapViewHome.mapType = MKMapTypeHybrid;
[self zoomToFitMapAnnotations:MapViewHome];
}

unable to draw mkpolyline

I have used MKMapView to show map and current user location which is working correctly.
Now I want to draw a polyline as user moves but it is not working I tried follwing code:
for(int i = 0; i < [longarray count]; i++)
{
NSNumber *latt=[latarray objectAtIndex:i];
NSNumber *lonn=[longarray objectAtIndex:i];
sklat =[[NSString stringWithFormat:#"%#",latt]doubleValue];
sklongi =[[NSString stringWithFormat:#"%#",lonn]doubleValue];
CLLocationCoordinate2D coordinate1 = CLLocationCoordinate2DMake(sklat,sklongi);
// break the string down even further to latitude and longitude fields.
MKMapPoint point = MKMapPointForCoordinate(coordinate1);
// if it is the first point, just use them, since we have nothing to compare to yet.
pointsArray[i] = point;
}
self.routeLine = [MKPolyline polylineWithPoints:pointsArray count:[latarray count]];
free(pointsArray);
[mapview addOverlay:self.routeLine];
then i usedd overlay function as
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKOverlayView* overlayView = nil;
if(overlay == self.routeLine)
{
routeLineView = [[MKPolylineView alloc] initWithPolyline:self.routeLine] ;
routeLineView.fillColor = [UIColor colorWithRed:0.000 green:5.100 blue:0.100 alpha:1];
routeLineView.strokeColor = [UIColor colorWithRed:0.000 green:5.100 blue:0.100 alpha:1];
routeLineView.lineWidth = 4;
overlayView = routeLineView;
}
return overlayView;
}

Grouped/Ungroup MKAnnotation depending on the zoom level (And keep it fast)

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];
}

Resources