Преобразовать GMSVisibleRegion в CLRegion или MKCoordinateRegion
Я использую GoogleMaps SDK и в настоящее время пытаюсь преобразовать GMSVisibleRegion в CLRegion.
GMSVisibleRegion определяется как:
typedef struct {
CLLocationCoordinate2D nearLeft;
CLLocationCoordinate2D nearRight;
CLLocationCoordinate2D farLeft;
CLLocationCoordinate2D farRight;
} GMSVisibleRegion;
Какой самый быстрый способ сделать это?
К сожалению, сложно понять, что разработчик имел в виду под именами "ближний" и "дальний". Я думаю, что этот комментарий также может быть полезен:
/**
* Returns the region (four location coordinates) that is visible according to
* the projection.
*
* The visible region can be non-rectangular. The result is undefined if the
* projection includes points that do not map to anywhere on the map (e.g.,
* camera sees outer space).
*/
- (GMSVisibleRegion)visibleRegion;
Большое спасибо!
РЕДАКТИРОВАТЬ: ОК, мой первый шаг был создать MKCoordinateRegion для GMSVisibleRegion.
Я предлагаю следующий код для преобразования GMSVisibleRegion в MKCoordinateRegion. Любые возражения.
+ (MKCoordinateRegion)regionForCenter:(CLLocationCoordinate2D)center andGMSVisibleRegion:(GMSVisibleRegion)visibleRegion
{
CLLocationDegrees latitudeDelta = visibleRegion.farLeft.latitude - visibleRegion.nearLeft.latitude;
CLLocationDegrees longitudeDelta = visibleRegion.farRight.longitude - visibleRegion.farLeft.longitude;
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return MKCoordinateRegionMake(center, span);
}
4 ответа
Я предполагаю, что "ближний" предназначен для углов обзора в нижней части экрана, а "дальний" - для углов в верхней части экрана. Это потому, что если вы наклонили вид, то нижние углы находятся ближе всего к камере, а верхние углы - самые дальние от камеры.
Один из способов превратить это в CLRegion
может быть использование цели камеры в качестве центра, а затем вычисление радиуса от максимального расстояния до четырех углов. Возможно, это не самый плотно подобранный круг в регионе, но, поскольку круг в любом случае не может соответствовать четырехугольнику вида, он может быть достаточно близко.
Вот вспомогательная функция для расчета расстояния в метрах между двумя CLLocationCoordinate
ценности:
double getDistanceMetresBetweenLocationCoordinates(
CLLocationCoordinate2D coord1,
CLLocationCoordinate2D coord2)
{
CLLocation* location1 =
[[CLLocation alloc]
initWithLatitude: coord1.latitude
longitude: coord1.longitude];
CLLocation* location2 =
[[CLLocation alloc]
initWithLatitude: coord2.latitude
longitude: coord2.longitude];
return [location1 distanceFromLocation: location2];
}
Тогда CLRegion
можно рассчитать так:
GMSMapView* mapView = ...;
...
CLLocationCoordinate2D centre = mapView.camera.target;
GMSVisibleRegion* visibleRegion = mapView.projection.visibleRegion;
double nearLeftDistanceMetres =
getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.nearLeft);
double nearRightDistanceMetres =
getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.nearRight);
double farLeftDistanceMetres =
getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.farLeft);
double farRightDistanceMetres =
getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.farRight);
double radiusMetres =
MAX(nearLeftDistanceMetres,
MAX(nearRightDistanceMetres,
MAX(farLeftDistanceMetres, farRightDistanceMetres)));
CLRegion region = [[CLRegion alloc]
initCircularRegionWithCenter: centre radius: radius identifier: @"id"];
ОБНОВИТЬ:
Что касается вашего обновления для MKCoordinateRegion
, ваш пример кода может не работать. Если карта была повернута на 90 градусов, то farLeft
а также nearLeft
будет иметь одинаковую широту и farRight
а также farLeft
будет иметь одинаковую долготу, и поэтому ваши значения широты и долготы будут равны нулю.
Вам нужно будет перебрать все четыре farLeft
, farRight
, nearLeft
, nearRight
вычислите минимальные и максимальные значения широты и долготы каждого из них, а затем вычислите дельту из этого значения.
Google Maps SDK для iOS включает вспомогательный класс, который уже делает кое-что для вас - GMSCoordinateBounds
, Это может быть инициализировано с GMSVisibleRegion
:
GMSMapView* mapView = ...;
....
GMSVisibleRegion visibleRegion = mapView.projection.visibleRegion;
GMSCoordinateBounds bounds =
[[GMSCoordinateBounds alloc] initWithRegion: visibleRegion];
GMSCoordinateBounds
затем имеет northEast
а также southWest
свойства, которые определяют границы. Таким образом, вы можете рассчитать дельты следующим образом:
CLLocationDegrees latitudeDelta =
bounds.northEast.latitude - bounds.southWest.latitude;
CLLocationDegrees longitudeDelta =
bounds.northEast.longitude - bounds.southWest.longitude;
Вы также можете рассчитать центр по границам, и, следовательно, MKCoordinateRegion
:
CLLocationCoordinate2D centre = CLLocationCoordinate2DMake(
(bounds.southWest.latitude + bounds.northEast.latitude) / 2,
(bounds.southWest.longitude + bounds.northEast.longitude) / 2);
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return MKCoordinateRegionMake(centre, span);
Приложение для пуристов
Если вы хотите быть абсолютно строгим, вы должны внести поправку в отношении международной даты. Это было бы пустой тратой усилий в большинстве приложений, но эта проблема в последнее время вызывает у меня большое горе, поэтому я решил бросить ее в сообщество.
Опираясь на обновление Druce (боюсь, я не могу комментировать)...
GMSMapView* mapView = ...;
....
GMSVisibleRegion visibleRegion = mapView.projection.visibleRegion;
GMSCoordinateBounds bounds =
[[GMSCoordinateBounds alloc] initWithRegion: visibleRegion];
Локатору ничего не нужно с этим делать
CLLocationDegrees latitudeDelta =
bounds.northEast.latitude - bounds.southWest.latitude;
Соглашение предусматривает, что регион, который охватывает международную линию дат, может иметь юго-западный угол в Японии (+140 долготы) и его северо-восточный угол на Аляске (-150 долготы). Сложение и деление на два дает точку вокруг неправильной стороны земного шара.
Особый случай, когда northEast.longitude меньше, чем southWest.longitude, требует обработки
CLLocationCoordinate2D centre;
CLLocationDegrees longitudeDelta;
if(bounds.northEast.longitude >= bounds.southWest.longitude) {
//Standard case
centre = CLLocationCoordinate2DMake(
(bounds.southWest.latitude + bounds.northEast.latitude) / 2,
(bounds.southWest.longitude + bounds.northEast.longitude) / 2);
longitudeDelta = bounds.northEast.longitude - bounds.southWest.longitude;
} else {
//Region spans the international dateline
centre = CLLocationCoordinate2DMake(
(bounds.southWest.latitude + bounds.northEast.latitude) / 2,
(bounds.southWest.longitude + bounds.northEast.longitude + 360) / 2);
longitudeDelta = bounds.northEast.longitude + 360
- bounds.southWest.longitude;
}
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return MKCoordinateRegionMake(centre, span);
Основываясь на ответе @Saxon Druce, это быстрое расширение для настройки и получения region
используя MKCoordinateRegion на GMSMapView
extension GMSMapView {
var region : MKCoordinateRegion {
get {
let position = self.camera
let visibleRegion = self.projection.visibleRegion()
let bounds = GMSCoordinateBounds(region: visibleRegion)
let latitudeDelta = bounds.northEast.latitude - bounds.southWest.latitude
let longitudeDelta = bounds.northEast.longitude - bounds.southWest.longitude
let center = CLLocationCoordinate2DMake(
(bounds.southWest.latitude + bounds.northEast.latitude) / 2,
(bounds.southWest.longitude + bounds.northEast.longitude) / 2)
let span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta)
return MKCoordinateRegionMake(center, span)
}
set {
let northEast = CLLocationCoordinate2DMake(newValue.center.latitude - newValue.span.latitudeDelta/2, newValue.center.longitude - newValue.span.longitudeDelta/2)
let southWest = CLLocationCoordinate2DMake(newValue.center.latitude + newValue.span.latitudeDelta/2, newValue.center.longitude + newValue.span.longitudeDelta/2)
let bounds = GMSCoordinateBounds(coordinate: northEast, coordinate: southWest)
let update = GMSCameraUpdate.fitBounds(bounds, withPadding: 0)
self.moveCamera(update)
}
}
}
Для тех, кто ищет шаблонный код, основанный на всех ответах и исправлениях, представленных до сих пор, вот region
реализовано как категория в GMSMapView:
//
// GMSMapViewExtensions.h
//
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import <GoogleMaps/GoogleMaps.h>
@interface GMSMapView (GMSMapViewExtensions)
@end
а также
//
// GMSMapViewExtensions.m
//
#import "GMSMapViewExtensions.h"
@implementation GMSMapView (GMSMapViewExtensions)
- (MKCoordinateRegion) region {
GMSVisibleRegion visibleRegion = self.projection.visibleRegion;
GMSCoordinateBounds * bounds = [[GMSCoordinateBounds alloc] initWithRegion: visibleRegion];
CLLocationDegrees latitudeDelta = bounds.northEast.latitude - bounds.southWest.latitude;
CLLocationCoordinate2D centre;
CLLocationDegrees longitudeDelta;
if (bounds.northEast.longitude >= bounds.southWest.longitude) {
// Standard case
centre = CLLocationCoordinate2DMake(
(bounds.southWest.latitude + bounds.northEast.latitude) / 2,
(bounds.southWest.longitude + bounds.northEast.longitude) / 2);
longitudeDelta = bounds.northEast.longitude - bounds.southWest.longitude;
} else {
// Region spans the international dateline
centre = CLLocationCoordinate2DMake(
(bounds.southWest.latitude + bounds.northEast.latitude) / 2,
(bounds.southWest.longitude + bounds.northEast.longitude + 360) / 2);
longitudeDelta = bounds.northEast.longitude + 360 - bounds.southWest.longitude;
}
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return MKCoordinateRegionMake(centre, span);
}
- (MKMapRect)visibleMapRect {
MKCoordinateRegion region = [self 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));
}
@end
Пример использования:
GMSMapView * mapView = .... // init code
MKCoordinateRegion mapRegion = mapView.region;