How to check if MKCoordinateRegion contains CLLocationCoordinate2D without using MKMapView?
Solution 1
In case there is anybody else confused with latitudes and longitues, here is tested, working solution:
MKCoordinateRegion region = self.mapView.region;
CLLocationCoordinate2D location = user.gpsposition.coordinate;
CLLocationCoordinate2D center = region.center;
CLLocationCoordinate2D northWestCorner, southEastCorner;
northWestCorner.latitude = center.latitude - (region.span.latitudeDelta / 2.0);
northWestCorner.longitude = center.longitude - (region.span.longitudeDelta / 2.0);
southEastCorner.latitude = center.latitude + (region.span.latitudeDelta / 2.0);
southEastCorner.longitude = center.longitude + (region.span.longitudeDelta / 2.0);
if (
location.latitude >= northWestCorner.latitude &&
location.latitude <= southEastCorner.latitude &&
location.longitude >= northWestCorner.longitude &&
location.longitude <= southEastCorner.longitude
)
{
// User location (location) in the region - OK :-)
NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| IN!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude);
}else {
// User location (location) out of the region - NOT ok :-(
NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| OUT!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude);
}
Solution 2
I'm posting this answer as the accepted solution is not valid in my opinion. This answer is also not perfect but it handles the case when coordinates wrap around 360 degrees boundaries, which is enough to be suitable in my situation.
+ (BOOL)coordinate:(CLLocationCoordinate2D)coord inRegion:(MKCoordinateRegion)region
{
CLLocationCoordinate2D center = region.center;
MKCoordinateSpan span = region.span;
BOOL result = YES;
result &= cos((center.latitude - coord.latitude)*M_PI/180.0) > cos(span.latitudeDelta/2.0*M_PI/180.0);
result &= cos((center.longitude - coord.longitude)*M_PI/180.0) > cos(span.longitudeDelta/2.0*M_PI/180.0);
return result;
}
Solution 3
You can convert your location to a point with MKMapPointForCoordinate
, then use MKMapRectContainsPoint
on the mapview's visibleMapRect
. This is completely off the top of my head. Let me know if it works.
Solution 4
The other answers all have faults. The accepted answer is a little verbose, and fails near the international dateline. The cosine answer is workable, but fails for very small regions (because delta cosine is sine which tends towards zero near zero, meaning for smaller angular differences we expect zero change) This answer should work correctly for all situations, and is simpler.
Swift:
/* Standardises and angle to [-180 to 180] degrees */
class func standardAngle(var angle: CLLocationDegrees) -> CLLocationDegrees {
angle %= 360
return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle
}
/* confirms that a region contains a location */
class func regionContains(region: MKCoordinateRegion, location: CLLocation) -> Bool {
let deltaLat = abs(standardAngle(region.center.latitude - location.coordinate.latitude))
let deltalong = abs(standardAngle(region.center.longitude - location.coordinate.longitude))
return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong
}
Objective C:
/* Standardises and angle to [-180 to 180] degrees */
+ (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle {
angle %= 360
return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle
}
/* confirms that a region contains a location */
+ (BOOL)region:(MKCoordinateRegion*)region containsLocation:(CLLocation*)location {
CLLocationDegrees deltaLat = fabs(standardAngle(region.center.latitude - location.coordinate.latitude))
CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude))
return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong
}
This method fails for regions that include either pole though, but then the coordinate system itself fails at the poles. For most applications, this solution should suffice. (Note, not tested on Objective C)
Solution 5
I've used this code to determine if a coordinate is within a circular region (a coordinate with a radius around it).
- (BOOL)location:(CLLocation *)location isNearCoordinate:(CLLocationCoordinate2D)coordinate withRadius:(CLLocationDistance)radius
{
CLCircularRegion *circularRegion = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:radius identifier:@"radiusCheck"];
return [circularRegion containsCoordinate:coordinate];
}
Lukasz
Updated on February 22, 2020Comments
-
Lukasz over 4 years
I need to check if user location belongs to the MKCoordinateRegion. I was surprised not to find simple function for this, something like: CGRectContainsCGPoint(rect, point).
I found following piece of code:
CLLocationCoordinate2D topLeftCoordinate = CLLocationCoordinate2DMake(region.center.latitude + (region.span.latitudeDelta/2.0), region.center.longitude - (region.span.longitudeDelta/2.0)); CLLocationCoordinate2D bottomRightCoordinate = CLLocationCoordinate2DMake(region.center.latitude - (region.span.latitudeDelta/2.0), region.center.longitude + (region.span.longitudeDelta/2.0)); if (location.latitude < topLeftCoordinate.latitude || location.latitude > bottomRightCoordinate.latitude || location.longitude < bottomRightCoordinate.longitude || location.longitude > bottomRightCoordinate.longitude) { // Coordinate fits into the region }
But, I am not sure if it is accurate as documentation does not specify exactly how the region rectangle is calculated.
There must be simpler way to do it. Have I overlooked some function in the MapKit framework documentation?
-
Lukasz about 12 yearsIt feels completely overwhelming to initialize whole MKMapView and set it up just for such a simple check. I need to calculate this outside any view controller.
-
nevan king about 12 yearsSorry, I thought you were working with a mapview already in place. If you only have that region, you'll have to rely on it to be accurate. Why do you think the region is no good? Where did you get the region from?
-
Lukasz about 12 yearsThe region is OK. I am just not sure if I am checking against it correctly. The documentation of MKCoordinateRegion does not specify exactly how the latitude and longitude spans constructs area rectangle.
-
nevan king about 12 yearsThe way you're doing it is fine. The region specifies a span which is in degrees, the same as latitude and longitude. They convert directly. I'm not quite sure about the logic of your if statement. Shouldn't they be
&&
instead of||
? -
lichen19853 about 11 yearsI doubt this would work: 1. why should location.latitude >= northWestCorner.latitude? Shouldn't it be sounthEastCorner.latitude? 2. What if calculated minimum longitude is -2.0, maximum longitude is 2.0, and your location.longitude is 359.0?
-
james Burns over 10 yearsThis appears to be a great, simple solution. I have to test more, but may be the best solution to this tricky problem.
-
MarekR about 10 years@lichen19853 is right that it will fail when tested around 360 degrees. See my answer below for a slightly more correct solution.
-
Brainware over 8 yearsThis should be the accepted solution. The cos() function takes care of the 0 to 360 degree issue. Even though it performs a non-linear scale on the distance, it is compared to an equally scaled delta, so it works like a charm.
-
Verticon almost 8 yearsIn the standardAngle method, for the case where the normalized angle > 180: shouldn't the return value be 360 - angle instead of 360 - 180?
-
Verticon almost 7 yearsThis worked for me for a small, well defined rectangular region in my city. I cannot attest to the general case.
-
Nikita Ivaniushchenko about 6 yearsYou should use span.delta/2 when comparing to latitude or longitude.
-
Nikita Ivaniushchenko about 6 yearsUse region.span.latitudeDelta/2 and region.span.longitudeDelta in last line