在以前的《如何判斷一個多邊形是否合法》 一文中有提到,用無人機規劃飛行路線前,每每須要框選一個多邊形的區域。html
而在地圖控件上顯示這個多邊形區域時,每每會遇到這樣一個需求:須要把所要測繪的多邊形區域移動到地圖中心。git
實現這個需求的基本思路就是:獲取到多邊形區域的重心點座標,而後利用地圖控件的 setCenter
方法,就能夠把地圖的顯示中心移動到多邊形區域重心了。那麼問題來了,如何求出一個多邊形的重心點座標呢?算法
這裏所說的重心,也經常叫幾何中心swift
這裏首先給出一個公式:數組
平面多邊形 能夠被剖分爲 n個有限的簡單圖形 ,這些簡單圖形的重心點爲 ,面積爲 ,那麼這個平面多邊形的重心點座標爲 函數
公式參考: 維基百科post
通常來講咱們能夠給多邊形進行三角剖分,而 即爲多邊形的總面積,那麼這個公式能夠理解爲:ui
多邊形重心橫座標 = 多邊形剖分的每個三角形重心的橫座標 * 該三角形的面積之和 / 多邊形總面積spa
多邊形重心縱座標 = 多邊形剖分的每個三角形重心的縱座標 * 該三角形的面積之和 / 多邊形總面積.net
因此這裏就把問題拆分紅了三個小問題:
那麼經過簡單座標計算,能夠得出其重心座標爲
計算三角形的面積,咱們這裏利用 向量積
來計算,咱們知道平面中的兩個向量的叉乘的模等於以這兩個向量爲邊的平行四邊形的面積,那麼以這個兩個向量爲邊的三角形,則是這個平行四邊形的面積的一半。
參考:向量叉積
如上圖,已知平面上兩點 ,以 A,B和座標原點 構成的三角形的面積 S 爲:
這裏給出運算草稿:
爲何這裏咱們會以原點做爲第三個點構成三角形呢?實際上是跟接下來求多邊形面積是有關聯的。
咱們在上面給出的求平面多邊形重心的公式中有說到,通常咱們會把多邊形剖分爲多個三角形。 那麼這個剖分點 P 咱們能夠設在哪裏呢?這裏先給出結論:這個剖分點能夠設置在多邊形的內部,也能夠設置到外部。
爲何這個剖分點能夠設置到外部呢?咱們能夠經過簡單的三角形狀況來推廣到多邊形的狀況。 對於三角形ABC,咱們把剖分點設置在其外部 P 的一點上
若是你們還記得 《如何判斷一個多邊形是否合法》 一文中有講過向量叉積是有正負之分的,而且根據上面所說的計算三角形面積,那麼以 P 爲剖分點,經過向量積能夠得出這個三角形的面積 A 爲:
由於 向量PB 在 向量PA 的順時針方向,因此 的結果是負數的。那麼上面的面積計算公式其實就能夠理解爲:
三角形ABC的面積 = 三角形PBC面積 + 三角形PCA面積 - 三角形PAB面積
假設這四個點的座標爲:,經過上面的公式進行計算,具體的演算過程我就不給出了,這裏直接給出計算結果:
咱們能夠發現,計算結果中沒有 的項,由於它們在計算過程當中給消去了,數學就是這麼奇妙!因此咱們能夠得出一個結論,多邊形的面積結果與這個剖分點的位置是無關的。那麼爲了計算方便,咱們固然選擇把這個 P 點設置到原點上啦。
那麼只要咱們知道多邊形的每個頂點,經過原點進行剖分紅多個三角形,而後經過向量的叉乘求出每一個三角的面積,最後相加,就能夠求出多邊形的面積了。
好了,說到這裏,咱們已經找到全部知足最開始的計算多邊形重心點座標的全部計算元素了。是時候上代碼了,這裏構建一個函數calculatePolygonGravityCenter(coordinates: [CLLocationCoordinate2D])
,這個函數傳入的參數是多邊形在地圖上的座標點數組。
func calculatePolygonGravityCenter(coordinates: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
var area = 0.0 // 多邊形面積
var gravityLat = 0.0 // 重心點 latitude
var gravityLng = 0.0 // 重心點 longitude
for (index, coordinate) in coordinates.enumerated() {
// 1
let lat = coordinate.latitude
let lng = coordinate.longitude
let nextLat = coordinates[(index + 1) % coordinates.count].latitude
let nextLng = coordinates[(index + 1) % coordinates.count].longitude
// 2
let tempArea = (nextLat * lng - nextLng * lat) / 2.0
// 3
area += tempArea
// 4
gravityLat += tempArea * (lat + nextLat) / 3
gravityLng += tempArea * (lng + nextLng) / 3
}
// 5
gravityLat = gravityLat / area
gravityLng = gravityLng / area
return CLLocationCoordinate2D(latitude: gravityLat, longitude: gravityLng)
}
複製代碼
對應上面代碼的註釋:
(lat + nextLat) / 3
是以這兩個點和原點組成的三角形的重心橫座標,這樣的累加gravityLat += tempArea * (lat + nextLat) / 3
實際上是求公式 中的 的值。