private func addCircle() { mapView.removeOverlays(mapView.overlays) let circle = MKCircle(centerCoordinate: mapCenter,radius: 8500000.0) mapView.addOverlay(circle) }
我还有一个自定义双击手势处理程序,它会覆盖地图视图的标准处理程序,并允许通过双击地图视图来更改地图中心:
private func configureGestureRecognizer() { doubleTapGestureRecognizer.addTarget(self,action: Selector("handleDoubleTap:")) doubleTapGestureRecognizer.numberOfTapsrequired = 2 if let subview = mapView.subviews.first as? UIView { subview.addGestureRecognizer(doubleTapGestureRecognizer) } else { println("Can't add a gesture recognizer") } } @objc private func handleDoubleTap(sender: UITapGestureRecognizer) { let point = sender.locationInView(mapView) let location = mapView.convertPoint(point,toCoordinateFromView: mapView) mapCenter = location addCircles() }
结果很奇怪:
您可能会注意到这两个半径之间存在显着差异:第二个半径比第一个半径大!
发生了什么,如何使它们正确显示?
编辑
感谢@blacksquare,我可以更接近解决方案,但仍然存在北极问题:
(小圈jsut代表一个中心)
解决方法
正如Anna和Warren都提到的那样,这不是一个错误 – 这是预期的行为.但是,在boundingMapRect和radius之间的文档中似乎存在差异.文档表明,半径是距离中心点的度量单位,在您的示例中显然不是这种情况.
我认为这里发生的事情是苹果可能从未打算将MKCircle用于你正在使用它的规模上. MKCircle创建一个2D圆,它既不能是圆形,也不能是投影地图上圆形区域的精确表示.
现在,如果要做的只是创建一个没有扭曲并且相对于赤道长度的半径的均匀圆,可以将赤道上的圆的长度设置为基本半径,然后计算比例当前点的半径如下:
let baseCoord = CLLocationCoordinate2D(latitude: 0,longitude: 0) let radius: Double = 850000.0 override func viewDidLoad() { super.viewDidLoad() mapView.region = MKCoordinateRegion( center: baseCoord,span: MKCoordinateSpan( latitudeDelta: 90,longitudeDelta: 180 ) ) mapCenter = baseCoord let circle = MKCircle(centerCoordinate: mapCenter,radius: radius) baseRadius = circle.boundingMapRect.size.height / 2 mapView.delegate = self configureGestureRecognizer() } private func addCircle() { mapView.removeOverlays(mapView.overlays) let circle = MKCircle(centerCoordinate: mapCenter,radius: radius) var currentRadius = circle.boundingMapRect.size.height / 2 let factor = baseRadius / currentRadius var updatedRadius = factor * radius let circleToDraw = MKCircle(centerCoordinate: mapCenter,radius: updatedRadius) mapView.addOverlay(circleToDraw) }
但如果你的计划要准确覆盖点击x米范围内的所有空间,那就有点棘手了.首先,您将获取双击动作中的单击坐标,然后将其用作多边形的中心.
@objc private func handleDoubleTap(sender: UITapGestureRecognizer) { let point = sender.locationInView(mapView) currentCoord = mapView.convertPoint(point,toCoordinateFromView: mapView) mapCenter = currentCoord addPolygon() }
在addPolygon中,获取坐标并设置叠加层:
private func addPolygon() { var mapCoords = getCoordinates() mapView.removeOverlays(mapView.overlays) let polygon = MKPolygon(coordinates: &mapCoords,count: mapCoords.count) mapView.addOverlay(polygon) }
给定一个点,一个方位和一个角距离(坐标之间的距离除以地球的半径),您可以使用以下公式计算另一个坐标的位置.请务必导入Darwin,以便您可以访问三角函数库
let globalRadius: Double = 6371000 let π = M_PI private func getCoordinates() -> [CLLocationCoordinate2D] { var coordinates = [CLLocationCoordinate2D]() let lat1: Double = (currentCoord!.latitude) let long1: Double = (currentCoord!.longitude) + 180 let factor = 30 if let a = annotation { mapView.removeAnnotation(annotation) } annotation = MKPointAnnotation() annotation!.setCoordinate(currentCoord!) annotation!.title = String(format: "%1.2f°,%1.2f°",lat1,long1) mapView.addAnnotation(annotation) var φ1: Double = lat1 * (π / 180) var λ1: Double = long1 * (π / 180) var angularDistance = radius / globalRadius var metersToNorthPole: Double = 0 var metersToSouthPole: Double = 0 for i in Int(lat1)..<89 { metersToNorthPole = metersToNorthPole + 111132.92 - (559.82 * cos(2 * φ1)) + (1.175 * cos(4 * φ1)) } for var i = lat1; i > -89; --i { metersToSouthPole = metersToSouthPole + 111132.92 - (559.82 * cos(2 * φ1)) + (1.175 * cos(4 * φ1)) } var startingBearing = -180 var endingBearing = 180 if metersToNorthPole - radius <= 0 { endingBearing = 0 startingBearing = -360 } for var i = startingBearing; i <= endingBearing; i += factor { var bearing = Double(i) var bearingInRadians: Double = bearing * (π / 180) var φ2: Double = asin(sin(φ1) * cos(angularDistance) + cos(φ1) * sin(angularDistance) * cos(bearingInRadians) ) var λ2 = atan2( sin(bearingInRadians) * sin(angularDistance) * cos(φ1),cos(angularDistance) - sin(φ1) * sin(φ2) ) + λ1 var lat2 = φ2 * (180 / π) var long2 = ( ((λ2 % (2 * π)) - π)) * (180.0 / π) if long2 < -180 { long2 = 180 + (long2 % 180) } if i == startingBearing && metersToNorthPole - radius <= 0 { coordinates.append(CLLocationCoordinate2D(latitude: 90,longitude: long2)) } else if i == startingBearing && metersToSouthPole - radius <= 0 { coordinates.append(CLLocationCoordinate2D(latitude: -90,longitude: long2)) } coordinates.append(CLLocationCoordinate2D(latitude: lat2,longitude: long2)) } if metersToNorthPole - radius <= 0 { coordinates.append(CLLocationCoordinate2D(latitude: 90,longitude: coordinates[coordinates.count - 1].longitude)) } else if metersToSouthPole - radius <= 0 { coordinates.append(CLLocationCoordinate2D(latitude: -90,longitude: coordinates[coordinates.count - 1].longitude)) } return coordinates }
在getCoordinates中,我们将度数转换为弧度,然后在我们的半径大于到北极或南极的距离的情况下添加一些锚定坐标.
以下是极点附近曲线的几个例子,半径分别为8500km和850km:
下面是最终输出的示例,其中包含一个额外的MKGeodesicPolyline叠加(Geodesics表示球面上最短的曲线),它显示了曲线的实际构建方式: