在MKMapView中计算MKPolygon的面积

时间:2015-04-08 11:58:00

标签: swift mkmapview mapkit

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

4 个答案:

答案 0 :(得分:5)

Stefan的回答在 Swift 3.0

中实现
import MapKit
let kEarthRadius = 6378137.0

func radians(degrees: Double) -> Double {
    return degrees * M_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.
}

答案 1 :(得分:4)

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

答案 2 :(得分:1)

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

StefanS的答案使用方程式来近似面积。我想更精确些。我实现了3种不同的功能。近似值,精确的球形三角形和精确的球形四边形。

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

0.208-近似时间基线

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

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

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

我使用了维基百科的球形三角学,面积和球形多余度中的这个等式:https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess

enter image description here

StefanS的文章: Chamberlain&Duquette撰写的“球形多边形上的某些算法”(加利福尼亚理工学院,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
}

答案 3 :(得分:0)

如果您所在区域的尺寸不是太大,以至于您需要调整地球椭球体的效果,您仍然可以使用2D几何来计算表面区域。

假设p1和p2相距小于几度且p1是圆的中心,而p2是半径上的任意点(径向点),则以下函数将返回以平方米为单位的面积。 / p>

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()计算使用Great Circle计算来获得点之间的正确距离,因此半径相当准确,但是当半径太大时,近似误差也会增大。