If the 4 points do not represent rectangle or a circle but any 4 points on the map you will need to create the system yourself. For a point to be inside a shape represented by 4 oriented points it is easiest to check the cross product between the center and each sequential pair of bounds points. All results must be positive for clock wise order for the point to be inside the bounds. The second thing is you need to convert the coordinates to cartesian system and orient them... Let the code speak for itself:
- (double)crossProductZCoordinateForCenter:(CLLocationCoordinate2D)center left:(CLLocationCoordinate2D)left right:(CLLocationCoordinate2D)right {
CLLocationCoordinate2D A = CLLocationCoordinate2DMake(left.latitude-center.latitude, left.longitude-center.longitude);
CLLocationCoordinate2D B = CLLocationCoordinate2DMake(right.latitude-center.latitude, right.longitude-center.longitude);
return B.latitude*A.longitude - A.latitude*B.longitude;
}
- (BOOL)isCartesianCoordinate:(CLLocationCoordinate2D)coordinate insideBounds:(Bounds)bounds {
// Now we will break the system into 4 triangles and check their orientation by using a z component of the cross product. If one of them if negative the coordinate is not inside the region
if([self crossProductZCoordinateForCenter:coordinate left:bounds.southWest right:bounds.northWest] < .0) return NO;
if([self crossProductZCoordinateForCenter:coordinate left:bounds.northWest right:bounds.northEast] < .0) return NO;
if([self crossProductZCoordinateForCenter:coordinate left:bounds.northEast right:bounds.southEast] < .0) return NO;
if([self crossProductZCoordinateForCenter:coordinate left:bounds.southEast right:bounds.southWest] < .0) return NO;
return YES;
}
- (BOOL)isCoordinate:(CLLocationCoordinate2D)coordinate insideBounds:(Bounds)bounds {
// We may treat the coordinates as cartesian but east should always be larger then west and north should be larger then south
// Those smaller must be increased by 360 degrees
if(bounds.southEast.latitude < bounds.southWest.latitude) bounds.southEast.latitude += 360.0;
if(bounds.northEast.latitude < bounds.northWest.latitude) bounds.northEast.latitude += 360.0;
if(bounds.northEast.longitude < bounds.southEast.longitude) bounds.northEast.longitude += 360.0;
if(bounds.northWest.longitude < bounds.southWest.longitude) bounds.northWest.longitude += 360.0;
// Check if any of the combination coordinates are cartesicly inside the bounds
// We need to increase the longitude and the latitude by 360 and check all 4 combinations
if([self isCartesianCoordinate:coordinate insideBounds:bounds]) return YES;
if([self isCartesianCoordinate:CLLocationCoordinate2DMake(coordinate.latitude+360.0, coordinate.longitude) insideBounds:bounds]) return YES;
if([self isCartesianCoordinate:CLLocationCoordinate2DMake(coordinate.latitude, coordinate.longitude+360.0) insideBounds:bounds]) return YES;
if([self isCartesianCoordinate:CLLocationCoordinate2DMake(coordinate.latitude+360.0, coordinate.longitude+360.0) insideBounds:bounds]) return YES;
return NO;
}
And some simple tests might come in handy:
- (void)resampleCoordinate:(CLLocationCoordinate2D *)coordinate {
if(coordinate->latitude < -180.0) coordinate->latitude += 360.0;
if(coordinate->latitude > 180.0) coordinate->latitude -= 360.0;
if(coordinate->longitude < -180.0) coordinate->longitude += 360.0;
if(coordinate->longitude > 180.0) coordinate->longitude -= 360.0;
}
- (void)testLocationSystem {
NSInteger numberOfTests = 0;
NSInteger numberOfTestsCheckedOut = 0;
for(double latitude = -180.0; latitude <= 180.0; latitude++) {
for(double longitude = -180.0; longitude <= 180.0; longitude++) {
Bounds bounds;
bounds.southWest = CLLocationCoordinate2DMake(latitude-15.0, longitude-15.0);
bounds.northEast = CLLocationCoordinate2DMake(latitude+15.0, longitude+15.0);
bounds.northWest = CLLocationCoordinate2DMake(latitude-15.0, longitude+15.0);
bounds.southEast = CLLocationCoordinate2DMake(latitude+15.0, longitude-15.0);
[self resampleCoordinate:&(bounds.northWest)];
[self resampleCoordinate:&(bounds.northEast)];
[self resampleCoordinate:&(bounds.southEast)];
[self resampleCoordinate:&(bounds.southWest)];
numberOfTests++;
BOOL success = [self isCoordinate:CLLocationCoordinate2DMake(latitude, longitude) insideBounds:bounds];
if(success) {
numberOfTestsCheckedOut++;
}
else {
NSLog(@"Failed");
}
}
}
NSLog(@"%d/%d succeeded", (int)numberOfTestsCheckedOut, (int)numberOfTests);
}
- (void)testLocationFailSystem {
NSInteger numberOfTests = 0;
NSInteger numberOfTestsCheckedOut = 0;
for(double latitude = -180.0; latitude <= 180.0; latitude++) {
for(double longitude = -180.0; longitude <= 180.0; longitude++) {
Bounds bounds;
bounds.southWest = CLLocationCoordinate2DMake(latitude-15.0, longitude-15.0);
bounds.northEast = CLLocationCoordinate2DMake(latitude+15.0, longitude+15.0);
bounds.northWest = CLLocationCoordinate2DMake(latitude-15.0, longitude+15.0);
bounds.southEast = CLLocationCoordinate2DMake(latitude+15.0, longitude-15.0);
[self resampleCoordinate:&(bounds.northWest)];
[self resampleCoordinate:&(bounds.northEast)];
[self resampleCoordinate:&(bounds.southEast)];
[self resampleCoordinate:&(bounds.southWest)];
for(double angle = .0f; angle < M_PI; angle+=M_PI/10.0) {
CLLocationCoordinate2D coordiunate = CLLocationCoordinate2DMake(latitude+cos(angle)*20.0, longitude+sin(angle)*20.0);
[self resampleCoordinate:&coordiunate];
numberOfTests++;
BOOL success = [self isCoordinate:coordiunate insideBounds:bounds]; // must fail
if(success == NO) {
numberOfTestsCheckedOut++;
}
else {
NSLog(@"Failed");
}
}
}
}
NSLog(@"%d/%d succeeded", (int)numberOfTestsCheckedOut, (int)numberOfTests);
}
These all pass for me. If you find a situation not working but should or other way around please contact me.