将MKCoordinateRegion转换为MKMapRect

46

我的应用程序中有一个正方形的MKMapView,并且我希望设置一个中心点和视图在米下的精确高度/宽度。

创建一个MKCoordinateRegion并将地图设置为它(如此代码中所示...

MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center_coord, 1000.0, 1000.0);
[self.mapView setRegion:region animated:YES];

在这里使用区域(region)就意味着至少该区域会被显示,通常情况下展示的内容比区域更多。


我计划使用setVisibleMapRect:animated:方法代替,因为我认为这将缩放到实际传递的MKMapRect


那么,是否有一种简单的方法来转换MKCoordinateRegion和MKMapRect之间的坐标系?也许可以获取区域的左上角和右下角坐标,并使用它们创建MKMapRect?


我在Map Kit函数参考文档中没有找到方便的方法。


(使用iOS 5、Xcode 4.2)

11个回答

61

为了增加实现方式的数量:

- (MKMapRect)MKMapRectForCoordinateRegion:(MKCoordinateRegion)region
{
    MKMapPoint a = MKMapPointForCoordinate(CLLocationCoordinate2DMake(
        region.center.latitude + region.span.latitudeDelta / 2, 
        region.center.longitude - region.span.longitudeDelta / 2));
    MKMapPoint b = MKMapPointForCoordinate(CLLocationCoordinate2DMake(
        region.center.latitude - region.span.latitudeDelta / 2, 
        region.center.longitude + region.span.longitudeDelta / 2));
    return MKMapRectMake(MIN(a.x,b.x), MIN(a.y,b.y), ABS(a.x-b.x), ABS(a.y-b.y));
}

NB:有许多方法可以在MKMapRectMKCoordinateRegion之间进行转换。这种方法肯定不是MKCoordinateRegionMakeWithDistance()的精确反转,但相当接近。因此,在转换时要小心,因为可能会丢失信息。


2
谢谢!省去了我一些时间。 - Vinzius
1
太棒了,谢谢。使用map rect而不是region来进行偏移是一个很好的方法,因为setVisibleMapRect有边缘插图的选项。 - SimplGy
1
这会产生错误的结果,生成的矩形被侧向翻转了。我使用了下面@Wan Liqun的答案,它完美地解决了问题! - Pavel Gurov
正如@PavelGurov所提到的,这会产生错误的结果?我认为在Swift中,最后一行应该是return MKMapRectMake(a.x, a.y, abs(a.x-b.x), abs(a.y-b.y) - nekonari
2
@PavelGurov 是正确的,尽管这个方法能够在某些情况下工作,例如在经度接近最大/最小值的情况下它会产生错误的结果。 - Ricardo Barroso

47

这是Leo和Barnhart解决方案的Swift版本

func MKMapRectForCoordinateRegion(region:MKCoordinateRegion) -> MKMapRect {
    let topLeft = CLLocationCoordinate2D(latitude: region.center.latitude + (region.span.latitudeDelta/2), longitude: region.center.longitude - (region.span.longitudeDelta/2))
    let bottomRight = CLLocationCoordinate2D(latitude: region.center.latitude - (region.span.latitudeDelta/2), longitude: region.center.longitude + (region.span.longitudeDelta/2))

    let a = MKMapPoint(topLeft)
    let b = MKMapPoint(bottomRight)
    
    return MKMapRect(origin: MKMapPoint(x:min(a.x,b.x), y:min(a.y,b.y)), size: MKMapSize(width: abs(a.x-b.x), height: abs(a.y-b.y)))
}

非常感谢您。 - Fattie
4
针对Swift 4.2,更改如下代码: let a = MKMapPoint(topLeft) let b = MKMapPoint(bottomRight) - Denis Kutlubaev

12

使用MKMapPointForCoordinate将区域的两个点(左上和右下)转换,然后使用这两个MKMapPoints创建MKMapRect。

        CLLocationCoordinate2D coordinateOrigin = CLLocationCoordinate2DMake(latitude, longitude);
        CLLocationCoordinate2D coordinateMax = CLLocationCoordinate2DMake(latitude + cellSize, longitude + cellSize);

        MKMapPoint upperLeft = MKMapPointForCoordinate(coordinateOrigin);
        MKMapPoint lowerRight = MKMapPointForCoordinate(coordinateMax);

        MKMapRect mapRect = MKMapRectMake(upperLeft.x,
                                          upperLeft.y,
                                          lowerRight.x - upperLeft.x,
                                          lowerRight.y - upperLeft.y);

10

你可以使用方法将MKCoordinateRegion转换为CGRect

- (CGRect)convertRegion:(MKCoordinateRegion)region toRectToView:(UIView *)view

使用 - (MKMapRect)mapRectForRect:(CGRect)rect 方法,或者先使用 MKMapPointForCoordinate 方法将坐标转换为 MKPoint,再使用该点来构造 MKMapRect,最终使用 setVisibleMapRect:animated: 方法。


2
你能添加一下提到的函数所属的类吗?并且可能提供一个完整的示例来展示如何进行转换。 - Daniel
2
@Daniel,它在iOS7中已被弃用,这可能是你找不到该方法的原因。你可以在覆盖层类中找到它。 - Vinzius

9

@Bogdan

我认为应该是:

 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));

根据苹果API参考资料,MKCoordinateRegion.center表示该区域的中心点;MKCoordinateSpan.latitudeDelta表示地图上要显示的南北距离(以度为单位);MKCoordinateSpan.longitudeDelta表示地图上要显示的东西距离(以度为单位)。

这会产生预期的结果,而不是被接受的答案。 - Pavel Gurov

4

对于跨越经线(以及绕过极点)的情况,仍需更加小心,否则MKMapPointForCoordinate会返回-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)
}

以下是一些测试用例代码(使用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)
}

2

@David提供的答案(以及@onmyway133提供的Swift 3版本)存在显著错误,当区域跨越从东半球(经度0度到180度)到西半球(经度-180度到0度)的反子午线时。MKMapRect的宽度将比应该的要大(通常 大于实际需要的宽度)。

这里是修复方法(适用于Swift 3代码):

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)

使用上述代码将MKCoordinateRegion转换为MKMapRect,然后使用MKCoordinateRegionForMapRect()将其转换回MKCoordinateRegion,可以在地图上的任何位置使输入区域和输出区域非常吻合。请保留HTML标签。

@GilroyKilroy 在正确地将纬度剪裁为[-90, 90]。跨越极点的坐标区域在极地区域向下看起来像一个蝴蝶结,这将转化为两个不相交的MKMapRect区域。 - Mark A Durham

2

@David已经提供了Swift 3的答案

func mapRect(region: MKCoordinateRegion) -> MKMapRect {
  let topLeft = CLLocationCoordinate2D(
    latitude: region.center.latitude + (region.span.latitudeDelta/2.0),
    longitude: region.center.longitude - (region.span.longitudeDelta/2.0)
  )

  let bottomRight = CLLocationCoordinate2D(
    latitude: region.center.latitude - (region.span.latitudeDelta/2.0),
    longitude: region.center.longitude + (region.span.longitudeDelta/2.0)
  )

  let topLeftMapPoint = MKMapPointForCoordinate(topLeft)
  let bottomRightMapPoint = MKMapPointForCoordinate(bottomRight)

  let origin = MKMapPoint(x: topLeftMapPoint.x,
                          y: topLeftMapPoint.y)
  let size = MKMapSize(width: fabs(bottomRightMapPoint.x - topLeftMapPoint.x),
                       height: fabs(bottomRightMapPoint.y - topLeftMapPoint.y))

  return MKMapRect(origin: origin, size: size)
}

1

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

由于扩展的强大,这里提供了一个适用于Swift的扩展:

extension MKCoordinateRegion {
    public var rect : MKMapRect {
        let topLeft = CLLocationCoordinate2D(latitude: self.center.latitude + (self.span.latitudeDelta/2), longitude: self.center.longitude - (self.span.longitudeDelta/2))
        let bottomRight = CLLocationCoordinate2D(latitude: self.center.latitude - (self.span.latitudeDelta/2), longitude: self.center.longitude + (self.span.longitudeDelta/2))

        let a = MKMapPoint(topLeft)
        let b = MKMapPoint(bottomRight)
        
        return MKMapRect(origin: MKMapPoint(x:min(a.x,b.x), y:min(a.y,b.y)), size: MKMapSize(width: abs(a.x-b.x), height: abs(a.y-b.y)))
    }
}

这使得region.rect可以正常工作 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接