我正面临 MKCircle 外观的奇怪行为。基本上我想画一个半径为 8500 公里的圆,圆心是任意的。这是我的代码:
private func addCircle() {
mapView.removeOverlays(mapView.overlays)
let circle = MKCircle(centerCoordinate: mapCenter, radius: 8500000.0)
mapView.addOverlay(circle)
}
我还有一个自定义的双击手势处理程序,它会覆盖 map View 的标准手势处理程序,并允许通过双击 map View 来更改 map 中心:
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,我可以更接近解决方案,但北极仍然存在问题:
(小圆只是代表一个中心)
最佳答案
根据 Apple 的 MKCircle
文档:“随着纬度值从赤道向两极移动, map 点之间的物理距离变小。这意味着需要更多的 map 点来表示相同的距离。因此,当圆的中心点远离赤道并移向两极时,圆叠加层的边界矩形会变大。”
因此,正如 Anna 和 Warren 都提到的,这不是错误——这是预期的行为。但是,boundingMapRect
和 radius
之间的文档似乎存在差异。文档表明半径是从中心点开始的测量值(以米为单位),在您的示例中显然不是这种情况。
我认为这里发生的事情是 Apple 可能从未打算在您使用它的规模上使用 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
中,我们将度数转换为弧度,然后在我们的半径大于到北极或南极的距离的情况下再添加一些锚定坐标。
以下是半径分别为 8500 公里和 850 公里的极点附近的几个曲线示例:
这是最终输出的示例,带有附加的 MKGeodesicPolyline
叠加层(测地线表示球面上可能的最短曲线),显示了曲线的实际构建方式:
关于ios - MKMapView MKCircle 渲染一个半径太大的圆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28872162/