Преобразовать MKCoordinateRegion в MKMapRect
У меня есть квадратный MKMapView в моем приложении, и я хочу установить центральную точку и точную высоту / ширину вида в метрах.
Создание MKCoordinateRegion и установка карты для него (как в этом коде...
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center_coord, 1000.0, 1000.0);
[self.mapView setRegion:region animated:YES];
..) не работает должным образом, потому что использование регионов здесь просто означает, что по крайней мере этот регион отображается, как правило, больше, чем регион.
Я планирую вместо этого использовать setVisibleMapRect: animated: метод, так как я считаю, что это увеличит фактический переданный MKMapRect.
Итак, есть ли простой способ преобразования между MKcoordinateRegion и MKMapRect? Возможно, получить верхнюю левую и нижнюю правую координаты региона и использовать их для создания MKMapRect?
Я не увидел ничего удобного в Справочнике по функциям Map Kit.
(Используя iOS 5, Xcode 4.2)
11 ответов
Чтобы добавить еще одну реализацию в кучу:
- (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()
, но приближается к нему довольно хорошо. Так что будьте осторожны, конвертируя туда-сюда, потому что информация может быть потеряна.
Это решение Swift verion to Leo & 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)))
}
Используйте 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);
Вы можете использовать метод для преобразования MKCoordinateRegion
в CGRect
- (CGRect)convertRegion:(MKCoordinateRegion)region toRectToView:(UIView *)view
и использовать - (MKMapRect)mapRectForRect:(CGRect)rect
или использовать MKMapPointForCoordinate
метод для первого преобразования координат в MKPoint
и использовать это для формирования MKMapRect
в конечном итоге использовать setVisibleMapRect:animated:
@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, MKCoordinateRegion.center представляет центральную точку региона; и MKCoordinateSpan.latitudeDelta представляет величину расстояния с севера на юг (измеряется в градусах) для отображения на карте; MKCoordinateSpan.longitudeDelta представляет величину расстояния между востоком и западом (в градусах), отображаемую для региона карты.
Все же нужно быть немного более осторожным при пересечении меридиана (а также обтекании полюсов), иначе 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)
}
Ответ, который дал @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(), я получаю очень хорошее согласие между областью ввода и областью вывода повсюду на карте.
@ Ответ Дэвида в 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)
}
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))
}
И поскольку правила расширений, вот расширение для 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
просто работать :)
Используйте встроенную функцию MKCoordinateRegionForMapRect
MKCoordinateRegion region = MKCoordinateRegionForMapRect(rect);