MKMapView MKCircle 渲染一个半径太大的圆
MKMapView MKCircle renders a circle with too big radius
我遇到了 MKCircle 外观的奇怪行为。基本上我想画一个半径为 8500 公里的圆,圆心是任意的。这是我的代码:
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,我可以更接近解决方案,但北极仍然存在问题:
(小圆只是代表一个圆心)
根据 Apple 的 MKCircle
文档:"As latitude values move away from the equator and toward the poles, the physical distance between map points gets smaller. This means that more map points are needed to represent the same distance. As a result, the bounding rectangle of a circle overlay gets larger as the center point of that circle moves away from the equator and toward the poles."
因此,正如 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 米范围内的所有 space,那就有点棘手了。首先,您将在双击操作中获取点击坐标,然后将其用作多边形的中心。
@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)
}
给定一个点、一个方位角和一个 angular 距离(坐标之间的距离除以地球半径),您可以使用以下公式计算另一个坐标的位置。请务必导入 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
叠加层的最终输出示例(测地线表示球面上最短的可能曲线),显示了曲线的实际构建方式:
OP 问题的答案
这是怎么回事,由于使用了地图投影,显示的地图会出现差异。 MKCircle
方法将在任何给定的纬度生成准确的圆(前提是半径未覆盖多个纬度),但由于地图投影的原因,它们的大小会有所不同。
为了在较大的缩放级别获得相似的圆圈,您必须更改相对于您的纬度的半径,这可能会影响经度距离。另外圆圈现在代表什么?
要获得相等的圆圈,一种方法是使用 MapPoints, which has a latitude relative method, namely MKMetersPerMapPointAtLatitude。如果将其乘以世界各地的给定数字,则圆圈的大小将相等,但如前所述:这个圆圈代表什么?
换句话说:你需要考虑圆应该代表什么,因为在没有校正的情况下使用它确实代表了到该位置的距离,但它肯定看起来不像由于地图投影问题,在世界地图上查看多个圆圈!
MKMapView 投影功能示例
我做了一个小示例应用程序,我在世界各地的不同位置添加了一些带有硬编码位置和半径的圆圈。这产生了左边的图像,它有不同的圆圈大小。
使用下面代码中描述的纬度相对方法,圆圈大小相同。我还在巴拿马城添加了一个圆圈,其半径等于到墨西哥城的距离,表明给定纬度的 CLLocationDistance(以米为单位)在某种程度上是正确的。
图片生成代码
下面列出了用于生成右图的代码中有趣的部分。左图基于相同的代码,删除了 * MKMetersPerMapPointAtLatitude(...)
部分,并且半径不同。
let panamaCityLoc = CLLocationCoordinate2D(latitude: 8.9936000, longitude: -79.51979300)
let mexicoCityLoc = CLLocationCoordinate2D(latitude: 19.4284700, longitude: -99.1276600)
let newYorkLoc = CLLocationCoordinate2D(latitude: 40.7142700, longitude: -74.0059700)
let nuukLoc = CLLocationCoordinate2D(latitude: 64.1834700, longitude: -51.7215700)
let northlyLoc = CLLocationCoordinate2D(latitude: 80.0000000, longitude: -68.00)
var mapCenter = nuukLoc
mapView.centerCoordinate = mapCenter
var radiusInSomething : CLLocationDistance = 10000000.0
mapView.removeOverlays(mapView.overlays)
mapView.addOverlay(MKCircle(centerCoordinate: nuukLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(nuukLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: panamaCityLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(panamaCityLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: newYorkLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(newYorkLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: mexicoCityLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(mexicoCityLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: northlyLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(northlyLoc.latitude)))
// Try to figure out something related to distances...
var panamaCityMapPoint = MKMapPointForCoordinate(panamaCityLoc)
var mexicoCityMapPoint = MKMapPointForCoordinate(mexicoCityLoc)
var distancePanamaToMexixo = MKMetersBetweenMapPoints(panamaCityMapPoint, mexicoCityMapPoint)
println("Distance Panama City to Mexico City according to dateandtime.info: 2410 km")
println("Distance Panama City to Mexico: \(distancePanamaToMexixo) CLLocationDistance (or m)")
println(" meters/MapPoint at latitude Panama City: \( MKMetersPerMapPointAtLatitude(panamaCityLoc.latitude) ) ")
println(" in mapPoints: \( distancePanamaToMexixo / MKMetersPerMapPointAtLatitude(panamaCityLoc.latitude) ) ")
mapView.addOverlay(MKCircle(centerCoordinate: panamaCityLoc, radius: distancePanamaToMexixo))
我在最后添加了一些关于不同距离、地图点等的println
,这些产生了以下输出:
Distance Panama City to Mexico City according to dateandtime.info: 2410 km
Distance Panama City to Mexico: 2408968.73912751 CLLocationDistance (or m)
meters/MapPoint at latitude Panama City: 0.146502523951599
in mapPoints: 16443189.333198
如果有人想在 Swift 3 中实现它,我创建了一个 MKPolygon 的子类,它根据 Kellan 的出色回答呈现测地线圆。
只需使用
创建它
let circle = MKGeodesicCircle(center: CLLocationCoordinate2D, radius: 100000)
这是 Swift 文件
import UIKit
import Darwin
import CoreLocation
import MapKit
class MKGeodesicCircle: MKPolygon {
convenience init(center: CLLocationCoordinate2D, radius: CLLocationDistance) {
self.init(center: center, radius: radius, fromRadial: 0, toRadial: 360)
}
convenience init(center: CLLocationCoordinate2D, radius: CLLocationDistance, fromRadial: CLLocationDegrees, toRadial:CLLocationDegrees) {
let currentCoord:CLLocationCoordinate2D!
currentCoord = center
let coords = MKGeodesicCircle.getCoordinates(currentCoord: currentCoord, radius: radius, fromRadial: fromRadial, toRadial: toRadial)
self.init()
self.init(coordinates: coords, count: coords.count)
}
class func getCoordinates(currentCoord: CLLocationCoordinate2D!, radius: CLLocationDistance, fromRadial: CLLocationDegrees, toRadial: CLLocationDegrees) -> [CLLocationCoordinate2D] {
let globalRadius: Double = 6371000
let π = M_PI
var coordinates = [CLLocationCoordinate2D]()
let lat1: Double = (currentCoord!.latitude)
let long1: Double = (currentCoord!.longitude) + 180
let factor = 3
let φ1: Double = lat1 * (π / 180)
let λ1: Double = long1 * (π / 180)
let angularDistance = radius / globalRadius
var metersToNorthPole: Double = 0
var metersToSouthPole: Double = 0
for _ in Int(lat1)..<89 {
metersToNorthPole = metersToNorthPole + 111132.92 - (559.82 * cos(2 * φ1)) + (1.175 * cos(4 * φ1))
}
for _ in stride(from: lat1, through: -89, by: -1) {
metersToSouthPole = metersToSouthPole + 111132.92 - (559.82 * cos(2 * φ1)) + (1.175 * cos(4 * φ1))
}
var startingBearing = Int(fromRadial) - 180
var endingBearing = Int(toRadial) - 180
if metersToNorthPole - radius <= 0 {
endingBearing = Int(fromRadial) - 0
startingBearing = Int(toRadial) * -1
}
for i in stride(from: startingBearing, through: endingBearing, by: factor) {
//for var i = startingBearing; i <= endingBearing; i += factor {
let bearing = Double(i)
let bearingInRadians: Double = bearing * (π / 180)
let φ2: Double = asin(sin(φ1) * cos(angularDistance)
+ cos(φ1) * sin(angularDistance)
* cos(bearingInRadians)
)
let λ2 = atan2(
sin(bearingInRadians) * sin(angularDistance) * cos(φ1),
cos(angularDistance) - sin(φ1) * sin(φ2)
) + λ1
let lat2 = φ2 * (180 / π)
var long2 = ( ((λ2.truncatingRemainder(dividingBy: (2 * π)) ) - π)) * (180.0 / π)
if long2 < -180 {
long2 = 180 + (long2.truncatingRemainder(dividingBy: 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
}
}
我遇到了 MKCircle 外观的奇怪行为。基本上我想画一个半径为 8500 公里的圆,圆心是任意的。这是我的代码:
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,我可以更接近解决方案,但北极仍然存在问题:
(小圆只是代表一个圆心)
根据 Apple 的 MKCircle
文档:"As latitude values move away from the equator and toward the poles, the physical distance between map points gets smaller. This means that more map points are needed to represent the same distance. As a result, the bounding rectangle of a circle overlay gets larger as the center point of that circle moves away from the equator and toward the poles."
因此,正如 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 米范围内的所有 space,那就有点棘手了。首先,您将在双击操作中获取点击坐标,然后将其用作多边形的中心。
@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)
}
给定一个点、一个方位角和一个 angular 距离(坐标之间的距离除以地球半径),您可以使用以下公式计算另一个坐标的位置。请务必导入 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
叠加层的最终输出示例(测地线表示球面上最短的可能曲线),显示了曲线的实际构建方式:
OP 问题的答案
这是怎么回事,由于使用了地图投影,显示的地图会出现差异。 MKCircle
方法将在任何给定的纬度生成准确的圆(前提是半径未覆盖多个纬度),但由于地图投影的原因,它们的大小会有所不同。
为了在较大的缩放级别获得相似的圆圈,您必须更改相对于您的纬度的半径,这可能会影响经度距离。另外圆圈现在代表什么?
要获得相等的圆圈,一种方法是使用 MapPoints, which has a latitude relative method, namely MKMetersPerMapPointAtLatitude。如果将其乘以世界各地的给定数字,则圆圈的大小将相等,但如前所述:这个圆圈代表什么?
换句话说:你需要考虑圆应该代表什么,因为在没有校正的情况下使用它确实代表了到该位置的距离,但它肯定看起来不像由于地图投影问题,在世界地图上查看多个圆圈!
MKMapView 投影功能示例
我做了一个小示例应用程序,我在世界各地的不同位置添加了一些带有硬编码位置和半径的圆圈。这产生了左边的图像,它有不同的圆圈大小。
使用下面代码中描述的纬度相对方法,圆圈大小相同。我还在巴拿马城添加了一个圆圈,其半径等于到墨西哥城的距离,表明给定纬度的 CLLocationDistance(以米为单位)在某种程度上是正确的。
图片生成代码
下面列出了用于生成右图的代码中有趣的部分。左图基于相同的代码,删除了 * MKMetersPerMapPointAtLatitude(...)
部分,并且半径不同。
let panamaCityLoc = CLLocationCoordinate2D(latitude: 8.9936000, longitude: -79.51979300)
let mexicoCityLoc = CLLocationCoordinate2D(latitude: 19.4284700, longitude: -99.1276600)
let newYorkLoc = CLLocationCoordinate2D(latitude: 40.7142700, longitude: -74.0059700)
let nuukLoc = CLLocationCoordinate2D(latitude: 64.1834700, longitude: -51.7215700)
let northlyLoc = CLLocationCoordinate2D(latitude: 80.0000000, longitude: -68.00)
var mapCenter = nuukLoc
mapView.centerCoordinate = mapCenter
var radiusInSomething : CLLocationDistance = 10000000.0
mapView.removeOverlays(mapView.overlays)
mapView.addOverlay(MKCircle(centerCoordinate: nuukLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(nuukLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: panamaCityLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(panamaCityLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: newYorkLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(newYorkLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: mexicoCityLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(mexicoCityLoc.latitude)))
mapView.addOverlay(MKCircle(centerCoordinate: northlyLoc,
radius: radiusInSomething * MKMetersPerMapPointAtLatitude(northlyLoc.latitude)))
// Try to figure out something related to distances...
var panamaCityMapPoint = MKMapPointForCoordinate(panamaCityLoc)
var mexicoCityMapPoint = MKMapPointForCoordinate(mexicoCityLoc)
var distancePanamaToMexixo = MKMetersBetweenMapPoints(panamaCityMapPoint, mexicoCityMapPoint)
println("Distance Panama City to Mexico City according to dateandtime.info: 2410 km")
println("Distance Panama City to Mexico: \(distancePanamaToMexixo) CLLocationDistance (or m)")
println(" meters/MapPoint at latitude Panama City: \( MKMetersPerMapPointAtLatitude(panamaCityLoc.latitude) ) ")
println(" in mapPoints: \( distancePanamaToMexixo / MKMetersPerMapPointAtLatitude(panamaCityLoc.latitude) ) ")
mapView.addOverlay(MKCircle(centerCoordinate: panamaCityLoc, radius: distancePanamaToMexixo))
我在最后添加了一些关于不同距离、地图点等的println
,这些产生了以下输出:
Distance Panama City to Mexico City according to dateandtime.info: 2410 km
Distance Panama City to Mexico: 2408968.73912751 CLLocationDistance (or m)
meters/MapPoint at latitude Panama City: 0.146502523951599
in mapPoints: 16443189.333198
如果有人想在 Swift 3 中实现它,我创建了一个 MKPolygon 的子类,它根据 Kellan 的出色回答呈现测地线圆。
只需使用
创建它let circle = MKGeodesicCircle(center: CLLocationCoordinate2D, radius: 100000)
这是 Swift 文件
import UIKit
import Darwin
import CoreLocation
import MapKit
class MKGeodesicCircle: MKPolygon {
convenience init(center: CLLocationCoordinate2D, radius: CLLocationDistance) {
self.init(center: center, radius: radius, fromRadial: 0, toRadial: 360)
}
convenience init(center: CLLocationCoordinate2D, radius: CLLocationDistance, fromRadial: CLLocationDegrees, toRadial:CLLocationDegrees) {
let currentCoord:CLLocationCoordinate2D!
currentCoord = center
let coords = MKGeodesicCircle.getCoordinates(currentCoord: currentCoord, radius: radius, fromRadial: fromRadial, toRadial: toRadial)
self.init()
self.init(coordinates: coords, count: coords.count)
}
class func getCoordinates(currentCoord: CLLocationCoordinate2D!, radius: CLLocationDistance, fromRadial: CLLocationDegrees, toRadial: CLLocationDegrees) -> [CLLocationCoordinate2D] {
let globalRadius: Double = 6371000
let π = M_PI
var coordinates = [CLLocationCoordinate2D]()
let lat1: Double = (currentCoord!.latitude)
let long1: Double = (currentCoord!.longitude) + 180
let factor = 3
let φ1: Double = lat1 * (π / 180)
let λ1: Double = long1 * (π / 180)
let angularDistance = radius / globalRadius
var metersToNorthPole: Double = 0
var metersToSouthPole: Double = 0
for _ in Int(lat1)..<89 {
metersToNorthPole = metersToNorthPole + 111132.92 - (559.82 * cos(2 * φ1)) + (1.175 * cos(4 * φ1))
}
for _ in stride(from: lat1, through: -89, by: -1) {
metersToSouthPole = metersToSouthPole + 111132.92 - (559.82 * cos(2 * φ1)) + (1.175 * cos(4 * φ1))
}
var startingBearing = Int(fromRadial) - 180
var endingBearing = Int(toRadial) - 180
if metersToNorthPole - radius <= 0 {
endingBearing = Int(fromRadial) - 0
startingBearing = Int(toRadial) * -1
}
for i in stride(from: startingBearing, through: endingBearing, by: factor) {
//for var i = startingBearing; i <= endingBearing; i += factor {
let bearing = Double(i)
let bearingInRadians: Double = bearing * (π / 180)
let φ2: Double = asin(sin(φ1) * cos(angularDistance)
+ cos(φ1) * sin(angularDistance)
* cos(bearingInRadians)
)
let λ2 = atan2(
sin(bearingInRadians) * sin(angularDistance) * cos(φ1),
cos(angularDistance) - sin(φ1) * sin(φ2)
) + λ1
let lat2 = φ2 * (180 / π)
var long2 = ( ((λ2.truncatingRemainder(dividingBy: (2 * π)) ) - π)) * (180.0 / π)
if long2 < -180 {
long2 = 180 + (long2.truncatingRemainder(dividingBy: 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
}
}