Skip to content

Instantly share code, notes, and snippets.

@jmcd
Last active October 4, 2021 19:32
Show Gist options
  • Save jmcd/4502302 to your computer and use it in GitHub Desktop.
Save jmcd/4502302 to your computer and use it in GitHub Desktop.
Calculate a new coordinate given a starting coordinate, bearing and distance. Objective-C
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "AnnotationCoordinateUtility.h"
@implementation AnnotationCoordinateUtility {
}
+ (void)mutateCoordinatesOfClashingAnnotations:(NSArray *)annotations {
NSDictionary *coordinateValuesToAnnotations = [self groupAnnotationsByLocationValue:annotations];
for (NSValue *coordinateValue in coordinateValuesToAnnotations.allKeys) {
NSMutableArray *outletsAtLocation = coordinateValuesToAnnotations[coordinateValue];
if (outletsAtLocation.count > 1) {
CLLocationCoordinate2D coordinate;
[coordinateValue getValue:&coordinate];
[self repositionAnnotations:outletsAtLocation toAvoidClashAtCoordination:coordinate];
}
}
}
+ (NSDictionary *)groupAnnotationsByLocationValue:(NSArray *)annotations {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (id<MKAnnotation> pin in annotations) {
CLLocationCoordinate2D coordinate = pin.coordinate;
NSValue *coordinateValue = [NSValue valueWithBytes:&coordinate objCType:@encode(CLLocationCoordinate2D)];
NSMutableArray *annotationsAtLocation = result[coordinateValue];
if (!annotationsAtLocation) {
annotationsAtLocation = [NSMutableArray array];
result[coordinateValue] = annotationsAtLocation;
}
[annotationsAtLocation addObject:pin];
}
return result;
}
+ (void)repositionAnnotations:(NSMutableArray *)annotations toAvoidClashAtCoordination:(CLLocationCoordinate2D)coordinate {
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 <MKAnnotation> annotation = annotations[i];
annotation.coordinate = newCoordinate;
}
}
+ (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;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment