Skip to content

Commit a37addb

Browse files
authored
Fix roundtrip conversion on Linux (#9)
Also: Add SerializationTest, some consistency=
1 parent 7f75344 commit a37addb

2 files changed

Lines changed: 63 additions & 17 deletions

File tree

Sources/GeoJSONKit/GeoJSON.swift

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public struct GeoJSON: Hashable {
2424
case unexpectedRoot
2525
case missingOrInvalidRequiredField(String)
2626
case wrongNumberOfCoordinates(String)
27-
case wrongTypeOfSimpleGeometry
27+
case wrongTypeOfSimpleGeometry(String)
2828
}
2929

3030
/// A GeoJSON object may represent a region of space (a Geometry), a
@@ -84,8 +84,10 @@ public struct GeoJSON: Hashable {
8484
}
8585
self = .collection(geometries)
8686

87-
default:
88-
throw SerializationError.wrongTypeOfSimpleGeometry
87+
case .feature:
88+
throw SerializationError.wrongTypeOfSimpleGeometry("Can't turn Feature into GeometryObject")
89+
case .featureCollection:
90+
throw SerializationError.wrongTypeOfSimpleGeometry("Can't turn FeatureCollection into GeometryObject")
8991
}
9092
}
9193

@@ -152,13 +154,21 @@ public struct GeoJSON: Hashable {
152154
self.altitude = altitude
153155
}
154156

155-
fileprivate init(coordinates: [Degrees]) throws {
157+
fileprivate init(coordinates: [Any]) throws {
156158
guard coordinates.count >= 2 else {
157159
throw SerializationError.wrongNumberOfCoordinates("At least 2 per position")
158160
}
159-
latitude = coordinates[1]
160-
longitude = coordinates[0]
161-
altitude = coordinates.count >= 3 ? coordinates[2] : nil
161+
162+
if let lat = coordinates[1] as? Degrees, let lng = coordinates[0] as? Degrees {
163+
latitude = lat
164+
longitude = lng
165+
} else if let lat = coordinates[1] as? Decimal, let lng = coordinates[0] as? Decimal {
166+
latitude = GeoJSON.Degrees((lat as NSDecimalNumber).doubleValue)
167+
longitude = GeoJSON.Degrees((lng as NSDecimalNumber).doubleValue)
168+
} else {
169+
throw SerializationError.wrongTypeOfSimpleGeometry("Expected \(Degrees.self) for coordinates but got \(Swift.type(of: coordinates[0])) (\(coordinates[0])) and \(Swift.type(of: coordinates[1])) (\(coordinates[1]))")
170+
}
171+
altitude = coordinates.count >= 3 ? coordinates[2] as? Degrees : nil
162172
}
163173

164174
fileprivate func toJSON() -> [Degrees] {
@@ -248,17 +258,15 @@ public struct GeoJSON: Hashable {
248258
case polygon(Polygon)
249259

250260
fileprivate init(coordinates: [Any]) throws {
251-
if let coordinates = coordinates as? [[[Degrees]]] {
261+
if let coordinates = coordinates as? [[[Any]]] {
252262
let positions = try coordinates.map { try $0.map { try Position(coordinates: $0) }}
253263
self = .polygon(Polygon(positions))
254-
} else if let coordinates = coordinates as? [[Degrees]] {
264+
} else if let coordinates = coordinates as? [[Any]] {
255265
let positions = try coordinates.map { try Position(coordinates: $0) }
256266
self = .lineString(LineString(positions: positions))
257-
} else if let coordinates = coordinates as? [Degrees] {
267+
} else {
258268
let position = try Position(coordinates: coordinates)
259269
self = .point(position)
260-
} else {
261-
throw SerializationError.wrongTypeOfSimpleGeometry
262270
}
263271
}
264272

@@ -537,11 +545,11 @@ fileprivate enum Adjuster {
537545
return dict.mapValues(prune(_:))
538546
} else if value is Int || value is [Int] || value is [[Int]] || value is Bool || value is [Bool] || value is [[Bool]] {
539547
return value
540-
} else if let doubles = value as? [Double] {
548+
} else if let doubles = value as? [GeoJSON.Degrees] {
541549
return doubles.prune
542-
} else if let doubless = value as? [[Double]] {
550+
} else if let doubless = value as? [[GeoJSON.Degrees]] {
543551
return doubless.map(\.prune)
544-
} else if let double = value as? Double {
552+
} else if let double = value as? GeoJSON.Degrees {
545553
return Decimal(double)
546554
} else if let array = value as? [Any] {
547555
return array.map(prune(_:))
@@ -552,9 +560,9 @@ fileprivate enum Adjuster {
552560

553561
static func hashable(_ value: Any) -> AnyHashable? {
554562
if let dict = value as? [String: Any] {
555-
return dict.mapValues(hashable(_:))
563+
return dict.compactMapValues(hashable(_:))
556564
} else if let array = value as? [Any] {
557-
return array.map(hashable(_:))
565+
return array.compactMap(hashable(_:))
558566
} else if let hashable = value as? AnyHashable {
559567
return hashable
560568
} else {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// SerializationTest.swift
3+
//
4+
//
5+
// Created by Adrian Schönig on 13/3/2022.
6+
//
7+
8+
import XCTest
9+
10+
@testable import GeoJSONKit
11+
12+
class SerializationTest: XCTestCase {
13+
14+
func testPointToDictAndBack() throws {
15+
let point = GeoJSON.GeometryObject.single(.point(.init(latitude: -33.9451, longitude: 151.2581)))
16+
let pointAsDict = point.toJSON(prune: true)
17+
let fromDict = try GeoJSON.GeometryObject(dict: pointAsDict)
18+
XCTAssertEqual(point, fromDict)
19+
}
20+
21+
func testGeometryRoundtrip() throws {
22+
for name in ["point", "linestring", "multilinestring", "multipoint", "multipolygon", "polygon", "polygon-hole", "geometrycollection"] {
23+
24+
let data = try XCTestCase.loadData(filename: name)
25+
let parsed = try GeoJSON(data: data)
26+
27+
guard
28+
case let .geometry(geometry) = parsed.object
29+
else { return XCTFail("Unexpected structure") }
30+
31+
let asDict = geometry.toJSON(prune: true)
32+
let fromDict = try GeoJSON.GeometryObject(dict: asDict)
33+
XCTAssertEqual(geometry, fromDict)
34+
}
35+
36+
}
37+
38+
}

0 commit comments

Comments
 (0)