如何在MGLMultiPolygonFeature中找到中心坐标

时间:2018-12-01 10:22:05

标签: swift mapbox mkpolygon

我使用的是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
        }
    })
}

2 个答案:

答案 0 :(得分:4)

解决方案取决于您的要求。如果要求中心在多边形内,那么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)

答案 1 :(得分:2)

我想您可以在这里找到所需的所有信息:https://blog.mapbox.com/a-new-algorithm-for-finding-a-visual-center-of-a-polygon-7c77e6492fbc

它链接到javascript模块(https://github.com/mapbox/polylabel),但是我希望您可以轻松地重写它。

为了不只是分享网址,我在这里复制了博客文章中最相关的信息:

基本原理是使用四叉树。主要概念是将二维空间递归细分为四个象限。 从覆盖多边形的几个大单元格开始。将它们递归地细分为四个较小的单元格,将单元格中心作为候选者进行探测,并丢弃可能包含比我们已经发现的解决方案更好的解决方案的单元格。

我们如何知道是否可以丢弃细胞?让我们考虑一个多边形上的正方形样本单元:

enter image description here

如果我们知道从像元中心到多边形的距离(上面的距离),则像元内的任何点到多边形的距离都不能大于dist + radius,其中radius是像元的半径。如果该潜在像元最大值小于或等于我们已经处理过的像元的最佳距离(在给定的精度内),则可以安全地丢弃该像元。

要使此假设对任何单元格正确工作,无论其中心是否在多边形内,我们都需要使用到多边形的带符号距离-如果点在多边形内,则为正;如果点在多边形外,则为负。