Skip to content

Commit

Permalink
Bugfix: Holes in polygons where not subtracted from the outer ring ar…
Browse files Browse the repository at this point in the history
…ea (#61)
  • Loading branch information
trasch authored Jul 29, 2024
1 parent 8303092 commit 9b0211f
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 16 deletions.
33 changes: 17 additions & 16 deletions Sources/GISTools/Algorithms/Area.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#if !os(Linux)
import CoreLocation
import CoreLocation
#endif
import Foundation

Expand All @@ -11,12 +11,12 @@ extension Polygon {
///
/// - Returns: The area of the outer ring minus the areas of the inner rings, in square meters.
public var area: Double {
guard let outerRing = outerRing else { return 0.0 }
guard let outerRing else { return 0.0 }

var area: Double = abs(outerRing.area)

if let innerRings = innerRings {
area += innerRings.reduce(0.0, { $0 + abs($1.area) })
if let innerRings {
area -= innerRings.reduce(0.0, { $0 + abs($1.area) })
}

return area
Expand Down Expand Up @@ -47,33 +47,34 @@ extension Ring {
/// Laboratory, Pasadena, CA, June 2007 https://trs.jpl.nasa.gov/handle/2014/41271
public var area: Double {
let coordinates = projection == .epsg4326
? self.coordinates
: self.coordinates.map({ $0.projected(to: .epsg4326) })
? coordinates
: coordinates.map({ $0.projected(to: .epsg4326) })

var area: Double = 0.0
var area = 0.0
let coordinatesCount = coordinates.count

if coordinatesCount > 2 {
for index in 0 ..< coordinatesCount {
let controlPoints: (Coordinate3D, Coordinate3D, Coordinate3D)

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

area += (controlPoints.2.longitude.degreesToRadians - controlPoints.0.longitude.degreesToRadians) * sin(controlPoints.1.latitude.degreesToRadians)
Expand Down
32 changes: 32 additions & 0 deletions Tests/GISToolsTests/Algorithms/AreaTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@testable import GISTools
import XCTest

final class AreaTests: XCTestCase {

func testArea() throws {
let polygon1 = try XCTUnwrap(Polygon([[
Coordinate3D(x: 0.0, y: 0.0),
Coordinate3D(x: 100.0, y: 0.0),
Coordinate3D(x: 100.0, y: 100.0),
Coordinate3D(x: 0.0, y: 100.0),
Coordinate3D(x: 0.0, y: 0.0)
]]))
let polygon2 = try XCTUnwrap(Polygon([[
Coordinate3D(x: 0.0, y: 0.0),
Coordinate3D(x: 100.0, y: 0.0),
Coordinate3D(x: 100.0, y: 100.0),
Coordinate3D(x: 0.0, y: 100.0),
Coordinate3D(x: 0.0, y: 0.0)
], [
Coordinate3D(x: 25.0, y: 25.0),
Coordinate3D(x: 75.0, y: 25.0),
Coordinate3D(x: 75.0, y: 75.0),
Coordinate3D(x: 25.0, y: 75.0),
Coordinate3D(x: 25.0, y: 25.0)
]]))

XCTAssertEqual(polygon1.area, 10000.0, accuracy: 0.1)
XCTAssertEqual(polygon2.area, 7500.0, accuracy: 0.1)
}

}

0 comments on commit 9b0211f

Please sign in to comment.