计算一个多边形的重心点坐标 (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. 中国知网-任意多边形匀面重心的计算方法
相关文章
相关标签/搜索