計算一個多邊形的重心點座標 (Swift 代碼實現)

背景介紹與問題分析

在以前的《如何判斷一個多邊形是否合法》 一文中有提到,用無人機規劃飛行路線前,每每須要框選一個多邊形的區域。html

而在地圖控件上顯示這個多邊形區域時,每每會遇到這樣一個需求:須要把所要測繪的多邊形區域移動到地圖中心。git

實現這個需求的基本思路就是:獲取到多邊形區域的重心點座標,而後利用地圖控件的 setCenter方法,就能夠把地圖的顯示中心移動到多邊形區域重心了。那麼問題來了,如何求出一個多邊形的重心點座標呢?算法

這裏所說的重心,也經常叫幾何中心swift

這裏首先給出一個公式:數組

平面多邊形 X 能夠被剖分爲 n個有限的簡單圖形 X_1,X_2,....X_n,這些簡單圖形的重心點爲 C_1,面積爲 A_1,那麼這個平面多邊形的重心點座標爲 (C_x,C_y)函數

C_x = \frac{\sum C_{i_x} A_i}{\sum A_i}, C_y = \frac{\sum C_{i_y} A_i}{\sum A_i}

公式參考: 維基百科post

通常來講咱們能夠給多邊形進行三角剖分,而 \sum{A_i} 即爲多邊形的總面積,那麼這個公式能夠理解爲:ui

多邊形重心橫座標 = 多邊形剖分的每個三角形重心的橫座標 * 該三角形的面積之和 / 多邊形總面積spa

多邊形重心縱座標 = 多邊形剖分的每個三角形重心的縱座標 * 該三角形的面積之和 / 多邊形總面積.net

因此這裏就把問題拆分紅了三個小問題:

  • 求每一個剖分出來的三角形的重心。
  • 求每一個剖分出來的三角形的面積。
  • 求多邊形的面積。

算法解析

1. 求三角形的重心

三角形重心
三角形的重心:三條中線的交點。其中重心到其中一個頂點的距離是重心到該頂點對邊中點的距離的2倍。
即:GC = 2 * GP,也就是說重心座標在 CP 線段上距離 AB 的中點 P 的 1/3 處。 假設 A,B,C 三點的座標爲:

A:(x_1,y_1),B:(x_2,y_2),C:(x_3,y_3)

那麼經過簡單座標計算,能夠得出其重心座標爲 (x,y)

x = \frac{(x_1+x_2+x_3)}{3} , y = \frac{(y_1+y_2+y_3)}{3}

2. 求三角形面積

計算三角形的面積,咱們這裏利用 向量積來計算,咱們知道平面中的兩個向量的叉乘的模等於以這兩個向量爲邊的平行四邊形的面積,那麼以這個兩個向量爲邊的三角形,則是這個平行四邊形的面積的一半。

參考:向量叉積

如上圖,已知平面上兩點 A:(x_1,y_1),B(x_2,y_2) ,以 A,B和座標原點 P(0,0) 構成的三角形的面積 S 爲:

S=\frac{\vec{PB}\times\vec{PA}}{2} = \frac{x_2y_1 - x_1y_2 }{2}

這裏給出運算草稿:

爲何這裏咱們會以原點做爲第三個點構成三角形呢?實際上是跟接下來求多邊形面積是有關聯的。

3. 求多邊形的面積

咱們在上面給出的求平面多邊形重心的公式中有說到,通常咱們會把多邊形剖分爲多個三角形。 那麼這個剖分點 P 咱們能夠設在哪裏呢?這裏先給出結論:這個剖分點能夠設置在多邊形的內部,也能夠設置到外部。

爲何這個剖分點能夠設置到外部呢?咱們能夠經過簡單的三角形狀況來推廣到多邊形的狀況。 對於三角形ABC,咱們把剖分點設置在其外部 P 的一點上

若是你們還記得 《如何判斷一個多邊形是否合法》 一文中有講過向量叉積是有正負之分的,而且根據上面所說的計算三角形面積,那麼以 P 爲剖分點,經過向量積能夠得出這個三角形的面積 A 爲:

A = \frac{1}{2}(\vec{PB} \times \vec{PC} + \vec{PC} \times \vec{PA} + \vec{PA} \times \vec{PB})

由於 向量PB 在 向量PA 的順時針方向,因此 \vec{PA} \times \vec{PB} 的結果是負數的。那麼上面的面積計算公式其實就能夠理解爲:

三角形ABC的面積 = 三角形PBC面積 + 三角形PCA面積 - 三角形PAB面積

假設這四個點的座標爲:P(x_0,y_0), A(x_1,y_1), B(x_2,y_2), C(x_3,y_3),經過上面的公式進行計算,具體的演算過程我就不給出了,這裏直接給出計算結果:

A = x_1y_2-x_2y_1+x_2y_3-x_3y_2+x_3y_1-x_1y_3

咱們能夠發現,計算結果中沒有 x_0,y_0 的項,由於它們在計算過程當中給消去了,數學就是這麼奇妙!因此咱們能夠得出一個結論,多邊形的面積結果與這個剖分點的位置是無關的。那麼爲了計算方便,咱們固然選擇把這個 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)
}
複製代碼

對應上面代碼的註釋:

  1. 拿到多邊形上連續兩個點的座標,咱們能夠把 latitude 看作橫座標,longitude 是縱座標。
  2. 利用向量叉乘計算這兩個點與原點組成的三角形的面積。
  3. 全部面積之和得出多邊形的面積,就是求公式 C_x = \frac{\sum C_{i_x} A_i}{\sum A_i} 中的 \sum A_i
  4. (lat + nextLat) / 3 是以這兩個點和原點組成的三角形的重心橫座標,這樣的累加gravityLat += tempArea * (lat + nextLat) / 3 實際上是求公式 C_x = \frac{\sum C_{i_x} A_i}{\sum A_i} 中的 \sum C_{i_x} A_i 的值。
  5. 到這一步就簡單了,直接套用公式 C_x = \frac{\sum C_{i_x} A_i}{\sum A_i}

參考資料

  1. 維基百科-Centroid
  2. 維基百科-叉積
  3. cnblogs-用向量積(叉積)計算三角形的面積和多邊形面積
  4. 知乎-兩個向量的叉乘爲何是面積
  5. 中國知網-任意多邊形勻面重心的計算方法
相關文章
相關標籤/搜索