Skip to content

Commit

Permalink
✨ Implement AnyBoundingBox.union
Browse files Browse the repository at this point in the history
Also fix `GeometryCollection` encoding
  • Loading branch information
RemiBardon committed Feb 10, 2022
1 parent 990a409 commit d1b68c2
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 5 deletions.
13 changes: 11 additions & 2 deletions Sources/GeoJSON/BoundingBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,17 @@ public enum AnyBoundingBox: BoundingBox, Hashable, Codable {
public static var zero: AnyBoundingBox = .twoDimensions(.zero)

public func union(_ other: AnyBoundingBox) -> AnyBoundingBox {
#warning("Implement `AnyBoundingBox.union`")
fatalError("Not implemented yet")
switch (self, other) {
case let (.twoDimensions(self), .twoDimensions(other)):
return .twoDimensions(self.union(other))

case let (.twoDimensions(bbox2d), .threeDimensions(bbox3d)),
let (.threeDimensions(bbox3d), .twoDimensions(bbox2d)):
return .threeDimensions(bbox3d.union(bbox2d))

case let (.threeDimensions(self), .threeDimensions(other)):
return .threeDimensions(self.union(other))
}
}

case twoDimensions(BoundingBox2D)
Expand Down
33 changes: 33 additions & 0 deletions Sources/GeoJSON/GeoJSON+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,39 @@ extension SingleGeometry {

}

fileprivate enum GeometryCollectionCodingKeys: String, CodingKey {
case geoJSONType = "type"
case geometries, bbox
}

extension GeometryCollection {

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: GeometryCollectionCodingKeys.self)

let type = try container.decode(GeoJSON.`Type`.self, forKey: .geoJSONType)
guard type == Self.geoJSONType else {
throw DecodingError.typeMismatch(Self.self, DecodingError.Context(
codingPath: container.codingPath,
debugDescription: "Found GeoJSON type '\(type.rawValue)'"
))
}

let geometries = try container.decode([AnyGeometry].self, forKey: .geometries)

self.init(geometries: geometries)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: GeometryCollectionCodingKeys.self)

try container.encode(Self.geoJSONType, forKey: .geoJSONType)
try container.encode(self.geometries, forKey: .geometries)
try container.encode(self.bbox, forKey: .bbox)
}

}

fileprivate enum AnyGeometryCodingKeys: String, CodingKey {
case geoJSONType = "type"
}
Expand Down
6 changes: 5 additions & 1 deletion Sources/GeoJSON/Geometries/GeometryCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ public struct GeometryCollection: CodableGeometry {

public static var geometryType: GeoJSON.`Type`.Geometry { .geometryCollection }

public var _bbox: AnyBoundingBox { asAnyGeometry.bbox }
public var _bbox: AnyBoundingBox { geometries.bbox }

public var asAnyGeometry: AnyGeometry { .geometryCollection(self) }

public var geometries: [AnyGeometry]

public init(geometries: [AnyGeometry]) {
self.geometries = geometries
}

}
5 changes: 3 additions & 2 deletions Sources/GeoJSON/Objects/FeatureCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ public struct FeatureCollection<

public var features: [Feature<ID, Geometry, Properties>]

// FIXME: Fix bounding box
public var bbox: AnyBoundingBox? { nil }
public var bbox: AnyBoundingBox? {
features.compactMap(\.bbox).reduce(nil, { $0.union($1.asAny) })
}

}

Expand Down
6 changes: 6 additions & 0 deletions Sources/GeoModels/2D/BoundingBox2D.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,9 @@ extension BoundingBox2D: BoundingBox {
}

}

extension BoundingBox2D {

public func union(_ bbox3d: BoundingBox3D) -> BoundingBox3D { bbox3d.union(self) }

}
9 changes: 9 additions & 0 deletions Sources/GeoModels/3D/BoundingBox3D.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,12 @@ extension BoundingBox3D: BoundingBox {
}

}

extension BoundingBox3D {

public func union(_ bbox2d: BoundingBox2D) -> BoundingBox3D {
let other = BoundingBox3D(bbox2d, lowAltitude: self.lowAltitude, zHeight: .zero)
return self.union(other)
}

}
80 changes: 80 additions & 0 deletions Tests/GeoJSONTests/GeoJSON+EncodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,84 @@ final class GeoJSONEncodableTests: XCTestCase {
XCTAssertEqual(string, expected)
}

func testFeatureCollectionOfGeometryCollectionEncode() throws {
struct FeatureProperties: Hashable, Codable {}

let featureCollection: FeatureCollection = FeatureCollection(features: [
Feature(
geometry: GeometryCollection(geometries: [
.point2D(Point2D(coordinates: .nantes)),
.point2D(Point2D(coordinates: .bordeaux)),
]),
properties: FeatureProperties()
),
Feature(
geometry: GeometryCollection(geometries: [
.point2D(Point2D(coordinates: .paris)),
.point2D(Point2D(coordinates: .marseille)),
]),
properties: FeatureProperties()
),
])
let data: Data = try JSONEncoder().encode(featureCollection)
let string: String = try XCTUnwrap(String(data: data, encoding: .utf8))

let expected: String = [
"{",
"\"type\":\"FeatureCollection\",",
// For some reason, `"bbox"` goes here 🤷
"\"bbox\":[-1.55366,43.29868,5.36468,48.85719],",
"\"features\":[",
"{",
// For some reason, `"properties"` goes here 🤷
"\"properties\":{},",
"\"type\":\"Feature\",",
"\"geometry\":{",
"\"type\":\"GeometryCollection\",",
// For some reason, `"bbox"` goes here 🤷
"\"bbox\":[-1.55366,44.8378,-0.58143,47.21881],",
"\"geometries\":[",
"{",
"\"type\":\"Point\",",
"\"coordinates\":[-1.55366,47.21881],",
"\"bbox\":[-1.55366,47.21881,-1.55366,47.21881]",
"},",
"{",
"\"type\":\"Point\",",
"\"coordinates\":[-0.58143,44.8378],",
"\"bbox\":[-0.58143,44.8378,-0.58143,44.8378]",
"}",
"]",
"},",
"\"bbox\":[-1.55366,44.8378,-0.58143,47.21881]",
"},",
"{",
// For some reason, `"properties"` goes here 🤷
"\"properties\":{},",
"\"type\":\"Feature\",",
"\"geometry\":{",
"\"type\":\"GeometryCollection\",",
// For some reason, `"bbox"` goes here 🤷
"\"bbox\":[2.3529,43.29868,5.36468,48.85719],",
"\"geometries\":[",
"{",
"\"type\":\"Point\",",
"\"coordinates\":[2.3529,48.85719],",
"\"bbox\":[2.3529,48.85719,2.3529,48.85719]",
"},",
"{",
"\"type\":\"Point\",",
"\"coordinates\":[5.36468,43.29868],",
"\"bbox\":[5.36468,43.29868,5.36468,43.29868]",
"}",
"]",
"},",
"\"bbox\":[2.3529,43.29868,5.36468,48.85719]",
"}",
"]",
"}",
].joined()
XCTAssertEqual(string, expected)
}

}

0 comments on commit d1b68c2

Please sign in to comment.