From d77070d9029c996b75a69a43fae68e5bed69195c Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Fri, 10 Nov 2017 10:24:11 -0500 Subject: [PATCH 01/15] Begin port of polygon area function --- Turf/Turf.swift | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index b4f72d83..3f80a17f 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -285,3 +285,65 @@ public struct Polyline { return closestCoordinate } } + +public struct Polygon { + var radius:Float = 6378137 + var coordinates: [CLLocationCoordinate2D] + + var polygonArea: Float { + return 0 + } + + /** + * Calculate the approximate area of the polygon were it projected onto the earth. + * 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 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 + * + */ + + var ringArea: Float { + var p1:CLLocationCoordinate2D + var p2:CLLocationCoordinate2D + var p3:CLLocationCoordinate2D + var lowerIndex:Int + var middleIndex:Int + var upperIndex:Int + var i:Int + var area:Float = 0 + var coordsLength:Int = coordinates.count + + if (coordsLength > 2) { + for(index, coordinate) in coordinates.enumerated() { + if (index == coordsLength - 2) { + lowerIndex = coordsLength - 2 + middleIndex = coordsLength - 1 + upperIndex = 0 + } else if(index == coordsLength - 1) { + lowerIndex = coordsLength - 1; + middleIndex = 0; + upperIndex = 1; + } else { + lowerIndex = index + middleIndex = index + 1 + upperIndex = index + 2 + } + + p1 = coordinates[lowerIndex] + p2 = coordinates[middleIndex] + p3 = coordinates[upperIndex] + area += (p3.longitude.toRadians() - p1.longitude.toRadians()) * sin(p2.latitude.toRadians()) + } + var RADIUS = 6378137 + + area = area * radius * radius / 2; + + } + + return 0 + } + + +} From e340401825d5f1a9e39a015db41a3b49c60b0eb2 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Tue, 21 Nov 2017 13:36:05 -0500 Subject: [PATCH 02/15] Use doubles instead of floats --- Turf/Turf.swift | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index 3f80a17f..80146330 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -5,6 +5,7 @@ public typealias RadianDistance = Double public typealias RadianDirection = Double let metersPerRadian = 6_373_000.0 +let equitorialRadius:Double = 6378137 /** A `RadianCoordinate2D` is a coordinate represented in radians as opposed to @@ -287,11 +288,10 @@ public struct Polyline { } public struct Polygon { - var radius:Float = 6378137 var coordinates: [CLLocationCoordinate2D] - var polygonArea: Float { - return 0 + var polygonArea: Double { + return 0; } /** @@ -304,7 +304,7 @@ public struct Polygon { * */ - var ringArea: Float { + var ringArea: Double { var p1:CLLocationCoordinate2D var p2:CLLocationCoordinate2D var p3:CLLocationCoordinate2D @@ -312,7 +312,7 @@ public struct Polygon { var middleIndex:Int var upperIndex:Int var i:Int - var area:Float = 0 + var area:Double = 0 var coordsLength:Int = coordinates.count if (coordsLength > 2) { @@ -336,14 +336,11 @@ public struct Polygon { p3 = coordinates[upperIndex] area += (p3.longitude.toRadians() - p1.longitude.toRadians()) * sin(p2.latitude.toRadians()) } - var RADIUS = 6378137 - area = area * radius * radius / 2; + area = area * equitorialRadius * equitorialRadius / 2; } - return 0 + return area } - - } From d89c654f58fdb5bff7d066dbb5ac71de41c952d5 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Tue, 21 Nov 2017 13:36:25 -0500 Subject: [PATCH 03/15] Add test fixtures + preliminary test --- Turf.xcodeproj/project.pbxproj | 6 ++++++ TurfTests/Fixtures/dc-polygon.geojson | 25 +++++++++++++++++++++++++ TurfTests/TurfTests.swift | 8 ++++++++ 3 files changed, 39 insertions(+) create mode 100644 TurfTests/Fixtures/dc-polygon.geojson diff --git a/Turf.xcodeproj/project.pbxproj b/Turf.xcodeproj/project.pbxproj index d0e5695c..f96bfcb8 100644 --- a/Turf.xcodeproj/project.pbxproj +++ b/Turf.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 052521D81FC47A1300DD266A /* dc-polygon.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 052521D71FC47A0300DD266A /* dc-polygon.geojson */; }; + 052521D91FC47A1900DD266A /* dc-polygon.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 052521D71FC47A0300DD266A /* dc-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 */; }; @@ -41,6 +43,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 052521D71FC47A0300DD266A /* dc-polygon.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = "dc-polygon.geojson"; sourceTree = ""; }; 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; }; @@ -135,6 +138,7 @@ isa = PBXGroup; children = ( 356D24581F179B72003BBB9D /* dc-line.geojson */, + 052521D71FC47A0300DD266A /* dc-polygon.geojson */, ); path = Fixtures; sourceTree = ""; @@ -291,6 +295,7 @@ buildActionMask = 2147483647; files = ( 35CB7F6F1F798A51008A18C8 /* dc-line.geojson in Resources */, + 052521D91FC47A1900DD266A /* dc-polygon.geojson in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -306,6 +311,7 @@ buildActionMask = 2147483647; files = ( 356D24591F179B72003BBB9D /* dc-line.geojson in Resources */, + 052521D81FC47A1300DD266A /* dc-polygon.geojson in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/TurfTests/Fixtures/dc-polygon.geojson b/TurfTests/Fixtures/dc-polygon.geojson new file mode 100644 index 00000000..4e9e1f4b --- /dev/null +++ b/TurfTests/Fixtures/dc-polygon.geojson @@ -0,0 +1,25 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + -77.0416259765625, + 39.06611426153784 + ], + [ + -77.32177734375, + 38.843986129756615 + ], + [ + -76.7669677734375, + 38.831149809348744 + ], + [ + -77.0416259765625, + 39.06611426153784 + ] + ] + } +} diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index 004a41a6..26bc705c 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -286,4 +286,12 @@ class TurfTests: XCTestCase { let b = radian.toDegrees() XCTAssertEqual(b, 229, accuracy: 1) } + + func testPolygonArea() { + let json = Fixture.JSONFromFileNamed(name: "dc-polygon") + let coordinates = ((json["geometry"] as! [String: Any])["coordinates"] as! [[Double]]).map { CLLocationCoordinate2D(latitude: $0[0], longitude: $0[1]) } + let polygon = Polygon(coordinates: coordinates) + + XCTAssertEqual(polygon.ringArea, 611152631.86) + } } From 4adfa4f60c32ffe77266a51957eb9c2ae28ac715 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Sun, 26 Nov 2017 16:02:11 -0500 Subject: [PATCH 04/15] Fill out polygonArea --- Turf/Turf.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index 80146330..bea74604 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -291,7 +291,17 @@ public struct Polygon { var coordinates: [CLLocationCoordinate2D] var polygonArea: Double { - return 0; + var area = 0; + + if (!coordinates.isEmpty && coordinates.count > 0) { + + area += abs(ringArea(coordinates[0])) + + for coordinate in coordinates { + area -= abs(ringArea(coordinate)) + } + } + return area; } /** @@ -338,9 +348,7 @@ public struct Polygon { } area = area * equitorialRadius * equitorialRadius / 2; - } - return area } } From a56f773140eb80dd7dddb1387dec011ec39953b7 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Fri, 1 Dec 2017 10:45:10 -0500 Subject: [PATCH 05/15] More refactoring --- Turf.xcodeproj/project.pbxproj | 12 ++++++------ Turf/Turf.swift | 15 +++++++-------- TurfTests/Fixtures/dc-polygon.geojson | 25 ------------------------- TurfTests/Fixtures/polygon.geojson | 19 +++++++++++++++++++ TurfTests/TurfTests.swift | 13 ++++++++++--- 5 files changed, 42 insertions(+), 42 deletions(-) delete mode 100644 TurfTests/Fixtures/dc-polygon.geojson create mode 100644 TurfTests/Fixtures/polygon.geojson diff --git a/Turf.xcodeproj/project.pbxproj b/Turf.xcodeproj/project.pbxproj index f96bfcb8..6a7d74ad 100644 --- a/Turf.xcodeproj/project.pbxproj +++ b/Turf.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 052521D81FC47A1300DD266A /* dc-polygon.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 052521D71FC47A0300DD266A /* dc-polygon.geojson */; }; - 052521D91FC47A1900DD266A /* dc-polygon.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 052521D71FC47A0300DD266A /* dc-polygon.geojson */; }; + 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 */; }; @@ -43,7 +43,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 052521D71FC47A0300DD266A /* dc-polygon.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = "dc-polygon.geojson"; sourceTree = ""; }; + 052521D71FC47A0300DD266A /* polygon.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = polygon.geojson; sourceTree = ""; }; 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; }; @@ -138,7 +138,7 @@ isa = PBXGroup; children = ( 356D24581F179B72003BBB9D /* dc-line.geojson */, - 052521D71FC47A0300DD266A /* dc-polygon.geojson */, + 052521D71FC47A0300DD266A /* polygon.geojson */, ); path = Fixtures; sourceTree = ""; @@ -295,7 +295,7 @@ buildActionMask = 2147483647; files = ( 35CB7F6F1F798A51008A18C8 /* dc-line.geojson in Resources */, - 052521D91FC47A1900DD266A /* dc-polygon.geojson in Resources */, + 052521D91FC47A1900DD266A /* polygon.geojson in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -311,7 +311,7 @@ buildActionMask = 2147483647; files = ( 356D24591F179B72003BBB9D /* dc-line.geojson in Resources */, - 052521D81FC47A1300DD266A /* dc-polygon.geojson in Resources */, + 052521D81FC47A1300DD266A /* polygon.geojson in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Turf/Turf.swift b/Turf/Turf.swift index bea74604..33496309 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -288,10 +288,10 @@ public struct Polyline { } public struct Polygon { - var coordinates: [CLLocationCoordinate2D] + var coordinates: [[CLLocationCoordinate2D]] var polygonArea: Double { - var area = 0; + var area:Double = 0 if (!coordinates.isEmpty && coordinates.count > 0) { @@ -313,8 +313,7 @@ public struct Polygon { * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * */ - - var ringArea: Double { + private func ringArea(_ listOfCoordinates: [CLLocationCoordinate2D]) -> Double { var p1:CLLocationCoordinate2D var p2:CLLocationCoordinate2D var p3:CLLocationCoordinate2D @@ -323,7 +322,7 @@ public struct Polygon { var upperIndex:Int var i:Int var area:Double = 0 - var coordsLength:Int = coordinates.count + var coordsLength:Int = listOfCoordinates.count if (coordsLength > 2) { for(index, coordinate) in coordinates.enumerated() { @@ -341,9 +340,9 @@ public struct Polygon { upperIndex = index + 2 } - p1 = coordinates[lowerIndex] - p2 = coordinates[middleIndex] - p3 = coordinates[upperIndex] + p1 = listOfCoordinates[lowerIndex] + p2 = listOfCoordinates[middleIndex] + p3 = listOfCoordinates[upperIndex] area += (p3.longitude.toRadians() - p1.longitude.toRadians()) * sin(p2.latitude.toRadians()) } diff --git a/TurfTests/Fixtures/dc-polygon.geojson b/TurfTests/Fixtures/dc-polygon.geojson deleted file mode 100644 index 4e9e1f4b..00000000 --- a/TurfTests/Fixtures/dc-polygon.geojson +++ /dev/null @@ -1,25 +0,0 @@ -{ - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - -77.0416259765625, - 39.06611426153784 - ], - [ - -77.32177734375, - 38.843986129756615 - ], - [ - -76.7669677734375, - 38.831149809348744 - ], - [ - -77.0416259765625, - 39.06611426153784 - ] - ] - } -} diff --git a/TurfTests/Fixtures/polygon.geojson b/TurfTests/Fixtures/polygon.geojson new file mode 100644 index 00000000..617ffdd6 --- /dev/null +++ b/TurfTests/Fixtures/polygon.geojson @@ -0,0 +1,19 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [125, -15], + [113, -22], + [117, -37], + [130, -33], + [148, -39], + [154, -27], + [144, -15], + [125, -15] + ] + ] + } +} diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index 26bc705c..e3852d7b 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -288,10 +288,17 @@ class TurfTests: XCTestCase { } func testPolygonArea() { - let json = Fixture.JSONFromFileNamed(name: "dc-polygon") - let coordinates = ((json["geometry"] as! [String: Any])["coordinates"] as! [[Double]]).map { CLLocationCoordinate2D(latitude: $0[0], longitude: $0[1]) } + let json = Fixture.JSONFromFileNamed(name: "polygon") + let geometry = json["geometry"] as! [String: Any] + let geoJSONCoordinates = geometry["coordinates"] as! [[[Double]]] + let coordinates = geoJSONCoordinates.map { + return $0.map { + return CLLocationCoordinate2D(latitude: $0[0], longitude: $0[1]) + } + } + let polygon = Polygon(coordinates: coordinates) - XCTAssertEqual(polygon.ringArea, 611152631.86) + XCTAssertEqual(polygon.polygonArea, 7766240997209) } } From 0c4382e3f76e439cef07122389bc1aa1f7d3f668 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Sat, 2 Dec 2017 12:05:53 -0500 Subject: [PATCH 06/15] More edits --- Turf/Turf.swift | 2 +- TurfTests/TurfTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index 33496309..dd53a387 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -297,7 +297,7 @@ public struct Polygon { area += abs(ringArea(coordinates[0])) - for coordinate in coordinates { + for coordinate in coordinates.suffix(from: 1) { area -= abs(ringArea(coordinate)) } } diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index e3852d7b..808c33ea 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -293,7 +293,7 @@ class TurfTests: XCTestCase { let geoJSONCoordinates = geometry["coordinates"] as! [[[Double]]] let coordinates = geoJSONCoordinates.map { return $0.map { - return CLLocationCoordinate2D(latitude: $0[0], longitude: $0[1]) + return CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) } } From dd6eac8e774a0982bfa12cfb51034fdb45b926f9 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Fri, 8 Dec 2017 09:20:41 -0500 Subject: [PATCH 07/15] Get working calculation --- Turf/Turf.swift | 5 ++--- TurfTests/TurfTests.swift | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index dd53a387..a1da7535 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -320,12 +320,11 @@ public struct Polygon { var lowerIndex:Int var middleIndex:Int var upperIndex:Int - var i:Int var area:Double = 0 - var coordsLength:Int = listOfCoordinates.count + let coordsLength:Int = listOfCoordinates.count if (coordsLength > 2) { - for(index, coordinate) in coordinates.enumerated() { + for(index, _) in listOfCoordinates.enumerated() { if (index == coordsLength - 2) { lowerIndex = coordsLength - 2 middleIndex = coordsLength - 1 diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index 808c33ea..d1fc41d2 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -299,6 +299,6 @@ class TurfTests: XCTestCase { let polygon = Polygon(coordinates: coordinates) - XCTAssertEqual(polygon.polygonArea, 7766240997209) + XCTAssertEqual(polygon.polygonArea, 7766240997209, accuracy: 0.1) } } From d3ead9a001c929008e464a2d7dba116f804e755e Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Fri, 8 Dec 2017 09:35:15 -0500 Subject: [PATCH 08/15] Refactor --- Turf/Turf.swift | 54 +++++++++++++++++++-------------------- TurfTests/TurfTests.swift | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index a1da7535..92df8e76 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -288,20 +288,20 @@ public struct Polyline { } public struct Polygon { - var coordinates: [[CLLocationCoordinate2D]] + var polygonCoordinates: [[CLLocationCoordinate2D]] var polygonArea: Double { var area:Double = 0 - if (!coordinates.isEmpty && coordinates.count > 0) { + if (!polygonCoordinates.isEmpty && polygonCoordinates.count > 0) { - area += abs(ringArea(coordinates[0])) + area += abs(ringArea(polygonCoordinates[0])) - for coordinate in coordinates.suffix(from: 1) { + for coordinate in polygonCoordinates.suffix(from: 1) { area -= abs(ringArea(coordinate)) } } - return area; + return area } /** @@ -313,39 +313,39 @@ public struct Polygon { * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * */ - private func ringArea(_ listOfCoordinates: [CLLocationCoordinate2D]) -> Double { - var p1:CLLocationCoordinate2D - var p2:CLLocationCoordinate2D - var p3:CLLocationCoordinate2D - var lowerIndex:Int - var middleIndex:Int - var upperIndex:Int - var area:Double = 0 - let coordsLength:Int = listOfCoordinates.count + private func ringArea(_ coordinates: [CLLocationCoordinate2D]) -> Double { + var p1: CLLocationCoordinate2D + var p2: CLLocationCoordinate2D + var p3: CLLocationCoordinate2D + var lowerIndex: Int + var middleIndex: Int + var upperIndex: Int + var area: Double = 0 + let coordinatesCount: Int = coordinates.count - if (coordsLength > 2) { - for(index, _) in listOfCoordinates.enumerated() { - if (index == coordsLength - 2) { - lowerIndex = coordsLength - 2 - middleIndex = coordsLength - 1 + if (coordinatesCount > 2) { + for index in 0...coordinatesCount - 1 { + if (index == coordinatesCount - 2) { + lowerIndex = coordinatesCount - 2 + middleIndex = coordinatesCount - 1 upperIndex = 0 - } else if(index == coordsLength - 1) { - lowerIndex = coordsLength - 1; - middleIndex = 0; - upperIndex = 1; + } else if(index == coordinatesCount - 1) { + lowerIndex = coordinatesCount - 1 + middleIndex = 0 + upperIndex = 1 } else { lowerIndex = index middleIndex = index + 1 upperIndex = index + 2 } - p1 = listOfCoordinates[lowerIndex] - p2 = listOfCoordinates[middleIndex] - p3 = listOfCoordinates[upperIndex] + p1 = coordinates[lowerIndex] + p2 = coordinates[middleIndex] + p3 = coordinates[upperIndex] area += (p3.longitude.toRadians() - p1.longitude.toRadians()) * sin(p2.latitude.toRadians()) } - area = area * equitorialRadius * equitorialRadius / 2; + area = area * equitorialRadius * equitorialRadius / 2 } return area } diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index d1fc41d2..985c9d82 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -297,7 +297,7 @@ class TurfTests: XCTestCase { } } - let polygon = Polygon(coordinates: coordinates) + let polygon = Polygon(polygonCoordinates: coordinates) XCTAssertEqual(polygon.polygonArea, 7766240997209, accuracy: 0.1) } From 4b4ff4e1d4fb466a7df52ae3f0179e1afc5d8a62 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Fri, 8 Dec 2017 09:42:49 -0500 Subject: [PATCH 09/15] Place constants in struct; more refactoring --- Turf/CoreLocation.swift | 4 ++-- Turf/Turf.swift | 8 +++++--- TurfTests/TurfTests.swift | 4 +--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Turf/CoreLocation.swift b/Turf/CoreLocation.swift index 771128b3..274b1ce5 100644 --- a/Turf/CoreLocation.swift +++ b/Turf/CoreLocation.swift @@ -59,7 +59,7 @@ extension CLLocationCoordinate2D: Equatable { /// Returns a coordinate a certain Haversine distance away in the given direction. public func coordinate(at distance: CLLocationDistance, facing direction: CLLocationDirection) -> CLLocationCoordinate2D { - let radianCoordinate = RadianCoordinate2D(self).coordinate(at: distance / metersPerRadian, facing: direction.toRadians()) + let radianCoordinate = RadianCoordinate2D(self).coordinate(at: distance / Constants.metersPerRadian, facing: direction.toRadians()) return CLLocationCoordinate2D(radianCoordinate) } @@ -67,7 +67,7 @@ extension CLLocationCoordinate2D: Equatable { Returns the Haversine distance between two coordinates measured in degrees. */ public func distance(to coordinate: CLLocationCoordinate2D) -> CLLocationDistance { - return RadianCoordinate2D(self).distance(to: RadianCoordinate2D(coordinate)) * metersPerRadian + return RadianCoordinate2D(self).distance(to: RadianCoordinate2D(coordinate)) * Constants.metersPerRadian } } diff --git a/Turf/Turf.swift b/Turf/Turf.swift index 92df8e76..32f26118 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -4,8 +4,10 @@ public typealias LocationRadians = Double public typealias RadianDistance = Double public typealias RadianDirection = Double -let metersPerRadian = 6_373_000.0 -let equitorialRadius:Double = 6378137 +struct Constants { + static let metersPerRadian = 6_373_000.0 + static let equatorialRadius:Double = 6378137 +} /** A `RadianCoordinate2D` is a coordinate represented in radians as opposed to @@ -345,7 +347,7 @@ public struct Polygon { area += (p3.longitude.toRadians() - p1.longitude.toRadians()) * sin(p2.latitude.toRadians()) } - area = area * equitorialRadius * equitorialRadius / 2 + area = area * Constants.equatorialRadius * Constants.equatorialRadius / 2 } return area } diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index 985c9d82..8ec39b89 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -292,9 +292,7 @@ class TurfTests: XCTestCase { let geometry = json["geometry"] as! [String: Any] let geoJSONCoordinates = geometry["coordinates"] as! [[[Double]]] let coordinates = geoJSONCoordinates.map { - return $0.map { - return CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) - } + $0.map { CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) } } let polygon = Polygon(polygonCoordinates: coordinates) From 944e5816e73cac30f8bd6344c65e9dfe480e72aa Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Fri, 15 Dec 2017 10:08:08 -0500 Subject: [PATCH 10/15] Feedback edits from @1ec5 --- Turf/CoreLocation.swift | 4 +- Turf/Turf.swift | 82 ++++++++++++++++++--------------------- TurfTests/TurfTests.swift | 7 ++-- 3 files changed, 43 insertions(+), 50 deletions(-) diff --git a/Turf/CoreLocation.swift b/Turf/CoreLocation.swift index 274b1ce5..771128b3 100644 --- a/Turf/CoreLocation.swift +++ b/Turf/CoreLocation.swift @@ -59,7 +59,7 @@ extension CLLocationCoordinate2D: Equatable { /// Returns a coordinate a certain Haversine distance away in the given direction. public func coordinate(at distance: CLLocationDistance, facing direction: CLLocationDirection) -> CLLocationCoordinate2D { - let radianCoordinate = RadianCoordinate2D(self).coordinate(at: distance / Constants.metersPerRadian, facing: direction.toRadians()) + let radianCoordinate = RadianCoordinate2D(self).coordinate(at: distance / metersPerRadian, facing: direction.toRadians()) return CLLocationCoordinate2D(radianCoordinate) } @@ -67,7 +67,7 @@ extension CLLocationCoordinate2D: Equatable { Returns the Haversine distance between two coordinates measured in degrees. */ public func distance(to coordinate: CLLocationCoordinate2D) -> CLLocationDistance { - return RadianCoordinate2D(self).distance(to: RadianCoordinate2D(coordinate)) * Constants.metersPerRadian + return RadianCoordinate2D(self).distance(to: RadianCoordinate2D(coordinate)) * metersPerRadian } } diff --git a/Turf/Turf.swift b/Turf/Turf.swift index 32f26118..a8f8f051 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -4,10 +4,9 @@ public typealias LocationRadians = Double public typealias RadianDistance = Double public typealias RadianDirection = Double -struct Constants { - static let metersPerRadian = 6_373_000.0 - static let equatorialRadius:Double = 6378137 -} + +let metersPerRadian: CLLocationDistance = 6_373_000.0 +let equatorialRadius: CLLocationDistance = 6378137 /** A `RadianCoordinate2D` is a coordinate represented in radians as opposed to @@ -289,22 +288,8 @@ public struct Polyline { } } -public struct Polygon { - var polygonCoordinates: [[CLLocationCoordinate2D]] - - var polygonArea: Double { - var area:Double = 0 - - if (!polygonCoordinates.isEmpty && polygonCoordinates.count > 0) { - - area += abs(ringArea(polygonCoordinates[0])) - - for coordinate in polygonCoordinates.suffix(from: 1) { - area -= abs(ringArea(coordinate)) - } - } - return area - } +struct Ring { + var coordinates: [CLLocationCoordinate2D] /** * Calculate the approximate area of the polygon were it projected onto the earth. @@ -315,40 +300,47 @@ public struct Polygon { * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * */ - private func ringArea(_ coordinates: [CLLocationCoordinate2D]) -> Double { - var p1: CLLocationCoordinate2D - var p2: CLLocationCoordinate2D - var p3: CLLocationCoordinate2D - var lowerIndex: Int - var middleIndex: Int - var upperIndex: Int + internal func area() -> Double { var area: Double = 0 let coordinatesCount: Int = coordinates.count - if (coordinatesCount > 2) { - for index in 0...coordinatesCount - 1 { - if (index == coordinatesCount - 2) { - lowerIndex = coordinatesCount - 2 - middleIndex = coordinatesCount - 1 - upperIndex = 0 - } else if(index == coordinatesCount - 1) { - lowerIndex = coordinatesCount - 1 - middleIndex = 0 - upperIndex = 1 + if coordinatesCount > 2 { + for index in 0.. Date: Wed, 20 Dec 2017 09:37:04 -0500 Subject: [PATCH 11/15] Feedback edits; test still failing --- Turf/Turf.swift | 8 ++--- TurfTests/Fixtures/polygon.geojson | 50 +++++++++++++++++++++++++----- TurfTests/TurfTests.swift | 3 +- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index a8f8f051..e5b3c25c 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -6,7 +6,7 @@ public typealias RadianDirection = Double let metersPerRadian: CLLocationDistance = 6_373_000.0 -let equatorialRadius: CLLocationDistance = 6378137 +let equatorialRadius: CLLocationDistance = 6_378_137 /** A `RadianCoordinate2D` is a coordinate represented in radians as opposed to @@ -300,7 +300,7 @@ struct Ring { * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * */ - internal func area() -> Double { + var area: Double { var area: Double = 0 let coordinatesCount: Int = coordinates.count @@ -339,8 +339,8 @@ public struct Polygon { // Ported from https://github.com/Turfjs/turf/blob/a94151418cb969868fdb42955a19a133512da0fd/packages/turf-area/index.js var area: Double { - return abs(outerRing.area()) - innerRings - .map { abs($0.area()) } + return abs(outerRing.area) - innerRings + .map { abs($0.area) } .reduce(0, +) } } diff --git a/TurfTests/Fixtures/polygon.geojson b/TurfTests/Fixtures/polygon.geojson index 617ffdd6..d4cf0816 100644 --- a/TurfTests/Fixtures/polygon.geojson +++ b/TurfTests/Fixtures/polygon.geojson @@ -5,14 +5,48 @@ "type": "Polygon", "coordinates": [ [ - [125, -15], - [113, -22], - [117, -37], - [130, -33], - [148, -39], - [154, -27], - [144, -15], - [125, -15] + [ + -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 + ] ] ] } diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index 58278475..33a83c23 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -295,8 +295,9 @@ class TurfTests: XCTestCase { $0.map { CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) } } let outerRing = Ring(coordinates: allRings.first!) + let innerRing = Ring(coordinates: allRings[1]) - let polygon = Polygon(outerRing: outerRing, innerRings: []) + let polygon = Polygon(outerRing: outerRing, innerRings: [innerRing]) XCTAssertEqual(polygon.area, 7766240997209, accuracy: 0.1) } From dea04ac75a4aab3e2882820ac8229f265548ae42 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Wed, 20 Dec 2017 09:59:23 -0500 Subject: [PATCH 12/15] =?UTF-8?q?Actually=20update=20the=20test=20value=20?= =?UTF-8?q?=F0=9F=99=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TurfTests/TurfTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index 33a83c23..7e73e051 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -299,6 +299,6 @@ class TurfTests: XCTestCase { let polygon = Polygon(outerRing: outerRing, innerRings: [innerRing]) - XCTAssertEqual(polygon.area, 7766240997209, accuracy: 0.1) + XCTAssertEqual(polygon.area, 78588446934.43, accuracy: 0.1) } } From ead6d974da25f1fd16a8783ecfb8fe325fb71e26 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Fri, 22 Dec 2017 09:24:27 -0500 Subject: [PATCH 13/15] Minor refactor --- Turf/Turf.swift | 6 +++--- TurfTests/TurfTests.swift | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index e5b3c25c..8b70df9a 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -288,7 +288,7 @@ public struct Polyline { } } -struct Ring { +public struct Ring { var coordinates: [CLLocationCoordinate2D] /** @@ -300,7 +300,7 @@ struct Ring { * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 * */ - var area: Double { + public var area: Double { var area: Double = 0 let coordinatesCount: Int = coordinates.count @@ -338,7 +338,7 @@ public struct Polygon { // Ported from https://github.com/Turfjs/turf/blob/a94151418cb969868fdb42955a19a133512da0fd/packages/turf-area/index.js - var area: Double { + public var area: Double { return abs(outerRing.area) - innerRings .map { abs($0.area) } .reduce(0, +) diff --git a/TurfTests/TurfTests.swift b/TurfTests/TurfTests.swift index 7e73e051..d7c513d0 100644 --- a/TurfTests/TurfTests.swift +++ b/TurfTests/TurfTests.swift @@ -295,9 +295,11 @@ class TurfTests: XCTestCase { $0.map { CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0]) } } let outerRing = Ring(coordinates: allRings.first!) - let innerRing = Ring(coordinates: allRings[1]) + let innerRings = allRings.suffix(from: 1).map { + Ring(coordinates: $0) + } - let polygon = Polygon(outerRing: outerRing, innerRings: [innerRing]) + let polygon = Polygon(outerRing: outerRing, innerRings: innerRings) XCTAssertEqual(polygon.area, 78588446934.43, accuracy: 0.1) } From 51f86dc779c58c36ad6d7026517b78716b7d2cc1 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Tue, 2 Jan 2018 14:59:57 -0500 Subject: [PATCH 14/15] Documentation updates --- Turf/Turf.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index 8b70df9a..c4ee195e 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -6,6 +6,7 @@ public typealias RadianDirection = Double 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 /** @@ -292,12 +293,12 @@ public struct Ring { var coordinates: [CLLocationCoordinate2D] /** - * Calculate the approximate area of the polygon were it projected onto the earth. + * 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 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 + * Laboratory, Pasadena, CA, June 2007 https://trs.jpl.nasa.gov/handle/2014/41271 * */ public var area: Double { From 6e57647966aee198724624e66d8d7ca0112c9a23 Mon Sep 17 00:00:00 2001 From: Nadia Barbosa Date: Mon, 8 Jan 2018 12:46:07 -0500 Subject: [PATCH 15/15] Add documentation for Ring and Polygon --- Turf/Turf.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Turf/Turf.swift b/Turf/Turf.swift index c4ee195e..e1666fc8 100644 --- a/Turf/Turf.swift +++ b/Turf/Turf.swift @@ -289,6 +289,10 @@ public struct Polyline { } } + +/** + 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] @@ -333,6 +337,10 @@ public struct Ring { } } +/** + 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]