問題描述
我正面臨 MKCircle 外觀的奇怪行為.基本上,我正在嘗試繪制一個半徑為 8500 公里且具有任意中心的圓.這是我的代碼:
I'm facing with a strange behaviour of MKCircle appearance. Basically I'm trying to draw a circle with a radius of 8500 km with an arbitrary center. Here is my code:
private func addCircle() {
mapView.removeOverlays(mapView.overlays)
let circle = MKCircle(centerCoordinate: mapCenter, radius: 8500000.0)
mapView.addOverlay(circle)
}
我還有一個自定義的雙擊手勢處理程序,它覆蓋了地圖視圖的標準處理程序,并允許通過雙擊地圖視圖來更改地圖中心:
I also have a custom double tap gesture handler, which overwrites the standard one for map view and allows to change the map center by double tapping on the map view:
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()
}
結果很奇怪:
您可能會注意到這兩個半徑之間的顯著差異:第二個比第一個大很多!
You may notice a significant difference between those two radiuses: the second one is a way bigger than the first one!
發生了什么,如何讓它們正確顯示?
What's going on and how do I make them appear correctly?
編輯
感謝@blacksquare,我可以更接近解決方案,但北極仍然存在問題:
Thanks to @blacksquare I could get closer to solution, but still have an issue with the north pole:
(小圓圈代表一個中心)
(Small circle jsut represents a center)
推薦答案
根據 Apple 的 MKCircle
文檔:隨著緯度值從赤道向兩極移動,地圖之間的物理距離點變得更小.這意味著需要更多的地圖點來表示相同的距離.因此,隨著圓的中心點遠離赤道并朝向兩極移動,圓形疊加層的邊界矩形會變大."
According to Apple's documentation of 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
之間的文檔似乎存在差異.文檔表明,半徑是以米為單位的距離中心點的度量,在您的示例中顯然不是這種情況.
So as Anna and Warren both mentioned, this isn't a bug--this is the intended behavior. There seems, however, to be a discrepancy in the documentation between boundingMapRect
and radius
. The documentation suggests that the radius is the measure in meters from the center point, which is clearly not the case in your example.
我認為這里發生的情況是,Apple 可能從未打算在您使用它的規模上使用 MKCircle.MKCircle
創建一個 2D 圓,它不能既是圓又是投影圖上圓形區域的精確表示.
I think what's going on here is that Apple probably never intended MKCircle to be used on the scale that you're using it on. MKCircle
creates a 2D circle, which can't be both a circle and an accurate representation of a circular area on a projection map.
現在,如果您要做的只是創建一個不變形且半徑相對于赤道處長度的均勻圓,您可以將赤道處的圓的長度設置為基本半徑,然后計算當前點的半徑比例如下:
Now if all you want to do is create a uniform circle that isn't distorted and has a radius relative to its length at the equator, you can set the length of the circle at the equator as the base radius and then calculate the proportion of the radius at the current point like this:
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 米內的所有空間,那就有點棘手了.首先,您將在雙擊操作中獲取單擊坐標,然后將其用作多邊形的中心.
But if your plan is to accurately cover all space within x meters of the click, it's a bit trickier. First you'll grab the click-coordinate in the double-click action and then use that as the center of a polygon.
@objc private func handleDoubleTap(sender: UITapGestureRecognizer) {
let point = sender.locationInView(mapView)
currentCoord = mapView.convertPoint(point, toCoordinateFromView: mapView)
mapCenter = currentCoord
addPolygon()
}
在 addPolygon
中,獲取您的坐標并設置您的疊加層:
In addPolygon
, get your coordinates and set up your overlays:
private func addPolygon() {
var mapCoords = getCoordinates()
mapView.removeOverlays(mapView.overlays)
let polygon = MKPolygon(coordinates: &mapCoords, count: mapCoords.count)
mapView.addOverlay(polygon)
}
給定一個點、一個方位和一個角距離(坐標之間的距離除以地球的半徑),您可以使用以下公式計算另一個坐標的位置.請務必導入 Darwin
,以便您可以訪問三角函數庫
Given a point, a bearing, and an angular distance (distance between coordinates divided by the earth's radius), you can calculate the location of another coordinate using the following formula. Be sure to import Darwin
so you can have access to a library of trigonometric functions
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
中,我們將度數轉換為弧度,然后在我們的半徑大于到北極或南極的距離時添加更多錨定坐標.
In getCoordinates
we translate degrees to radians, and then add a few more anchoring coordinate in the event that our radius is greater than the distance to the north or south poles.
以下是兩個半徑分別為 8500 公里和 850 公里的極點附近的曲線示例:
Here are a couple examples of curves near the pole with radiuses of 8500km and 850km, respectively:
這是一個帶有附加 MKGeodesicPolyline
疊加層(測地線表示球面上最短的曲線)的最終輸出示例,它顯示了曲線的實際構建方式:
Here's a sample of the final output with an additional MKGeodesicPolyline
overlay (Geodesics represent the shortest possible curve over a spherical surface), that shows how the curve is actually being built:
這篇關于MKMapView MKCircle 渲染一個半徑太大的圓的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!