如何在围绕几个 annotations/coordinates 的 MapKit 上绘制圆形叠加?
How to draw circle overlay on MapKit that surrounds several annotations/coordinates?
我有一个数组 [CLLocationCoordinate2D]
,我想在 MKMapView
上围绕所有这些坐标画一个圆。
我已经设法围绕单个 CLLocationCoordinate2D
画了一个圆圈:
let coordinate = CLLocationCoordinate2D(latitude: 53, longitude: 27)
self.mapView.add(MKCircle(center: coordinate, radius: 100))
extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard overlay is MKCircle else { return MKOverlayRenderer() }
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = UIColor.red
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
circle.lineWidth = 1
return circle
}
}
如何画一个surrounds/encompasses所有坐标的圆?如下图:
我想出了 MKCoordinateRegion
初始化器,它提供了坐标区域,扩展有一个计算的 属性 来提供区域的半径。
extension MKCoordinateRegion {
init?(from coordinates: [CLLocationCoordinate2D]) {
guard coordinates.count > 1 else { return nil }
let a = MKCoordinateRegion.region(coordinates, fix: { [=10=] }, fix2: { [=10=] })
let b = MKCoordinateRegion.region(coordinates, fix: MKCoordinateRegion.fixMeridianNegativeLongitude, fix2: MKCoordinateRegion.fixMeridian180thLongitude)
guard (a != nil || b != nil) else { return nil }
guard (a != nil && b != nil) else {
self = a ?? b!
return
}
self = [a!, b!].min(by: { [=10=].span.longitudeDelta < .span.longitudeDelta }) ?? a!
}
var radius: CLLocationDistance {
let furthest = CLLocation(latitude: self.center.latitude + (span.latitudeDelta / 2),
longitude: center.longitude + (span.longitudeDelta / 2))
return CLLocation(latitude: center.latitude, longitude: center.longitude).distance(from: furthest)
}
// MARK: - Private
private static func region(_ coordinates: [CLLocationCoordinate2D],
fix: (CLLocationCoordinate2D) -> CLLocationCoordinate2D,
fix2: (CLLocationCoordinate2D) -> CLLocationCoordinate2D) -> MKCoordinateRegion? {
let t = coordinates.map(fix)
let min = CLLocationCoordinate2D(latitude: t.min { [=10=].latitude < .latitude }!.latitude,
longitude: t.min { [=10=].longitude < .longitude }!.longitude)
let max = CLLocationCoordinate2D(latitude: t.max { [=10=].latitude < .latitude }!.latitude,
longitude: t.max { [=10=].longitude < .longitude }!.longitude)
// find span
let span = MKCoordinateSpanMake(max.latitude - min.latitude, max.longitude - min.longitude)
// find center
let center = CLLocationCoordinate2D(latitude: max.latitude - span.latitudeDelta / 2,
longitude: max.longitude - span.longitudeDelta / 2)
return MKCoordinateRegion(center: fix2(center), span: span)
}
private static func fixMeridianNegativeLongitude(coordinate: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
guard (coordinate.longitude < 0) else { return coordinate }
let fixedLng = 360 + coordinate.longitude
return CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: fixedLng)
}
private static func fixMeridian180thLongitude(coordinate: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
guard (coordinate.longitude > 180) else { return coordinate }
let fixedLng = -360 + coordinate.longitude
return CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: fixedLng)
}
}
用法:
let coordinates: [CLLocationCoordinate2D] = self.mapView.annotations.map{ [=11=].coordinate }
if let region = MKCoordinateRegion(from: coordinates) {
self.mapView.add(MKCircle(center: region.center, radius: region.radius))
}
结果正是我想要的,能够处理跨越180度经线的坐标:
我有一个数组 [CLLocationCoordinate2D]
,我想在 MKMapView
上围绕所有这些坐标画一个圆。
我已经设法围绕单个 CLLocationCoordinate2D
画了一个圆圈:
let coordinate = CLLocationCoordinate2D(latitude: 53, longitude: 27)
self.mapView.add(MKCircle(center: coordinate, radius: 100))
extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard overlay is MKCircle else { return MKOverlayRenderer() }
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = UIColor.red
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
circle.lineWidth = 1
return circle
}
}
如何画一个surrounds/encompasses所有坐标的圆?如下图:
我想出了 MKCoordinateRegion
初始化器,它提供了坐标区域,扩展有一个计算的 属性 来提供区域的半径。
extension MKCoordinateRegion {
init?(from coordinates: [CLLocationCoordinate2D]) {
guard coordinates.count > 1 else { return nil }
let a = MKCoordinateRegion.region(coordinates, fix: { [=10=] }, fix2: { [=10=] })
let b = MKCoordinateRegion.region(coordinates, fix: MKCoordinateRegion.fixMeridianNegativeLongitude, fix2: MKCoordinateRegion.fixMeridian180thLongitude)
guard (a != nil || b != nil) else { return nil }
guard (a != nil && b != nil) else {
self = a ?? b!
return
}
self = [a!, b!].min(by: { [=10=].span.longitudeDelta < .span.longitudeDelta }) ?? a!
}
var radius: CLLocationDistance {
let furthest = CLLocation(latitude: self.center.latitude + (span.latitudeDelta / 2),
longitude: center.longitude + (span.longitudeDelta / 2))
return CLLocation(latitude: center.latitude, longitude: center.longitude).distance(from: furthest)
}
// MARK: - Private
private static func region(_ coordinates: [CLLocationCoordinate2D],
fix: (CLLocationCoordinate2D) -> CLLocationCoordinate2D,
fix2: (CLLocationCoordinate2D) -> CLLocationCoordinate2D) -> MKCoordinateRegion? {
let t = coordinates.map(fix)
let min = CLLocationCoordinate2D(latitude: t.min { [=10=].latitude < .latitude }!.latitude,
longitude: t.min { [=10=].longitude < .longitude }!.longitude)
let max = CLLocationCoordinate2D(latitude: t.max { [=10=].latitude < .latitude }!.latitude,
longitude: t.max { [=10=].longitude < .longitude }!.longitude)
// find span
let span = MKCoordinateSpanMake(max.latitude - min.latitude, max.longitude - min.longitude)
// find center
let center = CLLocationCoordinate2D(latitude: max.latitude - span.latitudeDelta / 2,
longitude: max.longitude - span.longitudeDelta / 2)
return MKCoordinateRegion(center: fix2(center), span: span)
}
private static func fixMeridianNegativeLongitude(coordinate: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
guard (coordinate.longitude < 0) else { return coordinate }
let fixedLng = 360 + coordinate.longitude
return CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: fixedLng)
}
private static func fixMeridian180thLongitude(coordinate: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
guard (coordinate.longitude > 180) else { return coordinate }
let fixedLng = -360 + coordinate.longitude
return CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: fixedLng)
}
}
用法:
let coordinates: [CLLocationCoordinate2D] = self.mapView.annotations.map{ [=11=].coordinate }
if let region = MKCoordinateRegion(from: coordinates) {
self.mapView.add(MKCircle(center: region.center, radius: region.radius))
}
结果正是我想要的,能够处理跨越180度经线的坐标: