计算一个多边形的重心点坐标(准确版)

2019-11-29 13:04:47

主要用途,就是在百度地图上画多边形,然后我们要得到这个多边形的重心。

后来我发现了,百度地图,map对象直接有这个接口。大家可以仔细看下api。

接下来,我们还是要看下这个算法问题。

参考地址 计算一个多边形的重心点坐标



背景介绍与问题分析

在之前的 《如何判断一个多边形是否合法》  一文中有提到,用无人机规划飞行路线前,往往需要框选一个多边形的区域。

而在地图控件上显示这个多边形区域时,往往会遇到这样一个需求:需要把所要测绘的多边形区域移动到地图中心。

实现这个需求的基本思路就是:获取到多边形区域的重心点坐标,然后利用地图控件的 setCenter方法,就可以把地图的显示中心移动到多边形区域重心了。那么问题来了,如何求出一个多边形的重心点坐标呢?

这里所说的重心,也常常叫几何中心

这里首先给出一个公式:

平面多边形 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}

公式参考: 维基百科

一般来说我们可以给多边形进行三角剖分,而 \sum{A_i} 即为多边形的总面积,那么这个公式可以理解为:

多边形重心横坐标 = 多边形剖分的每一个三角形重心的横坐标 * 该三角形的面积之和 / 多边形总面积

多边形重心纵坐标 = 多边形剖分的每一个三角形重心的纵坐标 * 该三角形的面积之和 / 多边形总面积

所以这里就把问题拆分成了三个小问题:

  • 求每个剖分出来的三角形的重心。

  • 求每个剖分出来的三角形的面积。

  • 求多边形的面积。

算法解析

1. 求三角形的重心


三角形重心


三角形的重心:三条中线的交点。其中重心到其中一个顶点的距离是重心到该顶点对边中点的距离的2倍。
即:GC = 2 * GP,也就是说重心坐标在 CP 线段上距离 AB 的中点 P 的 1/3 处。
假设 A,B,C 三点的坐标为:


那么通过简单坐标计算,可以得出其重心坐标为 (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. 中国知网-任意多边形匀面重心的计算方法



  • 2019-01-29 14:33:07

    child_process中spawn和exec方法的使用

    child_process是nw.exe的一个内置模块,通过它可以实现创建多线程,并可实现主线程和子线程之间的通信。child_process模块中主要使用有两个方法spawn和exec,这两个方法都可以用来创建子线程。除了spawn和exec外,child_process模块还有execFile,fork,spawnSync,execFileSync,execSync,它们都是基于spawn的不同封装。 --------------------- 作者:黄泽平 来源:CSDN 原文:https://blog.csdn.net/zeping891103/article/details/52230175 版权声明:本文为博主原创文章,转载请附上博文链接!

  • 2019-01-29 14:50:51

    Node.js 编写跨平台 spawn 语句

    Node.js 是跨平台的,也就是说它能运行在 Windows、OSX 和 Linux 平台上。很多 Node.js 开发者都是在 OSX 上做开发的,然后再将代码部署到 Linux 服务器上。由于 OSX 和 Linux 都是基于 Unix 的,因此两者共性很多。Windows 也是 Node.js 官方支持的平台,只要你通过正确的方式写代码,就能在各个平台上毫无压力的跑起来。

  • 2019-01-30 17:53:21

    视图与临时表

    视图与表的不同之处:视图是一个虚表,即视图所对应的数据不进行实际存储,数据库只存储视图的定义,对视图的数据进行操作时,系统根据视图的定义去操作与视图相关联的基本表。

  • 2019-02-01 08:43:59

    JS 随机排序算法

    使用JS编写一个方法 让数组中的元素每次刷新随机排列