Repository: GEOSwift/GEOSwift (swift)

Issue:
Release 7.0 removes negative buffer with no alternative
I was using the buffer function with a negative value to reduce the size of a geometry. I was previously using v5.1.0. After a change in v7.0.0 to remove support for negative buffer, there is no way to achieve the same result.

Pull request description:
Fixes #221

Patch:
diff --git a/.swiftlint.yml b/.swiftlint.yml
index 9ba63962..f90d77b4 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -8,6 +8,7 @@ disabled_rules:
   - force_cast
   - identifier_name
   - type_name
+  - file_length
   - no_space_in_method_call  # https://github.com/realm/SwiftLint/issues/2793
 opt_in_rules:
   - closure_spacing
diff --git a/GEOSwift/GEOS/GeometryConvertible+GEOS.swift b/GEOSwift/GEOS/GeometryConvertible+GEOS.swift
index 377cb69a..ef74b601 100644
--- a/GEOSwift/GEOS/GeometryConvertible+GEOS.swift
+++ b/GEOSwift/GEOS/GeometryConvertible+GEOS.swift
@@ -351,10 +351,7 @@ public extension GeometryConvertible {
 
     // MARK: - Buffer Functions
 
-    func buffer(by width: Double) throws -> Geometry {
-        guard width >= 0 else {
-            throw GEOSwiftError.negativeBufferWidth
-        }
+    func buffer(by width: Double) throws -> Geometry? {
         let context = try GEOSContext()
         let geosObject = try geometry.geosObject(with: context)
         // the last parameter in GEOSBuffer_r is called `quadsegs` and in other places in GEOS, it defaults to
@@ -364,7 +361,13 @@ public extension GeometryConvertible {
         guard let resultPointer = GEOSBuffer_r(context.handle, geosObject.pointer, width, 8) else {
             throw GEOSError.libraryError(errorMessages: context.errors)
         }
-        return try Geometry(geosObject: GEOSObject(context: context, pointer: resultPointer))
+        do {
+            return try Geometry(geosObject: GEOSObject(context: context, pointer: resultPointer))
+        } catch GEOSwiftError.tooFewPoints {
+            return nil
+        } catch {
+            throw error
+        }
     }
 
     // MARK: - Simplify Functions
diff --git a/GEOSwift/GEOSwiftError.swift b/GEOSwift/GEOSwiftError.swift
index c6a6e076..3865ed98 100644
--- a/GEOSwift/GEOSwiftError.swift
+++ b/GEOSwift/GEOSwiftError.swift
@@ -9,5 +9,4 @@ public enum GEOSwiftError: Error, Equatable {
     case invalidFeatureId
     case lengthIsZero
     case unexpectedEnvelopeResult(Geometry)
-    case negativeBufferWidth
 }


Test patch:
diff --git a/GEOSwiftTests/GEOS/GeometryConvertible+GEOSTests.swift b/GEOSwiftTests/GEOS/GeometryConvertible+GEOSTests.swift
index c27fe65e..9a2dc15c 100644
--- a/GEOSwiftTests/GEOS/GeometryConvertible+GEOSTests.swift
+++ b/GEOSwiftTests/GEOS/GeometryConvertible+GEOSTests.swift
@@ -897,14 +897,22 @@ final class GeometryConvertible_GEOSTests: XCTestCase {
         }
     }
 
-    func testNegativeBufferWidthThrows() {
-        XCTAssertThrowsError(try Point(x: 0, y: 0).buffer(by: -1)) { (error) in
-            if case GEOSwiftError.negativeBufferWidth = error {
-                // pass
-            } else {
-                XCTFail("Threw unexpected error: \(error)")
-            }
-        }
+    func testNegativeBufferWidthWithNonNilResult() throws {
+        let expectedGeometry = try Geometry.polygon(GEOSwift.Polygon(
+            exterior: GEOSwift.Polygon.LinearRing(points: [
+                Point(x: 6, y: 1),
+                Point(x: 4, y: 1),
+                Point(x: 4, y: -1),
+                Point(x: 6, y: -1),
+                Point(x: 6, y: 1)])))
+
+        let actualGeometry = try Polygon.testValueWithoutHole.buffer(by: -1)
+
+        try XCTAssertTrue(actualGeometry?.isTopologicallyEquivalent(to: expectedGeometry) ?? false)
+    }
+
+    func testNegativeBufferWidthWithNilResult() throws {
+        try XCTAssertNil(Point.testValue1.buffer(by: -1))
     }
 
     // MARK: - Simplify Functions