@@ -11,6 +11,8 @@ import XCTest
11
11
12
12
final class GeoJSONDecodableTests : XCTestCase {
13
13
14
+ // MARK: Specification tests
15
+
14
16
func testPosition2DDecode( ) throws {
15
17
let string : String = " [-1.55366,47.21881] "
16
18
let data : Data = try XCTUnwrap ( string. data ( using: . utf8) )
@@ -177,4 +179,192 @@ final class GeoJSONDecodableTests: XCTestCase {
177
179
XCTAssertEqual ( feature, expected)
178
180
}
179
181
182
+ // MARK: Real-world use cases
183
+
184
+ func testDecodeFeatureProperties( ) throws {
185
+ struct RealWorldProperties : Hashable , Codable {
186
+ let prop0 : String
187
+ }
188
+
189
+ let string = """
190
+ {
191
+ " type " : " Feature " ,
192
+ " geometry " : {
193
+ " type " : " Point " ,
194
+ " coordinates " : [102.0, 0.5]
195
+ },
196
+ " properties " : {
197
+ " prop0 " : " value0 "
198
+ }
199
+ }
200
+ """
201
+
202
+ let data : Data = try XCTUnwrap ( string. data ( using: . utf8) )
203
+ let feature = try JSONDecoder ( ) . decode ( Feature< RealWorldProperties> . self , from: data)
204
+
205
+ let expected : Feature = Feature (
206
+ geometry: . point2D( Point2D ( coordinates: . init( latitude: 0.5 , longitude: 102 ) ) ) ,
207
+ properties: RealWorldProperties ( prop0: " value0 " )
208
+ )
209
+ XCTAssertEqual ( feature, expected)
210
+ }
211
+
212
+ /// Example from [RFC 7946, section 1.5](https://datatracker.ietf.org/doc/html/rfc7946#section-1.5).
213
+ func testDecodeHeterogeneousFeatureCollection( ) throws {
214
+ enum HeterogeneousProperties : Hashable , Codable , CustomStringConvertible {
215
+ struct Properties1 : Hashable , Codable , CustomStringConvertible {
216
+ let prop0 : String
217
+
218
+ var description : String {
219
+ " {prop0: \" \( prop0) \" } "
220
+ }
221
+ }
222
+ struct Properties2 : Hashable , Codable , CustomStringConvertible {
223
+ let prop0 : String
224
+ let prop1 : Double
225
+
226
+ var description : String {
227
+ " {prop0: \" \( prop0) \" ,prop1: \( prop1) } "
228
+ }
229
+ }
230
+ struct Properties3 : Hashable , Codable , CustomStringConvertible {
231
+ struct Prop1 : Hashable , Codable , CustomStringConvertible {
232
+ let this : String
233
+
234
+ var description : String {
235
+ " {this: \" \( this) \" } "
236
+ }
237
+ }
238
+
239
+ let prop0 : String
240
+ let prop1 : Prop1
241
+
242
+ var description : String {
243
+ " {prop0: \" \( prop0) \" ,prop1: \( prop1) } "
244
+ }
245
+ }
246
+
247
+ enum CodingKeys : String , CodingKey {
248
+ case prop0, prop1
249
+ }
250
+
251
+ case type1( Properties1 )
252
+ case type2( Properties2 )
253
+ case type3( Properties3 )
254
+
255
+ var description : String {
256
+ switch self {
257
+ case . type1( let type1) :
258
+ return type1. description
259
+ case . type2( let type2) :
260
+ return type2. description
261
+ case . type3( let type3) :
262
+ return type3. description
263
+ }
264
+ }
265
+
266
+ init ( from decoder: Decoder ) throws {
267
+ let container = try decoder. container ( keyedBy: CodingKeys . self)
268
+
269
+ let prop0 = try container. decode ( String . self, forKey: . prop0)
270
+ do {
271
+ do {
272
+ let prop1 = try container. decode ( Double . self, forKey: . prop1)
273
+ self = . type2( . init( prop0: prop0, prop1: prop1) )
274
+ } catch {
275
+ let prop1 = try container. decode ( Properties3 . Prop1. self, forKey: . prop1)
276
+ self = . type3( . init( prop0: prop0, prop1: prop1) )
277
+ }
278
+ } catch {
279
+ self = . type1( . init( prop0: prop0) )
280
+ }
281
+ }
282
+
283
+ func encode( to encoder: Encoder ) throws {
284
+ fatalError ( " Useless " )
285
+ }
286
+ }
287
+
288
+ let string = """
289
+ {
290
+ " type " : " FeatureCollection " ,
291
+ " features " : [{
292
+ " type " : " Feature " ,
293
+ " geometry " : {
294
+ " type " : " Point " ,
295
+ " coordinates " : [102.0, 0.5]
296
+ },
297
+ " properties " : {
298
+ " prop0 " : " value0 "
299
+ }
300
+ }, {
301
+ " type " : " Feature " ,
302
+ " geometry " : {
303
+ " type " : " LineString " ,
304
+ " coordinates " : [
305
+ [102.0, 0.0],
306
+ [103.0, 1.0],
307
+ [104.0, 0.0],
308
+ [105.0, 1.0]
309
+ ]
310
+ },
311
+ " properties " : {
312
+ " prop0 " : " value0 " ,
313
+ " prop1 " : 0.0
314
+ }
315
+ }, {
316
+ " type " : " Feature " ,
317
+ " geometry " : {
318
+ " type " : " Polygon " ,
319
+ " coordinates " : [
320
+ [
321
+ [100.0, 0.0],
322
+ [101.0, 0.0],
323
+ [101.0, 1.0],
324
+ [100.0, 1.0],
325
+ [100.0, 0.0]
326
+ ]
327
+ ]
328
+ },
329
+ " properties " : {
330
+ " prop0 " : " value0 " ,
331
+ " prop1 " : {
332
+ " this " : " that "
333
+ }
334
+ }
335
+ }]
336
+ }
337
+ """
338
+
339
+ let data : Data = try XCTUnwrap ( string. data ( using: . utf8) )
340
+ let feature = try JSONDecoder ( ) . decode ( FeatureCollection< HeterogeneousProperties> . self , from: data)
341
+
342
+ let expected : FeatureCollection < HeterogeneousProperties > = FeatureCollection ( features: [
343
+ Feature (
344
+ geometry: . point2D( Point2D ( coordinates: . init( latitude: 0.5 , longitude: 102 ) ) ) ,
345
+ properties: . type1( . init( prop0: " value0 " ) )
346
+ ) ,
347
+ Feature (
348
+ geometry: . lineString2D( . init( coordinates: [
349
+ . init( latitude: 0.0 , longitude: 102.0 ) ,
350
+ . init( latitude: 1.0 , longitude: 103.0 ) ,
351
+ . init( latitude: 0.0 , longitude: 104.0 ) ,
352
+ . init( latitude: 1.0 , longitude: 105.0 )
353
+ ] ) !) ,
354
+ properties: . type2( . init( prop0: " value0 " , prop1: 0 ) )
355
+ ) ,
356
+ Feature (
357
+ geometry: . polygon2D( . init( coordinates: . init( arrayLiteral: [
358
+ . init( latitude: 0.0 , longitude: 100.0 ) ,
359
+ . init( latitude: 0.0 , longitude: 101.0 ) ,
360
+ . init( latitude: 1.0 , longitude: 101.0 ) ,
361
+ . init( latitude: 1.0 , longitude: 100.0 ) ,
362
+ . init( latitude: 0.0 , longitude: 100.0 )
363
+ ] ) ) ) ,
364
+ properties: . type3( . init( prop0: " value0 " , prop1: . init( this: " that " ) ) )
365
+ ) ,
366
+ ] )
367
+ XCTAssertEqual ( feature, expected)
368
+ }
369
+
180
370
}
0 commit comments