Handling MKMapView Annotation Pins on the Same Coordinate
There is now an updated version of this post that reworks the solution from Objective-C to Swift 2.0.
A component of a project I am working on displays shop locations on a map. A problem arises when the shops are located at a shopping centre or mall – the shops are invariably geo-coded to the same latitude-longitude coordinates.
When the shops have the same coordinates, the annotations (pins) display in the exact same location on the map. This gives the appearance of there only being one pin, and indeed, the user can only tap one pin.
To overcome this, we implemented a routine to re-place the pins at new coordinates surrounding the contested coordinate.
First we group the annotations by coordinate.
for (id pin in annotations) {
CLLocationCoordinate2D coordinate = pin.coordinate;
NSValue *coordinateValue = [NSValue valueWithBytes:&coordinate objCType:@encode(CLLocationCoordinate2D)];
NSMutableArray *annotationsAtLocation = coordinateValuesToAnnotations[coordinateValue];
if (!annotationsAtLocation) {
annotationsAtLocation = [NSMutableArray array];
coordinateValuesToAnnotations[coordinateValue] = annotationsAtLocation;
}
[annotationsAtLocation addObject:pin];
}
This routine produces a dictionary keyed on NSValue
s containing a copy of the coordinate (you can’t key a NSDictionary
on a c-type). The value of a entry in the dictionary is a NSArray
of annotations at that coordinate.
You can see this only matches on exactly equal coordinates, but it would be relatively straightforward to group on coordinates that were close by calculating the distance between them.
Next we enumerate the dictionary looking for locations that have more than one annotation. When we find one, we reposition the annotations.
double distance = 3 * annotations.count / 2.0;
double radiansBetweenAnnotations = (M_PI * 2) / annotations.count;
for (int i = 0; i < annotations.count; i++) {
double heading = radiansBetweenAnnotations * i;
CLLocationCoordinate2D newCoordinate = [self calculateCoordinateFrom:coordinate onBearing:heading atDistance:distance];
id annotation = annotations[i];
annotation.coordinate = newCoordinate;
}
The pins are arranged in a circle around the contested point by dividing the circle by the number of contesting annotations. You can see that the distance from the contested coordinate to the new coordinate is a function of the number of annotations contesting – if there are few pins contesting the coordinate, then we have space to place the pins close to the coordinate.
MKMapView Annotation Pins
Finally, the new coordinate is calculated using an implementation of the function from this excellent resource: Destination point given distance and bearing from start point
+ (CLLocationCoordinate2D)calculateCoordinateFrom:(CLLocationCoordinate2D)coordinate onBearing:(double)bearingInRadians atDistance:(double)distanceInMetres {
double coordinateLatitudeInRadians = coordinate.latitude * M_PI / 180;
double coordinateLongitudeInRadians = coordinate.longitude * M_PI / 180;
double distanceComparedToEarth = distanceInMetres / 6378100;
double resultLatitudeInRadians = asin(sin(coordinateLatitudeInRadians) * cos(distanceComparedToEarth) + cos(coordinateLatitudeInRadians) * sin(distanceComparedToEarth) * cos(bearingInRadians));
double resultLongitudeInRadians = coordinateLongitudeInRadians + atan2(sin(bearingInRadians) * sin(distanceComparedToEarth) * cos(coordinateLatitudeInRadians), cos(distanceComparedToEarth) - sin(coordinateLatitudeInRadians) * sin(resultLatitudeInRadians));
CLLocationCoordinate2D result;
result.latitude = resultLatitudeInRadians * 180 / M_PI;
result.longitude = resultLongitudeInRadians * 180 / M_PI;
return result;
}
Search
Categories
- Artificial Intelligence
- Cloud Services
- Design
- Development
- Digital Health
- Digital Marketing
- Digital Transformation
- Environment
- Life at Storm
- UX
Archives
Subscribe to Email Updates
Subscribe
Categories
- Artificial Intelligence
- Cloud Services
- Design
- Development
- Digital Health
- Digital Marketing
- Digital Transformation
- Environment
- Life at Storm
- UX
Archives
Subscribe to Email Updates
Subscribe
- Artificial Intelligence
- Cloud Services
- Design
- Development
- Digital Health
- Digital Marketing
- Digital Transformation
- Environment
- Life at Storm
- UX
Archives
Subscribe to Email Updates
SubscribeWe are a digital transformation consultancy. We help our clients succeed.
View Services