在以前的《如何判断一个多边形是否合法》 一文中有提到,用无人机规划飞行路线前,每每须要框选一个多边形的区域。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
实际上是求公式