Skip to content

Commit b40fd9b

Browse files
committed
Can parse x/y/z coordinate JSON dictionaries now
1 parent fc20eea commit b40fd9b

File tree

2 files changed

+60
-11
lines changed

2 files changed

+60
-11
lines changed

Sources/GISTools/GeoJson/Coordinate3D.swift

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -346,27 +346,45 @@ extension Coordinate3D: Projectable {
346346

347347
extension Coordinate3D: GeoJsonReadable {
348348

349-
/// Create a coordinate from a JSON object.
349+
/// Create a coordinate from a JSON object, which can either be
350+
/// - GeoJSON,
351+
/// - or a dictionary with `x`, `y` and `z` values.
350352
///
351353
/// - Note: The [GeoJSON spec](https://datatracker.ietf.org/doc/html/rfc7946)
352354
/// uses CRS:84 that specifies coordinates in longitude/latitude order.
353-
/// - Important: The third value will always be ``altitude``, the fourth value
355+
/// - Important: The third value in GeoJSON coordinates will always be ``altitude``, the fourth value
354356
/// will be ``m`` if it exists. ``altitude`` can be a JSON `null` value.
355357
/// - important: The source is expected to be in EPSG:4326.
356358
public init?(json: Any?) {
357-
guard let pointArray = json as? [Double?],
358-
pointArray.count >= 2,
359-
let pLongitude = pointArray[0],
360-
let pLatitude = pointArray[1]
361-
else { return nil }
359+
var pLongitude: Double?
360+
var pLatitude: Double?
361+
var pAltitude: CLLocationDistance?
362+
var pM: Double?
363+
364+
if let pointArray = json as? [Double?],
365+
pointArray.count >= 2
366+
{
367+
pLongitude = pointArray[0]
368+
pLatitude = pointArray[1]
369+
pAltitude = if pointArray.count >= 3 { pointArray[2] } else { nil }
370+
pM = if pointArray.count >= 4 { pointArray[3] } else { nil }
371+
}
372+
else if let pointDictionary = json as? [String: Any] {
373+
pLongitude = pointDictionary["x"] as? Double
374+
pLatitude = pointDictionary["y"] as? Double
375+
pAltitude = pointDictionary["z"] as? CLLocationDistance
376+
pM = pointDictionary["m"] as? Double
377+
}
378+
else {
379+
return nil
380+
}
362381

363-
let pAltitude: CLLocationDistance? = if pointArray.count >= 3 { pointArray[2] } else { nil }
364-
let pM: Double? = if pointArray.count >= 4 { pointArray[3] } else { nil }
382+
guard let pLongitude, let pLatitude else { return nil }
365383

366384
self.init(latitude: pLatitude, longitude: pLongitude, altitude: pAltitude, m: pM)
367385
}
368386

369-
/// Dump the coordinate as a JSON object.
387+
/// Dump the coordinate as a GeoJSON coordinate.
370388
///
371389
/// - Important: The result JSON object will have a `null` value for the altitude
372390
/// if the ``altitude`` is `nil` and ``m`` exists.

Tests/GISToolsTests/GeoJson/CoordinateTests.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,21 @@ final class CoordinateTests: XCTestCase {
6262
}
6363

6464
func testDecodable() throws {
65-
let coordinateData = try XCTUnwrap("[10,15]".data(using: .utf8))
65+
let coordinateData = try XCTUnwrap("[10,15]".data(using: .utf8))
6666
let decodedCoordinate = try JSONDecoder().decode(Coordinate3D.self, from: coordinateData)
6767

6868
XCTAssertEqual(decodedCoordinate.asJson, [10.0, 15.0])
6969
}
7070

71+
func testJSONDictionary() throws {
72+
let decodedCoordinate = try XCTUnwrap(Coordinate3D(json: [
73+
"x": 10.0,
74+
"y": 15.0,
75+
]))
76+
77+
XCTAssertEqual(decodedCoordinate.asJson, [10.0, 15.0])
78+
}
79+
7180
func testDecodableInvalid() throws {
7281
let coordinateData1 = try XCTUnwrap("[10]".data(using: .utf8))
7382
XCTAssertThrowsError(try JSONDecoder().decode(Coordinate3D.self, from: coordinateData1))
@@ -85,6 +94,28 @@ final class CoordinateTests: XCTestCase {
8594
XCTAssertThrowsError(try JSONDecoder().decode(Coordinate3D.self, from: coordinateData5))
8695
}
8796

97+
func testJSONDictionaryInvalid() throws {
98+
XCTAssertNil(Coordinate3D(json: [
99+
"x": 10.0,
100+
]))
101+
102+
XCTAssertNil(Coordinate3D(json: [
103+
"y": 15.0,
104+
]))
105+
106+
XCTAssertNil(Coordinate3D(json: []))
107+
108+
XCTAssertNil(Coordinate3D(json: [
109+
"x": 10.0,
110+
"y": nil,
111+
]))
112+
113+
XCTAssertNil(Coordinate3D(json: [
114+
"x": 10.0,
115+
"y": NSNull(),
116+
]))
117+
}
118+
88119
func testDecodableInvalidNull() throws {
89120
let coordinateDataM = try XCTUnwrap("[10,null,null,1234]".data(using: .utf8))
90121
XCTAssertThrowsError(try JSONDecoder().decode(Coordinate3D.self, from: coordinateDataM))

0 commit comments

Comments
 (0)