根据苹果的文档MKCircle
:“随着纬度值远离赤道并向两极移动,地图点之间的物理距离变得更小。这意味着需要更多的地图点来表示相同的距离。因此,圆形叠加层的边界矩形变得随着该圆的中心点远离赤道并向两极移动,该圆就变大。”
正如安娜和沃伦都提到的,这不是一个错误——这是预期的行为。然而,文档之间似乎存在差异boundingMapRect
and 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()
}
In 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
}
In getCoordinates
我们将度数转换为弧度,然后在半径大于到北极或南极的距离的情况下添加更多锚定坐标。
以下是半径分别为 8500 公里和 850 公里的极点附近曲线的几个示例:
![enter image description here](https://i.stack.imgur.com/L6oGL.png)
这是最终输出的示例,带有附加内容MKGeodesicPolyline
覆盖(测地线表示球面上可能的最短曲线),显示了曲线的实际构建方式:
![enter image description here](https://i.stack.imgur.com/jifH7.jpg)