在MKMapView中计算MKPolygon的面积

7

我不知道如何在MKMapView上计算面积。有没有解决这个问题的人?

这是我的代码,但它返回的结果太多了:

func ringArea() -> Double{
    var area: Double = 0

    if templocations.count > 2 {
        var p1,p2:CLLocationCoordinate2D

        for var i = 0; i < templocations.count - 1; i++ {
            var loc = templocations[i] as CLLocation
            p1 = CLLocationCoordinate2D(latitude: loc.coordinate.latitude, longitude: loc.coordinate.longitude)

            loc = templocations[i+1] as CLLocation
            p2 = CLLocationCoordinate2D(latitude: loc.coordinate.latitude, longitude: loc.coordinate.longitude)

            var sinfunc: Float = (2 + sinf(Float(degreeToRadiant(p1.latitude))) + sinf(Float(degreeToRadiant(p2.latitude))))

            area += degreeToRadiant(p2.longitude - p1.longitude) * Double(sinfunc)
        }
        area = area * kEarthRadius * kEarthRadius / 2;
    }
    return area
}

1
你尝试过将 https://dev59.com/-WEh5IYBdhLWcg3ww11p 翻译成 Swift 吗? - Martin R
4个回答

12

Stefan的回答使用了Swift 5.0实现:

import MapKit
let kEarthRadius = 6378137.0

func radians(degrees: Double) -> Double {
    return degrees * .pi / 180
}

func regionArea(locations: [CLLocationCoordinate2D]) -> Double {

    guard locations.count > 2 else { return 0 }
    var area = 0.0

    for i in 0..<locations.count {
        let p1 = locations[i > 0 ? i - 1 : locations.count - 1]
        let p2 = locations[i]

        area += radians(degrees: p2.longitude - p1.longitude) * (2 + sin(radians(degrees: p1.latitude)) + sin(radians(degrees: p2.latitude)) )
    }
    area = -(area * kEarthRadius * kEarthRadius / 2)
    return max(area, -area) // In order not to worry about is polygon clockwise or counterclockwise defined.
}

2
你救了我的一天!非常感谢,兄弟! - Cleversou
1
请原谅我的无知。这个返回的单位是什么?米吗? - Julian B.
1
@JulianB。是的!平方米。如果您需要其他单位,您需要设置kEarthRadius的值为所需单位。 - Avt
@Avt,我在哪里可以获取kEarthRadius值的列表? - Anirudha Mahale

5

polygon on a sphere area 1 polygon on a sphere area 2

#define kEarthRadius 6378137
@implementation MKPolygon (AreaCalculation)

- (double) area {
  double area = 0;
  NSMutableArray *coords = [[self coordinates] mutableCopy];
  [coords addObject:[coords firstObject]];

  if (coords.count > 2) {
    CLLocationCoordinate2D p1, p2;
    for (int i = 0; i < coords.count - 1; i++) {
      p1 = [coords[i] MKCoordinateValue];
      p2 = [coords[i + 1] MKCoordinateValue];
      area += degreesToRadians(p2.longitude - p1.longitude) * (2 + sinf(degreesToRadians(p1.latitude)) + sinf(degreesToRadians(p2.latitude)));
    }

    area = - (area * kEarthRadius * kEarthRadius / 2);
  }
  return area;
}
- (NSArray *)coordinates {
  NSMutableArray *points = [NSMutableArray arrayWithCapacity:self.pointCount];
  for (int i = 0; i < self.pointCount; i++) {
    MKMapPoint *point = &self.points[i];
    [points addObject:[NSValue valueWithMKCoordinate:MKCoordinateForMapPoint(* point)]];
  }
  return points.copy;
}

double degreesToRadians(double radius) {
  return radius * M_PI / 180;
}

1
“球面情况”方程式详见 Chamberlain & Duquette 的《关于球面多边形的一些算法》(JPL出版物07-3,加州理工学院,2007年)。 - Roselle Tanner
@RoselleTanner 谢谢你的引用,是的,就是那个。我计划更新我的答案,加入球面情况(自从我的回答以来,这两年间,我已经忘记了我实际上实现了他们的球面算法,而不是平面算法)。 - StefanS
1
你使用了“球体情况-近似解”方程。你的文章还描述了一个“球体情况-精确解”。我使用了相同的球面过量概念来实现面积计算。如果你想看一下,我已经发表了它。 - Roselle Tanner

2
我使用球面超量法计算面积。StefanS的答案中有一个很好的解释,说明了球面超量法的工作原理。它使用三角形,其中一条边是多边形段,另外两条边是连接到极点的子午线。维基百科建议使用具有相同原理的“球面四边形”。它使用多边形段、赤道和连接赤道的两条子午线。

StefanS的答案使用了一个近似面积的方程。我想要更精确。我实现了3个不同的函数:近似值、精确的球面三角形和精确的球面四边形。

在我的用例中时间可以忽略不计,但这里是基准:

0.208- 近似时间基准

0.517- 精确的球面四边形时间基准

0.779- 精确的球面三角形时间基准

额外的好处是,四边形解决方案不需要调整对经度180度的情况,而近似方法需要。四边形解决方案比三角形解决方案简单得多。在我的测试中,四边形和三角形解决方案之间结果的最大差异约为0.0000001%。

我使用了维基百科球面三角学、面积和球面超量法中的这个方程式:https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess

enter image description here

以及StefanS的文章:“球形情况”方程式详见“一些用于球面多边形的算法”(JPL出版物07-3,加州理工学院,2007年)。

func areaUsingQuadrilateralSphericalExcess(_ coordinates: [CLLocationCoordinate2D]) -> Double {
// the poles cannot be in the polygon
    guard coordinates.count > 2 else { return 0 }
    let kEarthRadius = 6378137.0
    var sum = 0.0

    for i in 0..<coordinates.count {
        let p1Latitude = coordinates[i].latitude.degreesToRadians
        let p1Longitude = coordinates[i].longitude.degreesToRadians
        let p2Latitude = coordinates[i + 1].latitude.degreesToRadians
        let p2Longitude = coordinates[i + 1].longitude.degreesToRadians

        let sphericalExcess = 2 * atan((sin(0.5 * (p2Latitude + p1Latitude))/cos(0.5 * (p2Latitude - p1Latitude))) * tan((p2Longitude - p1Longitude)/2))
        sum += sphericalExcess
    }
    return abs(sum * kEarthRadius * kEarthRadius)   // if a clockwise polygon, sum will be negative
}

喜欢它。谢谢你通知我! - StefanS

0
如果您的区域尺寸不是如此巨大以至于需要调整地球椭球体的影响,您仍然可以使用2D几何来计算表面积。
假设p1和p2之间相距不到几度,p1是圆心,p2是半径上的任意点(径向点),则以下函数将返回以平方米为单位的面积。
func findCircleArea(centre: CLLocation, radialLocation: CLLocation) -> Double {
    // Find the distance from the centre to the radial location in metres
    let radius = centre.distanceFromLocation(radialLocation)

    // Apply standard 2D area calculation for a circle
    let area = M_PI * radius * radius

    // Answer is in square metres
    return area
}

如果p1和p2都是圆周上的点,则使用此函数:
func findCircleAreaBetweenPoints(p1: CLLocation, p2: CLLocation) -> Double {
    // Find the distance from the two points and halve it
    let radius = p1.distanceFromLocation(p2) / 2.0

    // Apply standard 2D area calculation for a circle
    let area = M_PI * radius * radius

    // Answer is in square metres
    return area
}

如果您不使用公制测量单位,请相应调整答案。

请记住,所有表面积计算都是近似值,因为假设标准的WGS84椭球在标准海平面上测量时并未考虑给定位置的高程变化。distanceFromLocation()计算使用大圆计算获取点之间的正确距离,因此半径是合理准确的,但当半径过大时,近似误差也会增加。


好的,谢谢回复!现在我需要为每个点调用 findCircleAreaBetweenPoints... 吗?像这样?func ringArea(locations: [CLLocation]) -> Double{ var area: Double = 0 if locations.count > 2 { var p1,p2:CLLocationCoordinate2D var i: Int = 0 while i < locations.count-1 { var loc = locations[i] as CLLocation var loc2 = locations[i+1] as CLLocation i++ area += findCircleAreaBetweenPoints(loc, p2: loc2) } } return area } - b4um11
有可能吗,当我在一个区域内拥有更多的位置时误差会更大吗?因为如果我使用所有点进行计算,结果肯定是错误的,但如果总是取第十个值,结果就非常好。在该区域内是否存在点数限制? - b4um11

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接