背景及问题分析

在 MeshKit 中,用户在规划好飞行区域后,进入到设置航线参数的页面,因为所规划的区域大小不尽相同,仅仅靠单一的 zoomLevel 配置,往往无法在地图上一目了然地看到整个航线区域,导致在用户体验上差强人意。

image.png

实现该需求的基本思路就是划定一个矩形区域,该矩形区域刚好可以容纳整个多边形区域,然后把地图的显示中心设置为该矩形区域就行了。
image.png
这里计算这个矩形区域非常简单,只需要把多边形的坐标顶点求极值,即可组合出矩形区域的四个顶点坐标。关键在于如何利用 MapKit 和 MapBox 的接口把地图显示中心设置为该矩形区域。

MapKit 设置中心区域

计算MKMapView的zoomLevel(地图缩放等级) 一文中有提到 MapKit 上的 MKCoordinateRegion ,这个对象定义了在地图上的一块区域。
image.png

可以通过 MapKit 提供的下面这个方法,来把此区域显示在地图中心。

  1. /// Changes the currently visible region and optionally animates the change.
  2. /// Declaration
  3. func setRegion(_ region: MKCoordinateRegion, animated: Bool)

如上图,我们只需求出 右上角(NorthEastPoint)左下角(SouthWestPoint) 即可定义出这个矩形。
具体设置代码如下:

  1. let polygonCoordinates: [CLLocationCoordinate2D] = [...] // 多边形顶点坐标
  2. let latitudes = coordinates.map { $0.latitude }
  3. let longtitudes = coordinates.map { $0.longitude }
  4. // 求坐标点极值
  5. guard let minLat = latitudes.min(), let maxLat = latitudes.max()
  6. , let minLng = longtitudes.min(), let maxLng = longtitudes.max() else { return }
  7. // 求出 NorthEast(右上角点) 和 SouthWest(左下角点) 坐标点
  8. let southWestCoordinate = CLLocationCoordinate2D(latitude: minLat, longitude: minLng)
  9. let northEastCoordinate = CLLocationCoordinate2D(latitude: maxLat, longitude: maxLng)
  10. // 根据 NorthEast(右上角点) 和 SouthWest(左下角点) 求出 经纬度跨度,即 MKCoordinateSpan
  11. let latDelta = northEastCoordinate.latitude - southWestCoordinate.latitude
  12. let lngDelta = northEastCoordinate.longitude - southWestCoordinate.longitude
  13. // 根据 NorthEast(右上角点) 和 SouthWest(左下角点)求出 中心点
  14. let centerLat = (northEastCoordinate.latitude + southWestCoordinate.latitude) / 2
  15. let centerlng = (northEastCoordinate.longitude + southWestCoordinate.longitude) / 2
  16. let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lngDelta)
  17. let center = CLLocationCoordinate2D(latitude: centerLat, longitude: centerlng)
  18. let region = MKCoordinateRegion(center: center, span: span)
  19. mapView.setRegion(region, animated: true)

MapBox 设置中心区域

在 MapBox 中有 setVisibleCoordinateBounds 这样一个方法,它的作用同样可以把一片区域显示在地图中心。它需要传入的是 MGLCoordinateBounds 对象。

  1. /**
  2. Changes the receiver’s viewport to fit the given coordinate bounds,
  3. optionally animating the change.
  4. @param bounds The bounds that the viewport will show in its entirety.
  5. */
  6. - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated;

MGLCoordinateBounds 对象,正是由我们上面所说的 右上角(NorthEastPoint)左下角(SouthWestPoint) 组成:

  1. /** A rectangular area as measured on a two-dimensional map projection. */
  2. typedef struct __attribute__((objc_boxable)) MGLCoordinateBounds {
  3. /** Coordinate at the southwest corner. */
  4. CLLocationCoordinate2D sw;
  5. /** Coordinate at the northeast corner. */
  6. CLLocationCoordinate2D ne;
  7. } MGLCoordinateBounds;

所以这里就很简单了,只需要构建 MGLCoordinateBounds 对象,然后调用 setVisibleCoordinateBound 方法即可:

  1. let polygonCoordinates: [CLLocationCoordinate2D] = [...] // 多边形顶点坐标
  2. let latitudes = coordinates.map { $0.latitude }
  3. let longtitudes = coordinates.map { $0.longitude }
  4. // 求坐标点极值
  5. guard let minLat = latitudes.min(), let maxLat = latitudes.max()
  6. , let minLng = longtitudes.min(), let maxLng = longtitudes.max() else { return }
  7. // 求出 NorthEast(右上角点) 和 SouthWest(左下角点) 坐标点
  8. let southWestCoordinate = CLLocationCoordinate2D(latitude: minLat, longitude: minLng)
  9. let northEastCoordinate = CLLocationCoordinate2D(latitude: maxLat, longitude: maxLng)
  10. let coordinateBound = MGLCoordinateBounds(sw: southWestCoordinate, ne: northEastCoordinate)
  11. mapbox.setVisibleCoordinateBounds(coordinateBound, animated: true)