Convert MKCoordinateRegion to MKMapRect

前端 未结 10 635
南笙
南笙 2020-12-02 10:52

I have a square MKMapView in my app, and I wish to set a center point and the exact height/width of the view in meters.

Creating an MKCoordinateRegion and setting th

相关标签:
10条回答
  • 2020-12-02 11:19

    @Bogdan

    I think it should be:

     CLLocationCoordinate2D topLeftCoordinate =
    CLLocationCoordinate2DMake(coordinateRegion.center.latitude
                               + (coordinateRegion.span.latitudeDelta/2.0),
                               coordinateRegion.center.longitude
                               - (coordinateRegion.span.longitudeDelta/2.0));
    
    MKMapPoint topLeftMapPoint = MKMapPointForCoordinate(topLeftCoordinate);
    
    CLLocationCoordinate2D bottomRightCoordinate =
    CLLocationCoordinate2DMake(coordinateRegion.center.latitude
                               - (coordinateRegion.span.latitudeDelta/2.0),
                               coordinateRegion.center.longitude
                               + (coordinateRegion.span.longitudeDelta/2.0));
    
    MKMapPoint bottomRightMapPoint = MKMapPointForCoordinate(bottomRightCoordinate);
    
    MKMapRect mapRect = MKMapRectMake(topLeftMapPoint.x,
                                      topLeftMapPoint.y,
                                      fabs(bottomRightMapPoint.x-topLeftMapPoint.x),
                                      fabs(bottomRightMapPoint.y-topLeftMapPoint.y));
    

    According to apple api reference, MKCoordinateRegion.center represents the center point of the region; and MKCoordinateSpan.latitudeDelta represents the amount of north-to-south distance (measured in degrees) to display on the map; MKCoordinateSpan.longitudeDelta represents amount of east-to-west distance (measured in degrees) to display for the map region.

    0 讨论(0)
  • 2020-12-02 11:19

    The answer @David gave (and consequently the Swift 3 version by @onmyway133) has a significant error whenever the region crosses over the anti-meridian from the Eastern Hemisphere (longitude 0 degrees to 180 degrees) to the Western Hemisphere (longitude -180 degrees to 0 degrees). The width of the MKMapRect will be bigger than it should be (usually much bigger).

    Here is the fix (for the Swift 3 code):

    let topLeftMapPoint = MKMapPointForCoordinate(topLeft)
    let bottomRightMapPoint = MKMapPointForCoordinate(bottomRight)
    var width = bottomRightMapPoint.x - topLeftMapPoint.x
    if width < 0.0 {
        // Rect crosses from the Eastern Hemisphere to the Western Hemisphere
        width += MKMapPointForCoordinate(CLLocationCoordinate2D(latitude: 0.0, longitude: 180.0)).x
    }
    let height = bottomRightMapPoint.y - topLeftMapPoint.y
    let size = MKMapSize(width: width, height: height)
    return MKMapRect(origin: topLeftMapPoint, size: size)
    

    Taking an MKCoordinateRegion, converting it into an MKMapRect with the code above, and then turning it back into an MKCoordinateRegion using MKCoordinateRegionForMapRect() gives me very good agreement between the input region and the output region everywhere on the map.

    0 讨论(0)
  • 2020-12-02 11:20

    Still have to be a bit more careful about crossing the meridian (as well as wrapping around the poles) otherwise MKMapPointForCoordinate returns -1, -1:

    public func MKMapRectForCoordinateRegion(region:MKCoordinateRegion) -> MKMapRect {
    var topLeft = CLLocationCoordinate2D(
        latitude: min(region.center.latitude + (region.span.latitudeDelta/2.0), 90),
        longitude: region.center.longitude - (region.span.longitudeDelta/2.0)
    )
    
    if topLeft.longitude < -180 {
        // We wrapped around the meridian
        topLeft.longitude += 360
    }
    
    var bottomRight = CLLocationCoordinate2D(
        latitude: max(region.center.latitude - (region.span.latitudeDelta/2.0), -90),
        longitude: region.center.longitude + (region.span.longitudeDelta/2.0)
    )
    
        if bottomRight.longitude > 180 {
            // We wrapped around the medridian
            bottomRight.longitude -= 360
        }
    
        let topLeftMapPoint = MKMapPointForCoordinate(topLeft)
        let bottomRightMapPoint = MKMapPointForCoordinate(bottomRight)
    
        var width = bottomRightMapPoint.x - topLeftMapPoint.x
        if width < 0.0 {
            // Rect crosses meridian
            width += MKMapPointForCoordinate(CLLocationCoordinate2D(latitude: 0.0, longitude: 180.0)).x
        }
        let height = bottomRightMapPoint.y - topLeftMapPoint.y
        let size = MKMapSize(width: width, height: height)
    
        return MKMapRect(origin: topLeftMapPoint, size: size)
    }
    

    Some test case code (using Nimble):

    func testMKMapRectForCoordinateRegion() {
        let northWesternRegion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(45.0, -90.0), MKCoordinateSpanMake(20.0, 20.0))
        let northWesternMapRect = MKMapRectForCoordinateRegion(region: northWesternRegion)
        let convertedNWRegion = MKCoordinateRegionForMapRect(northWesternMapRect)
        expect(self.equivalentRegions(northWesternRegion, convertedNWRegion)).to(beTrue())
    
        let northEasternRegion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(45.0, 90.0), MKCoordinateSpanMake(20.0, 20.0))
        let northEasternMapRect = MKMapRectForCoordinateRegion(region: northEasternRegion)
        let convertedNERegion = MKCoordinateRegionForMapRect(northEasternMapRect)
        expect(self.equivalentRegions(northEasternRegion, convertedNERegion)).to(beTrue())
    
        let southWesternRegion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(-45.0, -90.0), MKCoordinateSpanMake(20.0, 20.0))
        let southWesternMapRect = MKMapRectForCoordinateRegion(region: southWesternRegion)
        let convertedSWRegion = MKCoordinateRegionForMapRect(southWesternMapRect)
        expect(self.equivalentRegions(southWesternRegion, convertedSWRegion)).to(beTrue())
    
        let southEasternRegion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(-45.0, 90.0), MKCoordinateSpanMake(20.0, 20.0))
        let southEasternMapRect = MKMapRectForCoordinateRegion(region: southEasternRegion)
        let convertedSERegion = MKCoordinateRegionForMapRect(southEasternMapRect)
        expect(self.equivalentRegions(southEasternRegion, convertedSERegion)).to(beTrue())
    
        let meridianSpanEastRegion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0.0, 170.0), MKCoordinateSpanMake(20.0, 20.0))
        let meridianSpanEastMapRect = MKMapRectForCoordinateRegion(region: meridianSpanEastRegion)
        let convertedMeridianSpanEastRegion = MKCoordinateRegionForMapRect(meridianSpanEastMapRect)
        expect(self.equivalentRegions(meridianSpanEastRegion, convertedMeridianSpanEastRegion)).to(beTrue())
    
        let meridianSpanWestRegion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0.0, -170.0), MKCoordinateSpanMake(20.0, 20.0))
        let meridianSpanWestMapRect = MKMapRectForCoordinateRegion(region: meridianSpanWestRegion)
        let convertedMeridianSpanWestRegion = MKCoordinateRegionForMapRect(meridianSpanWestMapRect)
        expect(self.equivalentRegions(meridianSpanWestRegion, convertedMeridianSpanWestRegion)).to(beTrue())
    }
    
    fileprivate func equivalentRegions(_ regionA: MKCoordinateRegion, _ regionB: MKCoordinateRegion) -> Bool {
        // Allow a small delta between values
        let deltaAllowed: Double = 1.0
    
        return (fabs(regionA.center.latitude - regionB.center.latitude) < deltaAllowed) &&
                (fabs(regionA.center.longitude - regionB.center.longitude) < deltaAllowed) &&
                (fabs(regionA.span.latitudeDelta - regionB.span.latitudeDelta) < deltaAllowed) &&
                (fabs(regionA.span.longitudeDelta - regionB.span.longitudeDelta) < deltaAllowed)
    }
    
    0 讨论(0)
  • 2020-12-02 11:20

    Swift 5.1:

    func mapRectForCoordinateRegion(_ region: MKCoordinateRegion) -> MKMapRect {
       let topLeftCoordinate = CLLocationCoordinate2DMake(region.center.latitude + (region.span.latitudeDelta / 2.0), region.center.longitude - (region.span.longitudeDelta / 2.0))
       let topLeftMapPoint = MKMapPoint(topLeftCoordinate)
       let bottomRightCoordinate = CLLocationCoordinate2DMake(region.center.latitude - (region.span.latitudeDelta / 2.0), region.center.longitude + (region.span.longitudeDelta / 2.0))
       let bottomRightMapPoint = MKMapPoint(bottomRightCoordinate)
       return MKMapRect(x: topLeftMapPoint.x, y: topLeftMapPoint.y, width: fabs(bottomRightMapPoint.x - topLeftMapPoint.x), height: fabs(bottomRightMapPoint.y - topLeftMapPoint.y))
    

    }

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