我正在使用iOS Mapbox
SDK,我需要在多边形中找到中心坐标,因为我想在中心坐标中添加一个标记。我怎么能在Swift中做到这一点?
func drawPolygonFeature(shapes: [MGLShape & MGLFeature]) {
let shapeSource = MGLShapeSource(identifier: "MultiPolygonShapeSource", shapes: shapes, options: nil)
let lineStyleLayer = MGLLineStyleLayer(identifier: "LineStyleLayer", source: shapeSource)
lineStyleLayer.lineColor = NSExpression(forConstantValue: UIColor.purple)
lineStyleLayer.lineOpacity = NSExpression(forConstantValue: 0.5)
lineStyleLayer.lineWidth = NSExpression(forConstantValue: 4)
DispatchQueue.main.async(execute: {[weak self] in
guard let self = self else { return }
self.mapView.style?.addSource(shapeSource)
self.mapView.style?.addLayer(lineStyleLayer)
let multiPolygonFeature = shapes.first as? MGLMultiPolygonFeature
if let centerCoordinate = multiPolygonFeature?.polygons.first?.coordinate {
self.mapView.centerCoordinate = centerCoordinate
// but centerCoordinate var does not contain the center coordinate
}
})
}
我想你可以在这里找到你需要的所有信息:https://blog.mapbox.com/a-new-algorithm-for-finding-a-visual-center-of-a-polygon-7c77e6492fbc
它链接到一个javascript模块(https://github.com/mapbox/polylabel),但我希望你可以轻松地重写它。
为了不仅仅是共享网址,我在这里复制了博客文章中最相关的信息:
基本原则是使用四叉树。主要概念是将二维空间递归细分为四个象限。从覆盖多边形的几个大单元开始。递归地将它们细分为四个较小的单元格,探测单元中心作为候选单元并丢弃不可能包含比我们已经找到的解决方案更好的解决方案的单元格。
我们怎么知道细胞是否可以丢弃?让我们考虑一个多边形上的样本方格单元格:
如果我们知道从单元格中心到多边形的距离(上方的距离),单元格内的任何点都不能与dist + radius有更大的距离,其中radius是单元格的半径。如果潜在的单元格最大值小于或等于我们已处理的单元格的最佳距离(在给定精度内),我们可以安全地丢弃该单元格。
为了使这个假设适用于任何单元格而不管它们的中心是否在多边形内部,我们需要使用有符号距离到多边形 - 如果一个点在多边形内部则为正,如果在外部则为负。
解决方案取决于您的要求。如果要求中心位于多边形内,Paul van Roosendaal提供的解决方案是完美的。 但是,在许多情况下,如果中心也可以在多边形之外,则会更好。想一想,例如一个看起来像一个几乎闭合的环的多边形。在这种情况下,中心大致位于环的中心可能更自然,并且中心计算为多边形的the centroid。 在引用的wiki帖子中,this reference讨论了如何计算它,并展示了许多不同语言的实现。 我已将Java版本翻译成Swift,并添加了一个示例:
func signedPolygonArea(polygon: [CGPoint]) -> CGFloat {
let nr = polygon.count
var area: CGFloat = 0
for i in 0 ..< nr {
let j = (i + 1) % nr
area = area + polygon[i].x * polygon[j].y
area = area - polygon[i].y * polygon[j].x
}
area = area/2.0
return area
}
func polygonCenterOfMass(polygon: [CGPoint]) -> CGPoint {
let nr = polygon.count
var centerX: CGFloat = 0
var centerY: CGFloat = 0
var area = signedPolygonArea(polygon: polygon)
for i in 0 ..< nr {
let j = (i + 1) % nr
let factor1 = polygon[i].x * polygon[j].y - polygon[j].x * polygon[i].y
centerX = centerX + (polygon[i].x + polygon[j].x) * factor1
centerY = centerY + (polygon[i].y + polygon[j].y) * factor1
}
area = area * 6.0
let factor2 = 1.0/area
centerX = centerX * factor2
centerY = centerY * factor2
let center = CGPoint.init(x: centerX, y: centerY)
return center
}
let point0 = CGPoint.init(x: 1, y: 1)
let point1 = CGPoint.init(x: 2, y: 2)
let point2 = CGPoint.init(x: 4, y: 3)
let point3 = CGPoint.init(x: 4, y: 5)
let point4 = CGPoint.init(x: 3, y: 4)
let point5 = CGPoint.init(x: 2, y: 4)
let point6 = CGPoint.init(x: 1, y: 5)
let point7 = CGPoint.init(x: 3, y: 2)
let polygon = [point0, point1, point2, point3, point4, point5, point6, point7]
let center = polygonCenterOfMass(polygon: polygon)