How to check if MKCoordinateRegion contains CLLocationCoordinate2D without using MKMapView?

后端 未结 9 801
天涯浪人
天涯浪人 2020-12-13 05:08

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,

相关标签:
9条回答
  • 2020-12-13 05:18

    Owen Godfrey, the objective-C code doesn´t work, this is the good code: Fails on Objective-C, this is the good code:

    /* Standardises and angle to [-180 to 180] degrees */
    - (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle {
        angle=fmod(angle,360);
        return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle;
    }
    
    -(BOOL)thisRegion:(MKCoordinateRegion)region containsLocation:(CLLocation *)location{
        CLLocationDegrees deltaLat =fabs([self standardAngle:(region.center.latitude-location.coordinate.latitude)]);
        CLLocationDegrees deltaLong =fabs([self standardAngle:(region.center.longitude-location.coordinate.longitude)]);
        return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >=deltaLong;
    }
        CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude));
        return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong;
    }
    

    Thanks!

    0 讨论(0)
  • 2020-12-13 05:19

    I had problem with same calculations. I like conception proposed by Owen Godfrey here, bun even Fernando here missed the fact that latitude is wraped diferently than longitude and has diferent range. To clarify my proposal I post it with tests so you can check it out by your self.

    import XCTest
    import MapKit
    
    // MARK - The Solution
    
    extension CLLocationDegrees {
    
        enum WrapingDimension: Double {
            case latitude = 180
            case longitude = 360
        }
    
        /// Standardises and angle to [-180 to 180] or [-90 to 90] degrees
        func wrapped(diemension: WrapingDimension) -> CLLocationDegrees {
            let length = diemension.rawValue
            let halfLenght = length/2.0
            let angle = self.truncatingRemainder(dividingBy: length)
            switch diemension {
            case .longitude:
                //        return angle < -180.0 ? 360.0 + angle : angle > 180.0 ? -360.0 + angle : angle
                return angle < -halfLenght ? length + angle : angle > halfLenght ? -length + angle : angle
            case .latitude:
                //        return angle < -90.0 ? -180.0 - angle : angle > 90.0 ? 180.0 - angle : angle
                return angle < -halfLenght ? -length - angle : angle > halfLenght ? length - angle : angle
            }
        }
    }
    
    extension MKCoordinateRegion {
        /// confirms that a region contains a location
        func contains(_ coordinate: CLLocationCoordinate2D) -> Bool {
            let deltaLat = abs((self.center.latitude - coordinate.latitude).wrapped(diemension: .latitude))
            let deltalong = abs((self.center.longitude - coordinate.longitude).wrapped(diemension: .longitude))
            return self.span.latitudeDelta/2.0 >= deltaLat && self.span.longitudeDelta/2.0 >= deltalong
        }
    }
    
    // MARK - Unit tests
    
    class MKCoordinateRegionContaingTests: XCTestCase {
    
        func testRegionContains() {
            var region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 0), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
            var coords = CLLocationCoordinate2DMake(0, 0)
            XCTAssert(region.contains(coords))
    
            coords = CLLocationCoordinate2DMake(0.5, 0.5)
            XCTAssert(region.contains(coords))
    
            coords = CLLocationCoordinate2DMake(-0.5, 0.5)
            XCTAssert(region.contains(coords))
            coords = CLLocationCoordinate2DMake(0.5, 0.5000001)
            XCTAssert(!region.contains(coords)) // NOT Contains
            coords = CLLocationCoordinate2DMake(0.5, -0.5000001)
            XCTAssert(!region.contains(coords)) // NOT Contains
            coords = CLLocationCoordinate2DMake(1, 1)
            XCTAssert(!region.contains(coords)) // NOT Contains
    
            region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
            coords = CLLocationCoordinate2DMake(0, 180.5)
            XCTAssert(region.contains(coords))
            coords.longitude = 179.5
            XCTAssert(region.contains(coords))
            coords.longitude = 180.5000001
            XCTAssert(!region.contains(coords)) // NOT Contains
            coords.longitude = 179.5000001
            XCTAssert(region.contains(coords))
            coords.longitude = 179.4999999
            XCTAssert(!region.contains(coords)) // NOT Contains
    
            region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(90, -180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
            coords = CLLocationCoordinate2DMake(90.5, -180.5)
            XCTAssert(region.contains(coords))
    
            coords = CLLocationCoordinate2DMake(89.5, -180.5)
            XCTAssert(region.contains(coords))
    
            coords = CLLocationCoordinate2DMake(90.50000001, -180.5)
            XCTAssert(!region.contains(coords)) // NOT Contains
    
            coords = CLLocationCoordinate2DMake(89.50000001, -180.5)
            XCTAssert(region.contains(coords))
    
            coords = CLLocationCoordinate2DMake(89.49999999, -180.5)
            XCTAssert(!region.contains(coords)) // NOT Contains
        }
    
        func testStandardAngle() {
            var angle = 180.5.wrapped(diemension: .longitude)
            var required = -179.5
            XCTAssert(self.areAngleEqual(angle, required))
    
            angle = 360.5.wrapped(diemension: .longitude)
            required = 0.5
            XCTAssert(self.areAngleEqual(angle, required))
    
            angle = 359.5.wrapped(diemension: .longitude)
            required = -0.5
            XCTAssert(self.areAngleEqual(angle, required))
    
            angle = 179.5.wrapped(diemension: .longitude)
            required = 179.5
            XCTAssert(self.areAngleEqual(angle, required))
    
            angle = 90.5.wrapped(diemension: .latitude)
            required = 89.5
            XCTAssert(self.areAngleEqual(angle, required))
    
            angle = 90.5000001.wrapped(diemension: .latitude)
            required = 89.4999999
            XCTAssert(self.areAngleEqual(angle, required))
    
            angle = -90.5.wrapped(diemension: .latitude)
            required = -89.5
            XCTAssert(self.areAngleEqual(angle, required))
    
            angle = -90.5000001.wrapped(diemension: .latitude)
            required = -89.4999999
            XCTAssert(self.areAngleEqual(angle, required))
        }
    
        /// compare doubles with presition to 8 digits after the decimal point
        func areAngleEqual(_ a:Double, _ b:Double) -> Bool {
            let presition = 0.00000001
            let equal = Int(a / presition) == Int(b / presition)
            print(String(format:"%14.9f %@ %14.9f", a, equal ? "==" : "!=", b) )
            return equal
        }
    }
    
    0 讨论(0)
  • 2020-12-13 05:20

    Works for me like a charm (Swift 5)

    func check(
        location: CLLocationCoordinate2D,
        contains childLocation: CLLocationCoordinate2D,
        with radius: Double)
        -> Bool
    {
        let region = CLCircularRegion(center: location, radius: radius, identifier: "SearchId")
        return region.contains(childLocation)
    }
    
    0 讨论(0)
  • 2020-12-13 05:22

    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);
    }
    
    0 讨论(0)
  • 2020-12-13 05:23

    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];
    }
    
    0 讨论(0)
  • 2020-12-13 05:26

    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.

    0 讨论(0)
提交回复
热议问题