2012-02-14 4 views
30

У меня есть квадрат MKMapView в моем приложении, и я хочу установить центральную точку и точную высоту/ширину представления в метрах.Конвертировать MKCoordinateRegion в MKMapRect

Создание MKCoordinateRegion и установив карту к нему (как в этом коде ...

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

..) не работает должным образом, потому что использование регионов здесь просто означает, что по крайней мере отображается, что область, обычно больше, чем в регионе.


Я планирую использовать setVisibleMapRect:animated: метод вместо этого, как я считаю, что это будет увеличить до фактического MKMapRect прошло.

Итак, есть ли простой способ преобразования между MKcoordinateRegion и MKMapRect? Возможно, получая левые и нижние правые координаты области и используя их для создания MKMapRect?

Я не мог видеть ничего полезного в Map Kit Functions Reference.

(с использованием IOS 5, Xcode 4.2)

ответ

48

Чтобы добавить другую реализацию в кучу:

- (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: Есть много способов преобразования между MKMapRect и MKCoordinateRegion. Это, безусловно, не точный инверсный MKCoordinateRegionMakeWithDistance(), но довольно хорошо его приближает. Поэтому будьте осторожны, переходя туда и обратно, потому что информация может быть потеряна.

+2

Спасибо! Некоторое время меня спасало. – Vinzius

+0

Фантастический, спасибо. Использование прямоугольника map вместо области - отличный способ сделать смещение, потому что 'setVisibleMapRect' имеет возможность для вставки кромок. – SimplGy

+0

Это приводит к неправильному результату, результирующий прямоугольник сдвигается вбок. Я использовал @Wan Liqun ответ ниже, он работал отлично! –

9

вы можете использовать метод для преобразования MKCoordinateRegion в CGRect

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

и использовать - (MKMapRect)mapRectForRect:(CGRect)rect

или использовать MKMapPointForCoordinate метод для первого преобразования координат MKPoint и использовать его для формирования MKMapRect, чтобы в конечном итоге использовать setVisibleMapRect:animated:

+1

Не могли бы вы добавить класс, к которому относятся функции, о которых вы упомянули? и, возможно, полный пример того, как на самом деле сделать преобразование. – Daniel

+1

@ Даниэль, он устарел в iOS7, вероятно, поэтому вы не нашли этот метод. Вы можете найти его в классе наложения. – Vinzius

1

Используйте встроенную функцию MKCoordinateRegionForMapRect

MKCoordinateRegion region = MKCoordinateRegionForMapRect(rect); 
+15

это обратное преобразование, которое требуется, но полезно знать, я думаю, – squinlan

+1

Apple не давала прямых функций, потому что это неуместно для преобразования региона в прямой. Вы не должны делать это со своим приложением. –

12

Использование MKMapPointForCoordinate для преобразования 2 точки области (сверху/слева и снизу/справа), а затем создать MKMapRect с помощью 2 MKMapPoints

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

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

Согласно apple api reference, MKCoordinateRegion.center представляет собой центральную точку региона; и MKCoordinateSpan.latitudeDelta представляет количество расстояний между севером и югом (измеренное в градусах) для отображения на карте; MKCoordinateSpan.longitudeDelta представляет количество расстояния между востоком и западом (измеренное в градусах) для отображения для области карты.

+0

Это дает ожидаемый результат, а не принятый ответ –

17

Это Swift Verion к Лео & решение Barnhart

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 = MKMapPointForCoordinate(topLeft) 
    let b = MKMapPointForCoordinate(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))) 
} 
+0

Большое вам спасибо за это. – Fattie

1

@ ответ Давида, в 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

Ответ на этот вопрос @David дал (и, следовательно, версия Swift 3 от @ onmyway133) имеет значительную ошибку всякий раз, когда область пересекает антимеридиан из Восточного полушария (долгота 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 с кодом выше, а затем включить его в MKCoordinateRegion с помощью MKCoordinateRegionForMapRect() дает я очень хорошо согласен между областью ввода и областью вывода на карте.

+0

@ GilroyKilroy вполне правильно фиксирует широту в [-90, 90]. Координатная область, которая охватывает полюс, будет выглядеть как луковица, когда вы смотрите вниз на полярную область, и это переведет на две непересекающиеся области MKMapRect. –

0

Тем не менее должны быть немного более осторожным пересекая меридиан (а также обертку вокруг полюсов) в противном случае 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) 
} 

Некоторые тест-код (с помощью проворные):

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