【问题标题】:Calculate area of MKPolygon in an MKMapView计算 MKMapView 中 MKPolygon 的面积
【发布时间】:2015-04-08 11:58:00
【问题描述】:

我只是不知道如何计算 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
}

【问题讨论】:

标签: swift mkmapview mapkit


【解决方案1】:

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.
}

【讨论】:

  • 你拯救了我的一天!非常感谢兄弟!
  • 原谅我的无知。这将返回什么单位?米?
  • @JulianB。是的!平方米。如果你想要别的东西,你需要以你需要的单位设置 kEarthRadius 的值。
  • @Avt 我在哪里可以获得 kEarthRadius 值的列表?
【解决方案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;
}

【讨论】:

  • “球形情况”方程在 Chamberlain & Duquette 的“Some Algorithms for Polygons on a Sphere”中有详细说明(JPL 出版物 07-3,加州理工学院,2007 年)
  • @RoselleTanner 谢谢你的引用,是的,就是这样。我打算用球形案例来更新我的答案(在这两年里,自从我回答之后,我忘记了我实际上实现了他们的球形算法,而不是平面算法)
  • 您使用了“球形情况-近似”方程。您的文章还描述了“球形案例-精确解决方案”。我使用相同的球面过剩概念来实现面积。如果您想看一下,它会发布。
【解决方案3】:

我使用球面过剩来计算面积。 StefanS 的回答中的文章很好地解释了球形过剩的工作原理。它使用三角形,一条边是多边形段,另外两条边是连接到极点的经线。维基百科建议使用具有相同原理的“球形四边形”。它使用多边形线段、赤道和连接到赤道的经线的两条边。

StefanS 的回答使用面积近似的方程。我想更准确。我实现了 3 个不同的功能。近似值、精确球面三角形和精确球面四边形。

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

0.208- 近似时间基线

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

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

作为奖励,四边形解决方案不需要对逆子午线进行任何调整,而近似值则需要。四边形解决方案比三角形解决方案简单得多。在我的测试中,四边形和三角形解决方案之间结果的最大差异约为 0.0000001%。

我使用了来自维基百科的球面三角、面积和球面过剩的公式:https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess

还有 StefanS 的文章: “球面情况”方程在 Chamberlain & Duquette 的“Some Algorithms for Polygons on a Sphere”中有详细说明(JPL Publication 07-3,California Institute of Technology,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
}

【讨论】:

  • 爱它。感谢您通知我!
【解决方案4】:

如果您的区域尺寸没有太大以至于您需要根据地球椭球体的效果进行调整,您仍然可以使用 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]) -&gt; Double{ var area: Double = 0 if locations.count &gt; 2 { var p1,p2:CLLocationCoordinate2D var i: Int = 0 while i &lt; locations.count-1 { var loc = locations[i] as CLLocation var loc2 = locations[i+1] as CLLocation i++ area += findCircleAreaBetweenPoints(loc, p2: loc2) } } return area } ?
  • 有没有可能,我在一个区域的位置越多,错误就越高?因为如果我用所有点计算,结果肯定是错误的,但如果总是取第 10 个值,结果非常好。区域内点数有限制吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-25
  • 2011-08-12
  • 2017-09-04
  • 2015-01-16
  • 2011-06-07
  • 2018-01-09
  • 2018-04-15
相关资源
最近更新 更多