Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add polygon area calculation #21

Merged
merged 15 commits into from
Jan 8, 2018
Merged
6 changes: 6 additions & 0 deletions Turf.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
052521D81FC47A1300DD266A /* polygon.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 052521D71FC47A0300DD266A /* polygon.geojson */; };
052521D91FC47A1900DD266A /* polygon.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 052521D71FC47A0300DD266A /* polygon.geojson */; };
353E9B101F3E093A007CFA23 /* Turf.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 353E9B071F3E093A007CFA23 /* Turf.framework */; };
353E9B1E1F3E09D3007CFA23 /* CoreLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356D24531F17948C003BBB9D /* CoreLocation.swift */; };
353E9B1F1F3E09D8007CFA23 /* Turf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35650B0A1F150DDB00B5C158 /* Turf.swift */; };
Expand Down Expand Up @@ -41,6 +43,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
052521D71FC47A0300DD266A /* polygon.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = polygon.geojson; sourceTree = "<group>"; };
353E9B071F3E093A007CFA23 /* Turf.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Turf.framework; sourceTree = BUILT_PRODUCTS_DIR; };
353E9B0F1F3E093A007CFA23 /* TurfMacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TurfMacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
35650AF01F150DC500B5C158 /* Turf.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Turf.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -135,6 +138,7 @@
isa = PBXGroup;
children = (
356D24581F179B72003BBB9D /* dc-line.geojson */,
052521D71FC47A0300DD266A /* polygon.geojson */,
);
path = Fixtures;
sourceTree = "<group>";
Expand Down Expand Up @@ -291,6 +295,7 @@
buildActionMask = 2147483647;
files = (
35CB7F6F1F798A51008A18C8 /* dc-line.geojson in Resources */,
052521D91FC47A1900DD266A /* polygon.geojson in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -306,6 +311,7 @@
buildActionMask = 2147483647;
files = (
356D24591F179B72003BBB9D /* dc-line.geojson in Resources */,
052521D81FC47A1300DD266A /* polygon.geojson in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
70 changes: 69 additions & 1 deletion Turf/Turf.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ public typealias LocationRadians = Double
public typealias RadianDistance = Double
public typealias RadianDirection = Double

let metersPerRadian = 6_373_000.0

let metersPerRadian: CLLocationDistance = 6_373_000.0
// WGS84 equatorial radius as specified by the International Union of Geodesy and Geophysics
let equatorialRadius: CLLocationDistance = 6_378_137

/**
A `RadianCoordinate2D` is a coordinate represented in radians as opposed to
Expand Down Expand Up @@ -285,3 +288,68 @@ public struct Polyline {
return closestCoordinate
}
}


/**
Creates a `Ring` struct that represents a closed figure that is bounded by three or more straight line segments.
*/
public struct Ring {
var coordinates: [CLLocationCoordinate2D]

/**
* Calculate the approximate area of the polygon were it projected onto the earth, in square meters.
* Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
*
* Reference:
* Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
* Laboratory, Pasadena, CA, June 2007 https://trs.jpl.nasa.gov/handle/2014/41271
*
*/
public var area: Double {
var area: Double = 0
let coordinatesCount: Int = coordinates.count

if coordinatesCount > 2 {
for index in 0..<coordinatesCount {

let controlPoints: (CLLocationCoordinate2D, CLLocationCoordinate2D, CLLocationCoordinate2D)

if index == coordinatesCount - 2 {
controlPoints = (coordinates[coordinatesCount - 2],
coordinates[coordinatesCount - 1],
coordinates[0])
} else if index == coordinatesCount - 1 {
controlPoints = (coordinates[coordinatesCount - 1],
coordinates[0],
coordinates[1])
} else {
controlPoints = (coordinates[index],
coordinates[index + 1],
coordinates[index + 2])
}

area += (controlPoints.2.longitude.toRadians() - controlPoints.0.longitude.toRadians()) * sin(controlPoints.1.latitude.toRadians())
}

area *= equatorialRadius * equatorialRadius / 2
}
return area
}
}

/**
Creates a `Polygon` struct from an outer ring and optional inner rings.
Inner rings represent any holes the polygon may have.
*/
public struct Polygon {
var outerRing: Ring
var innerRings: [Ring]

// Ported from https://github.com/Turfjs/turf/blob/a94151418cb969868fdb42955a19a133512da0fd/packages/turf-area/index.js

public var area: Double {
return abs(outerRing.area) - innerRings
.map { abs($0.area) }
.reduce(0, +)
}
}
53 changes: 53 additions & 0 deletions TurfTests/Fixtures/polygon.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-109.05029296875,
37.00255267215955
],
[
-102.0849609375,
37.020098201368114
],
[
-102.041015625,
41.0130657870063
],
[
-109.072265625,
40.97989806962013
],
[
-109.05029296875,
37.00255267215955
]
],
[
[
-108.56689453125,
40.6306300839918
],
[
-108.61083984375,
37.43997405227057
],
[
-102.50244140624999,
37.405073750176925
],
[
-102.4365234375,
40.66397287638688
],
[
-108.56689453125,
40.6306300839918
]
]
]
}
}
17 changes: 17 additions & 0 deletions TurfTests/TurfTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,21 @@ class TurfTests: XCTestCase {
let b = radian.toDegrees()
XCTAssertEqual(b, 229, accuracy: 1)
}

func testPolygonArea() {
let json = Fixture.JSONFromFileNamed(name: "polygon")
let geometry = json["geometry"] as! [String: Any]
let geoJSONCoordinates = geometry["coordinates"] as! [[[Double]]]
let allRings = geoJSONCoordinates.map {
$0.map { CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) }
}
let outerRing = Ring(coordinates: allRings.first!)
let innerRings = allRings.suffix(from: 1).map {
Ring(coordinates: $0)
}

let polygon = Polygon(outerRing: outerRing, innerRings: innerRings)

XCTAssertEqual(polygon.area, 78588446934.43, accuracy: 0.1)
}
}