背景及问题分析
在 MeshKit 中,用户在规划好飞行区域后,进入到设置航线参数的页面,因为所规划的区域大小不尽相同,仅仅靠单一的 zoomLevel 配置,往往无法在地图上一目了然地看到整个航线区域,导致在用户体验上差强人意。
实现该需求的基本思路就是划定一个矩形区域,该矩形区域刚好可以容纳整个多边形区域,然后把地图的显示中心设置为该矩形区域就行了。
这里计算这个矩形区域非常简单,只需要把多边形的坐标顶点求极值,即可组合出矩形区域的四个顶点坐标。关键在于如何利用 MapKit 和 MapBox 的接口把地图显示中心设置为该矩形区域。
MapKit 设置中心区域
在 计算MKMapView的zoomLevel(地图缩放等级) 一文中有提到 MapKit 上的 MKCoordinateRegion
,这个对象定义了在地图上的一块区域。
可以通过 MapKit 提供的下面这个方法,来把此区域显示在地图中心。
/// Changes the currently visible region and optionally animates the change.
/// Declaration
func setRegion(_ region: MKCoordinateRegion, animated: Bool)
如上图,我们只需求出 右上角(NorthEastPoint)
和 左下角(SouthWestPoint)
即可定义出这个矩形。
具体设置代码如下:
let polygonCoordinates: [CLLocationCoordinate2D] = [...] // 多边形顶点坐标
let latitudes = coordinates.map { $0.latitude }
let longtitudes = coordinates.map { $0.longitude }
// 求坐标点极值
guard let minLat = latitudes.min(), let maxLat = latitudes.max()
, let minLng = longtitudes.min(), let maxLng = longtitudes.max() else { return }
// 求出 NorthEast(右上角点) 和 SouthWest(左下角点) 坐标点
let southWestCoordinate = CLLocationCoordinate2D(latitude: minLat, longitude: minLng)
let northEastCoordinate = CLLocationCoordinate2D(latitude: maxLat, longitude: maxLng)
// 根据 NorthEast(右上角点) 和 SouthWest(左下角点) 求出 经纬度跨度,即 MKCoordinateSpan
let latDelta = northEastCoordinate.latitude - southWestCoordinate.latitude
let lngDelta = northEastCoordinate.longitude - southWestCoordinate.longitude
// 根据 NorthEast(右上角点) 和 SouthWest(左下角点)求出 中心点
let centerLat = (northEastCoordinate.latitude + southWestCoordinate.latitude) / 2
let centerlng = (northEastCoordinate.longitude + southWestCoordinate.longitude) / 2
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lngDelta)
let center = CLLocationCoordinate2D(latitude: centerLat, longitude: centerlng)
let region = MKCoordinateRegion(center: center, span: span)
mapView.setRegion(region, animated: true)
MapBox 设置中心区域
在 MapBox 中有 setVisibleCoordinateBounds
这样一个方法,它的作用同样可以把一片区域显示在地图中心。它需要传入的是 MGLCoordinateBounds
对象。
/**
Changes the receiver’s viewport to fit the given coordinate bounds,
optionally animating the change.
@param bounds The bounds that the viewport will show in its entirety.
*/
- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated;
MGLCoordinateBounds
对象,正是由我们上面所说的 右上角(NorthEastPoint)
和 左下角(SouthWestPoint)
组成:
/** A rectangular area as measured on a two-dimensional map projection. */
typedef struct __attribute__((objc_boxable)) MGLCoordinateBounds {
/** Coordinate at the southwest corner. */
CLLocationCoordinate2D sw;
/** Coordinate at the northeast corner. */
CLLocationCoordinate2D ne;
} MGLCoordinateBounds;
所以这里就很简单了,只需要构建 MGLCoordinateBounds
对象,然后调用 setVisibleCoordinateBound
方法即可:
let polygonCoordinates: [CLLocationCoordinate2D] = [...] // 多边形顶点坐标
let latitudes = coordinates.map { $0.latitude }
let longtitudes = coordinates.map { $0.longitude }
// 求坐标点极值
guard let minLat = latitudes.min(), let maxLat = latitudes.max()
, let minLng = longtitudes.min(), let maxLng = longtitudes.max() else { return }
// 求出 NorthEast(右上角点) 和 SouthWest(左下角点) 坐标点
let southWestCoordinate = CLLocationCoordinate2D(latitude: minLat, longitude: minLng)
let northEastCoordinate = CLLocationCoordinate2D(latitude: maxLat, longitude: maxLng)
let coordinateBound = MGLCoordinateBounds(sw: southWestCoordinate, ne: northEastCoordinate)
mapbox.setVisibleCoordinateBounds(coordinateBound, animated: true)