How to check if MKCoordinateRegion contains CLLocationCoordinate2D without using MKMapView?

后端 未结 9 815
天涯浪人
天涯浪人 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: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
        }
    }
    

提交回复
热议问题