Coverage Report

Created: 2025-06-13 06:18

/src/gdal/ogr/ogrgeojsongeometry.cpp
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: MIT
2
// Copyright 2007, Mateusz Loskot
3
// Copyright 2008-2024, Even Rouault <even.rouault at spatialys.com>
4
5
/*! @cond Doxygen_Suppress */
6
7
#include "ogrgeojsongeometry.h"
8
#include "ogrlibjsonutils.h"
9
10
#include "ogr_geometry.h"
11
#include "ogr_spatialref.h"
12
13
static OGRPoint *OGRGeoJSONReadPoint(json_object *poObj);
14
static OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj);
15
static OGRLineString *OGRGeoJSONReadLineString(json_object *poObj,
16
                                               bool bRaw = false);
17
static OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj);
18
static OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj);
19
static OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj);
20
static OGRGeometryCollection *
21
OGRGeoJSONReadGeometryCollection(json_object *poObj,
22
                                 OGRSpatialReference *poSRS = nullptr);
23
24
/************************************************************************/
25
/*                           OGRGeoJSONGetType                          */
26
/************************************************************************/
27
28
GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj)
29
0
{
30
0
    if (nullptr == poObj)
31
0
        return GeoJSONObject::eUnknown;
32
33
0
    json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
34
0
    if (nullptr == poObjType)
35
0
        return GeoJSONObject::eUnknown;
36
37
0
    const char *name = json_object_get_string(poObjType);
38
0
    if (EQUAL(name, "Point"))
39
0
        return GeoJSONObject::ePoint;
40
0
    else if (EQUAL(name, "LineString"))
41
0
        return GeoJSONObject::eLineString;
42
0
    else if (EQUAL(name, "Polygon"))
43
0
        return GeoJSONObject::ePolygon;
44
0
    else if (EQUAL(name, "MultiPoint"))
45
0
        return GeoJSONObject::eMultiPoint;
46
0
    else if (EQUAL(name, "MultiLineString"))
47
0
        return GeoJSONObject::eMultiLineString;
48
0
    else if (EQUAL(name, "MultiPolygon"))
49
0
        return GeoJSONObject::eMultiPolygon;
50
0
    else if (EQUAL(name, "GeometryCollection"))
51
0
        return GeoJSONObject::eGeometryCollection;
52
0
    else if (EQUAL(name, "Feature"))
53
0
        return GeoJSONObject::eFeature;
54
0
    else if (EQUAL(name, "FeatureCollection"))
55
0
        return GeoJSONObject::eFeatureCollection;
56
0
    else
57
0
        return GeoJSONObject::eUnknown;
58
0
}
59
60
/************************************************************************/
61
/*                   OGRGeoJSONGetOGRGeometryType()                     */
62
/************************************************************************/
63
64
OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj)
65
0
{
66
0
    if (nullptr == poObj)
67
0
        return wkbUnknown;
68
69
0
    json_object *poObjType = CPL_json_object_object_get(poObj, "type");
70
0
    if (nullptr == poObjType)
71
0
        return wkbUnknown;
72
73
0
    OGRwkbGeometryType eType = wkbUnknown;
74
0
    const char *name = json_object_get_string(poObjType);
75
0
    if (EQUAL(name, "Point"))
76
0
        eType = wkbPoint;
77
0
    else if (EQUAL(name, "LineString"))
78
0
        eType = wkbLineString;
79
0
    else if (EQUAL(name, "Polygon"))
80
0
        eType = wkbPolygon;
81
0
    else if (EQUAL(name, "MultiPoint"))
82
0
        eType = wkbMultiPoint;
83
0
    else if (EQUAL(name, "MultiLineString"))
84
0
        eType = wkbMultiLineString;
85
0
    else if (EQUAL(name, "MultiPolygon"))
86
0
        eType = wkbMultiPolygon;
87
0
    else if (EQUAL(name, "GeometryCollection"))
88
0
        eType = wkbGeometryCollection;
89
0
    else
90
0
        return wkbUnknown;
91
92
0
    json_object *poCoordinates;
93
0
    if (eType == wkbGeometryCollection)
94
0
    {
95
0
        json_object *poGeometries =
96
0
            CPL_json_object_object_get(poObj, "geometries");
97
0
        if (poGeometries &&
98
0
            json_object_get_type(poGeometries) == json_type_array &&
99
0
            json_object_array_length(poGeometries) > 0)
100
0
        {
101
0
            if (OGR_GT_HasZ(OGRGeoJSONGetOGRGeometryType(
102
0
                    json_object_array_get_idx(poGeometries, 0))))
103
0
                eType = OGR_GT_SetZ(eType);
104
0
        }
105
0
    }
106
0
    else
107
0
    {
108
0
        poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
109
0
        if (poCoordinates &&
110
0
            json_object_get_type(poCoordinates) == json_type_array &&
111
0
            json_object_array_length(poCoordinates) > 0)
112
0
        {
113
0
            while (true)
114
0
            {
115
0
                auto poChild = json_object_array_get_idx(poCoordinates, 0);
116
0
                if (!(poChild &&
117
0
                      json_object_get_type(poChild) == json_type_array &&
118
0
                      json_object_array_length(poChild) > 0))
119
0
                {
120
0
                    if (json_object_array_length(poCoordinates) == 3)
121
0
                        eType = OGR_GT_SetZ(eType);
122
0
                    break;
123
0
                }
124
0
                poCoordinates = poChild;
125
0
            }
126
0
        }
127
0
    }
128
129
0
    return eType;
130
0
}
131
132
/************************************************************************/
133
/*                           OGRGeoJSONReadGeometry                     */
134
/************************************************************************/
135
136
OGRGeometry *OGRGeoJSONReadGeometry(json_object *poObj,
137
                                    OGRSpatialReference *poParentSRS)
138
0
{
139
140
0
    OGRGeometry *poGeometry = nullptr;
141
0
    OGRSpatialReference *poSRS = nullptr;
142
0
    lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, "crs");
143
0
    if (entry != nullptr)
144
0
    {
145
0
        json_object *poObjSrs =
146
0
            static_cast<json_object *>(const_cast<void *>(entry->v));
147
0
        if (poObjSrs != nullptr)
148
0
        {
149
0
            poSRS = OGRGeoJSONReadSpatialReference(poObj);
150
0
        }
151
0
    }
152
153
0
    OGRSpatialReference *poSRSToAssign = nullptr;
154
0
    if (entry != nullptr)
155
0
    {
156
0
        poSRSToAssign = poSRS;
157
0
    }
158
0
    else if (poParentSRS)
159
0
    {
160
0
        poSRSToAssign = poParentSRS;
161
0
    }
162
0
    else
163
0
    {
164
        // Assign WGS84 if no CRS defined on geometry.
165
0
        poSRSToAssign = OGRSpatialReference::GetWGS84SRS();
166
0
    }
167
168
0
    GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
169
0
    if (GeoJSONObject::ePoint == objType)
170
0
        poGeometry = OGRGeoJSONReadPoint(poObj);
171
0
    else if (GeoJSONObject::eMultiPoint == objType)
172
0
        poGeometry = OGRGeoJSONReadMultiPoint(poObj);
173
0
    else if (GeoJSONObject::eLineString == objType)
174
0
        poGeometry = OGRGeoJSONReadLineString(poObj);
175
0
    else if (GeoJSONObject::eMultiLineString == objType)
176
0
        poGeometry = OGRGeoJSONReadMultiLineString(poObj);
177
0
    else if (GeoJSONObject::ePolygon == objType)
178
0
        poGeometry = OGRGeoJSONReadPolygon(poObj);
179
0
    else if (GeoJSONObject::eMultiPolygon == objType)
180
0
        poGeometry = OGRGeoJSONReadMultiPolygon(poObj);
181
0
    else if (GeoJSONObject::eGeometryCollection == objType)
182
0
        poGeometry = OGRGeoJSONReadGeometryCollection(poObj, poSRSToAssign);
183
0
    else
184
0
    {
185
0
        CPLError(CE_Warning, CPLE_AppDefined,
186
0
                 "Unsupported geometry type detected. "
187
0
                 "Feature gets NULL geometry assigned.");
188
0
    }
189
190
0
    if (poGeometry && GeoJSONObject::eGeometryCollection != objType)
191
0
        poGeometry->assignSpatialReference(poSRSToAssign);
192
193
0
    if (poSRS)
194
0
        poSRS->Release();
195
196
0
    return poGeometry;
197
0
}
198
199
/************************************************************************/
200
/*                       GetJSONConstructName()                         */
201
/************************************************************************/
202
203
static const char *GetJSONConstructName(json_type eType)
204
0
{
205
0
    switch (eType)
206
0
    {
207
0
        case json_type_null:
208
0
            break;
209
0
        case json_type_boolean:
210
0
            return "boolean";
211
0
        case json_type_double:
212
0
            return "double";
213
0
        case json_type_int:
214
0
            return "int";
215
0
        case json_type_object:
216
0
            return "object";
217
0
        case json_type_array:
218
0
            return "array";
219
0
        case json_type_string:
220
0
            return "string";
221
0
    }
222
0
    return "null";
223
0
}
224
225
/************************************************************************/
226
/*                        OGRGeoJSONGetCoordinate()                     */
227
/************************************************************************/
228
229
static double OGRGeoJSONGetCoordinate(json_object *poObj,
230
                                      const char *pszCoordName, int nIndex,
231
                                      bool &bValid)
232
0
{
233
0
    json_object *poObjCoord = json_object_array_get_idx(poObj, nIndex);
234
0
    if (nullptr == poObjCoord)
235
0
    {
236
0
        CPLDebug("GeoJSON", "Point: got null object for %s.", pszCoordName);
237
0
        bValid = false;
238
0
        return 0.0;
239
0
    }
240
241
0
    const json_type eType = json_object_get_type(poObjCoord);
242
0
    if (json_type_double != eType && json_type_int != eType)
243
0
    {
244
0
        CPLError(CE_Failure, CPLE_AppDefined,
245
0
                 "OGRGeoJSONGetCoordinate(): invalid '%s' coordinate. "
246
0
                 "Unexpected type %s for '%s'. Expected double or integer.",
247
0
                 pszCoordName, GetJSONConstructName(eType),
248
0
                 json_object_to_json_string(poObjCoord));
249
0
        bValid = false;
250
0
        return 0.0;
251
0
    }
252
253
0
    return json_object_get_double(poObjCoord);
254
0
}
255
256
/************************************************************************/
257
/*                           OGRGeoJSONReadRawPoint                     */
258
/************************************************************************/
259
260
static bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point)
261
0
{
262
0
    if (json_type_array == json_object_get_type(poObj))
263
0
    {
264
0
        const int nSize = static_cast<int>(json_object_array_length(poObj));
265
266
0
        if (nSize < GeoJSONObject::eMinCoordinateDimension)
267
0
        {
268
0
            CPLError(CE_Warning, CPLE_AppDefined,
269
0
                     "OGRGeoJSONReadRawPoint(): "
270
0
                     "Invalid coord dimension for '%s'. "
271
0
                     "At least 2 dimensions must be present.",
272
0
                     json_object_to_json_string(poObj));
273
0
            return false;
274
0
        }
275
276
0
        bool bValid = true;
277
0
        const double dfX = OGRGeoJSONGetCoordinate(poObj, "x", 0, bValid);
278
0
        const double dfY = OGRGeoJSONGetCoordinate(poObj, "y", 1, bValid);
279
0
        point.setX(dfX);
280
0
        point.setY(dfY);
281
282
        // Read Z coordinate.
283
0
        if (nSize >= GeoJSONObject::eMaxCoordinateDimension)
284
0
        {
285
0
            if (nSize > GeoJSONObject::eMaxCoordinateDimension)
286
0
            {
287
0
                CPLErrorOnce(CE_Warning, CPLE_AppDefined,
288
0
                             "OGRGeoJSONReadRawPoint(): too many members in "
289
0
                             "array '%s': %d. At most %d are handled. Ignoring "
290
0
                             "extra members.",
291
0
                             json_object_to_json_string(poObj), nSize,
292
0
                             GeoJSONObject::eMaxCoordinateDimension);
293
0
            }
294
            // Don't *expect* mixed-dimension geometries, although the
295
            // spec doesn't explicitly forbid this.
296
0
            const double dfZ = OGRGeoJSONGetCoordinate(poObj, "z", 2, bValid);
297
0
            point.setZ(dfZ);
298
0
        }
299
0
        else
300
0
        {
301
0
            point.flattenTo2D();
302
0
        }
303
0
        return bValid;
304
0
    }
305
0
    else
306
0
    {
307
0
        CPLError(CE_Failure, CPLE_AppDefined,
308
0
                 "OGRGeoJSONReadRawPoint(): invalid Point. "
309
0
                 "Unexpected type %s for '%s'. Expected array.",
310
0
                 GetJSONConstructName(json_object_get_type(poObj)),
311
0
                 json_object_to_json_string(poObj));
312
0
    }
313
314
0
    return false;
315
0
}
316
317
/************************************************************************/
318
/*                           OGRGeoJSONReadPoint                        */
319
/************************************************************************/
320
321
OGRPoint *OGRGeoJSONReadPoint(json_object *poObj)
322
0
{
323
0
    if (!poObj)
324
0
    {
325
0
        CPLError(CE_Failure, CPLE_AppDefined,
326
0
                 "OGRGeoJSONReadPoint(): invalid Point object. Got null.");
327
0
        return nullptr;
328
0
    }
329
0
    json_object *poObjCoords = OGRGeoJSONFindMemberByName(poObj, "coordinates");
330
0
    if (nullptr == poObjCoords)
331
0
    {
332
0
        CPLError(CE_Failure, CPLE_AppDefined,
333
0
                 "OGRGeoJSONReadPoint(): invalid Point object. "
334
0
                 "Missing \'coordinates\' member.");
335
0
        return nullptr;
336
0
    }
337
338
0
    auto poPoint = std::make_unique<OGRPoint>();
339
0
    if (!OGRGeoJSONReadRawPoint(poObjCoords, *poPoint))
340
0
    {
341
0
        return nullptr;
342
0
    }
343
344
0
    return poPoint.release();
345
0
}
346
347
/************************************************************************/
348
/*                           OGRGeoJSONReadMultiPoint                   */
349
/************************************************************************/
350
351
OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj)
352
0
{
353
0
    if (!poObj)
354
0
    {
355
0
        CPLError(
356
0
            CE_Failure, CPLE_AppDefined,
357
0
            "OGRGeoJSONReadMultiPoint(): invalid MultiPoint object. Got null.");
358
0
        return nullptr;
359
0
    }
360
0
    json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
361
0
    if (nullptr == poObjPoints)
362
0
    {
363
0
        CPLError(CE_Failure, CPLE_AppDefined,
364
0
                 "Invalid MultiPoint object. "
365
0
                 "Missing \'coordinates\' member.");
366
0
        return nullptr;
367
0
    }
368
369
0
    std::unique_ptr<OGRMultiPoint> poMultiPoint;
370
0
    if (json_type_array == json_object_get_type(poObjPoints))
371
0
    {
372
0
        const auto nPoints = json_object_array_length(poObjPoints);
373
374
0
        poMultiPoint = std::make_unique<OGRMultiPoint>();
375
376
0
        for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
377
0
        {
378
0
            json_object *poObjCoords =
379
0
                json_object_array_get_idx(poObjPoints, i);
380
381
0
            OGRPoint pt;
382
0
            if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
383
0
            {
384
0
                return nullptr;
385
0
            }
386
0
            poMultiPoint->addGeometry(&pt);
387
0
        }
388
0
    }
389
0
    else
390
0
    {
391
0
        CPLError(CE_Failure, CPLE_AppDefined,
392
0
                 "OGRGeoJSONReadMultiPoint(): invalid MultiPoint. "
393
0
                 "Unexpected type %s for '%s'. Expected array.",
394
0
                 GetJSONConstructName(json_object_get_type(poObjPoints)),
395
0
                 json_object_to_json_string(poObjPoints));
396
0
    }
397
398
0
    return poMultiPoint.release();
399
0
}
400
401
/************************************************************************/
402
/*                           OGRGeoJSONReadLineString                   */
403
/************************************************************************/
404
405
OGRLineString *OGRGeoJSONReadLineString(json_object *poObj, bool bRaw)
406
0
{
407
0
    if (!poObj)
408
0
    {
409
0
        CPLError(
410
0
            CE_Failure, CPLE_AppDefined,
411
0
            "OGRGeoJSONReadLineString(): invalid LineString object. Got null.");
412
0
        return nullptr;
413
0
    }
414
0
    json_object *poObjPoints = nullptr;
415
416
0
    if (!bRaw)
417
0
    {
418
0
        poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
419
0
        if (nullptr == poObjPoints)
420
0
        {
421
0
            CPLError(CE_Failure, CPLE_AppDefined,
422
0
                     "Invalid LineString object. "
423
0
                     "Missing \'coordinates\' member.");
424
0
            return nullptr;
425
0
        }
426
0
    }
427
0
    else
428
0
    {
429
0
        poObjPoints = poObj;
430
0
    }
431
432
0
    std::unique_ptr<OGRLineString> poLine;
433
434
0
    if (json_type_array == json_object_get_type(poObjPoints))
435
0
    {
436
0
        const auto nPoints = json_object_array_length(poObjPoints);
437
438
0
        poLine = std::make_unique<OGRLineString>();
439
0
        poLine->setNumPoints(static_cast<int>(nPoints));
440
441
0
        for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
442
0
        {
443
0
            json_object *poObjCoords =
444
0
                json_object_array_get_idx(poObjPoints, i);
445
446
0
            OGRPoint pt;
447
0
            if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
448
0
            {
449
0
                return nullptr;
450
0
            }
451
0
            if (pt.getCoordinateDimension() == 2)
452
0
            {
453
0
                poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
454
0
            }
455
0
            else
456
0
            {
457
0
                poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
458
0
                                 pt.getZ());
459
0
            }
460
0
        }
461
0
    }
462
0
    else
463
0
    {
464
0
        CPLError(CE_Failure, CPLE_AppDefined,
465
0
                 "OGRGeoJSONReadLineString(): invalid MultiLineString. "
466
0
                 "Unexpected type %s for '%s'. Expected array.",
467
0
                 GetJSONConstructName(json_object_get_type(poObjPoints)),
468
0
                 json_object_to_json_string(poObjPoints));
469
0
    }
470
471
0
    return poLine.release();
472
0
}
473
474
/************************************************************************/
475
/*                           OGRGeoJSONReadMultiLineString              */
476
/************************************************************************/
477
478
OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj)
479
0
{
480
0
    CPLAssert(nullptr != poObj);
481
482
0
    json_object *poObjLines = OGRGeoJSONFindMemberByName(poObj, "coordinates");
483
0
    if (nullptr == poObjLines)
484
0
    {
485
0
        CPLError(CE_Failure, CPLE_AppDefined,
486
0
                 "Invalid MultiLineString object. "
487
0
                 "Missing \'coordinates\' member.");
488
0
        return nullptr;
489
0
    }
490
491
0
    std::unique_ptr<OGRMultiLineString> poMultiLine;
492
493
0
    if (json_type_array == json_object_get_type(poObjLines))
494
0
    {
495
0
        const auto nLines = json_object_array_length(poObjLines);
496
497
0
        poMultiLine = std::make_unique<OGRMultiLineString>();
498
499
0
        for (auto i = decltype(nLines){0}; i < nLines; ++i)
500
0
        {
501
0
            json_object *poObjLine = json_object_array_get_idx(poObjLines, i);
502
503
0
            OGRLineString *poLine = OGRGeoJSONReadLineString(poObjLine, true);
504
0
            if (poLine)
505
0
            {
506
0
                poMultiLine->addGeometryDirectly(poLine);
507
0
            }
508
0
        }
509
0
    }
510
0
    else
511
0
    {
512
0
        CPLError(CE_Failure, CPLE_AppDefined,
513
0
                 "OGRGeoJSONReadLineString(): invalid LineString. "
514
0
                 "Unexpected type %s for '%s'. Expected array.",
515
0
                 GetJSONConstructName(json_object_get_type(poObjLines)),
516
0
                 json_object_to_json_string(poObjLines));
517
0
    }
518
519
0
    return poMultiLine.release();
520
0
}
521
522
/************************************************************************/
523
/*                           OGRGeoJSONReadLinearRing                   */
524
/************************************************************************/
525
526
OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj)
527
0
{
528
0
    CPLAssert(nullptr != poObj);
529
530
0
    std::unique_ptr<OGRLinearRing> poRing;
531
532
0
    if (json_type_array == json_object_get_type(poObj))
533
0
    {
534
0
        const auto nPoints = json_object_array_length(poObj);
535
536
0
        poRing = std::make_unique<OGRLinearRing>();
537
0
        poRing->setNumPoints(static_cast<int>(nPoints));
538
539
0
        for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
540
0
        {
541
0
            json_object *poObjCoords = json_object_array_get_idx(poObj, i);
542
543
0
            OGRPoint pt;
544
0
            if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
545
0
            {
546
0
                return nullptr;
547
0
            }
548
549
0
            if (2 == pt.getCoordinateDimension())
550
0
                poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
551
0
            else
552
0
                poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
553
0
                                 pt.getZ());
554
0
        }
555
0
    }
556
0
    else
557
0
    {
558
0
        CPLError(CE_Warning, CPLE_AppDefined,
559
0
                 "OGRGeoJSONReadLinearRing(): unexpected type of JSON "
560
0
                 "construct %s for '%s'. Expected array.",
561
0
                 GetJSONConstructName(json_object_get_type(poObj)),
562
0
                 json_object_to_json_string(poObj));
563
0
    }
564
565
0
    return poRing.release();
566
0
}
567
568
/************************************************************************/
569
/*                           OGRGeoJSONReadPolygon                      */
570
/************************************************************************/
571
572
OGRPolygon *OGRGeoJSONReadPolygon(json_object *poObj, bool bRaw)
573
0
{
574
0
    if (!poObj)
575
0
    {
576
0
        CPLError(CE_Failure, CPLE_AppDefined,
577
0
                 "OGRGeoJSONReadPolygon(): invalid Polygon object. Got null.");
578
0
        return nullptr;
579
0
    }
580
0
    json_object *poObjRings = nullptr;
581
582
0
    if (!bRaw)
583
0
    {
584
0
        poObjRings = OGRGeoJSONFindMemberByName(poObj, "coordinates");
585
0
        if (nullptr == poObjRings)
586
0
        {
587
0
            CPLError(CE_Failure, CPLE_AppDefined,
588
0
                     "Invalid Polygon object. "
589
0
                     "Missing \'coordinates\' member.");
590
0
            return nullptr;
591
0
        }
592
0
    }
593
0
    else
594
0
    {
595
0
        poObjRings = poObj;
596
0
    }
597
598
0
    std::unique_ptr<OGRPolygon> poPolygon;
599
600
0
    if (json_type_array == json_object_get_type(poObjRings))
601
0
    {
602
0
        const auto nRings = json_object_array_length(poObjRings);
603
0
        if (nRings > 0)
604
0
        {
605
0
            json_object *poObjPoints = json_object_array_get_idx(poObjRings, 0);
606
0
            if (!poObjPoints)
607
0
            {
608
0
                poPolygon = std::make_unique<OGRPolygon>();
609
0
            }
610
0
            else
611
0
            {
612
0
                OGRLinearRing *poRing = OGRGeoJSONReadLinearRing(poObjPoints);
613
0
                if (poRing)
614
0
                {
615
0
                    poPolygon = std::make_unique<OGRPolygon>();
616
0
                    poPolygon->addRingDirectly(poRing);
617
0
                }
618
0
            }
619
620
0
            for (auto i = decltype(nRings){1};
621
0
                 i < nRings && nullptr != poPolygon; ++i)
622
0
            {
623
0
                poObjPoints = json_object_array_get_idx(poObjRings, i);
624
0
                if (poObjPoints)
625
0
                {
626
0
                    OGRLinearRing *poRing =
627
0
                        OGRGeoJSONReadLinearRing(poObjPoints);
628
0
                    if (poRing)
629
0
                    {
630
0
                        poPolygon->addRingDirectly(poRing);
631
0
                    }
632
0
                }
633
0
            }
634
0
        }
635
0
        else
636
0
        {
637
0
            poPolygon = std::make_unique<OGRPolygon>();
638
0
        }
639
0
    }
640
0
    else
641
0
    {
642
0
        CPLError(CE_Warning, CPLE_AppDefined,
643
0
                 "OGRGeoJSONReadPolygon(): unexpected type of JSON construct "
644
0
                 "%s for '%s'. Expected array.",
645
0
                 GetJSONConstructName(json_object_get_type(poObjRings)),
646
0
                 json_object_to_json_string(poObjRings));
647
0
    }
648
649
0
    return poPolygon.release();
650
0
}
651
652
/************************************************************************/
653
/*                           OGRGeoJSONReadMultiPolygon                 */
654
/************************************************************************/
655
656
OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj)
657
0
{
658
0
    CPLAssert(nullptr != poObj);
659
660
0
    json_object *poObjPolys = OGRGeoJSONFindMemberByName(poObj, "coordinates");
661
0
    if (nullptr == poObjPolys)
662
0
    {
663
0
        CPLError(CE_Failure, CPLE_AppDefined,
664
0
                 "Invalid MultiPolygon object. "
665
0
                 "Missing \'coordinates\' member.");
666
0
        return nullptr;
667
0
    }
668
669
0
    std::unique_ptr<OGRMultiPolygon> poMultiPoly;
670
671
0
    if (json_type_array == json_object_get_type(poObjPolys))
672
0
    {
673
0
        const auto nPolys = json_object_array_length(poObjPolys);
674
675
0
        poMultiPoly = std::make_unique<OGRMultiPolygon>();
676
677
0
        for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
678
0
        {
679
0
            json_object *poObjPoly = json_object_array_get_idx(poObjPolys, i);
680
0
            if (!poObjPoly)
681
0
            {
682
0
                poMultiPoly->addGeometryDirectly(
683
0
                    std::make_unique<OGRPolygon>().release());
684
0
            }
685
0
            else
686
0
            {
687
0
                OGRPolygon *poPoly = OGRGeoJSONReadPolygon(poObjPoly, true);
688
0
                if (poPoly)
689
0
                {
690
0
                    poMultiPoly->addGeometryDirectly(poPoly);
691
0
                }
692
0
            }
693
0
        }
694
0
    }
695
0
    else
696
0
    {
697
0
        CPLError(CE_Warning, CPLE_AppDefined,
698
0
                 "OGRGeoJSONReadMultiPolygon(): unexpected type of JSON "
699
0
                 "construct %s for '%s'. Expected array.",
700
0
                 GetJSONConstructName(json_object_get_type(poObjPolys)),
701
0
                 json_object_to_json_string(poObjPolys));
702
0
    }
703
704
0
    return poMultiPoly.release();
705
0
}
706
707
/************************************************************************/
708
/*                    OGRGeoJSONReadGeometryCollection                  */
709
/************************************************************************/
710
711
OGRGeometryCollection *
712
OGRGeoJSONReadGeometryCollection(json_object *poObj, OGRSpatialReference *poSRS)
713
0
{
714
0
    CPLAssert(nullptr != poObj);
715
716
0
    json_object *poObjGeoms = OGRGeoJSONFindMemberByName(poObj, "geometries");
717
0
    if (nullptr == poObjGeoms)
718
0
    {
719
0
        CPLError(CE_Failure, CPLE_AppDefined,
720
0
                 "Invalid GeometryCollection object. "
721
0
                 "Missing \'geometries\' member.");
722
0
        return nullptr;
723
0
    }
724
725
0
    std::unique_ptr<OGRGeometryCollection> poCollection;
726
727
0
    if (json_type_array == json_object_get_type(poObjGeoms))
728
0
    {
729
0
        poCollection = std::make_unique<OGRGeometryCollection>();
730
0
        poCollection->assignSpatialReference(poSRS);
731
732
0
        const auto nGeoms = json_object_array_length(poObjGeoms);
733
0
        for (auto i = decltype(nGeoms){0}; i < nGeoms; ++i)
734
0
        {
735
0
            json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
736
0
            if (!poObjGeom)
737
0
            {
738
0
                CPLError(CE_Warning, CPLE_AppDefined,
739
0
                         "OGRGeoJSONReadGeometryCollection(): skipping null "
740
0
                         "sub-geometry");
741
0
                continue;
742
0
            }
743
744
0
            OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObjGeom, poSRS);
745
0
            if (poGeometry)
746
0
            {
747
0
                poCollection->addGeometryDirectly(poGeometry);
748
0
            }
749
0
        }
750
0
    }
751
0
    else
752
0
    {
753
0
        CPLError(CE_Warning, CPLE_AppDefined,
754
0
                 "OGRGeoJSONReadGeometryCollection(): unexpected type of JSON "
755
0
                 "construct %s for '%s'. Expected array.",
756
0
                 GetJSONConstructName(json_object_get_type(poObjGeoms)),
757
0
                 json_object_to_json_string(poObjGeoms));
758
0
    }
759
760
0
    return poCollection.release();
761
0
}
762
763
/************************************************************************/
764
/*                           OGRGeoJSONGetGeometryName()                */
765
/************************************************************************/
766
767
const char *OGRGeoJSONGetGeometryName(OGRGeometry const *poGeometry)
768
0
{
769
0
    CPLAssert(nullptr != poGeometry);
770
771
0
    const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
772
773
0
    if (wkbPoint == eType)
774
0
        return "Point";
775
0
    else if (wkbLineString == eType)
776
0
        return "LineString";
777
0
    else if (wkbPolygon == eType)
778
0
        return "Polygon";
779
0
    else if (wkbMultiPoint == eType)
780
0
        return "MultiPoint";
781
0
    else if (wkbMultiLineString == eType)
782
0
        return "MultiLineString";
783
0
    else if (wkbMultiPolygon == eType)
784
0
        return "MultiPolygon";
785
0
    else if (wkbGeometryCollection == eType)
786
0
        return "GeometryCollection";
787
788
0
    return "Unknown";
789
0
}
790
791
/************************************************************************/
792
/*                    OGRGeoJSONReadSpatialReference                    */
793
/************************************************************************/
794
795
OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj)
796
0
{
797
798
    /* -------------------------------------------------------------------- */
799
    /*      Read spatial reference definition.                              */
800
    /* -------------------------------------------------------------------- */
801
0
    OGRSpatialReference *poSRS = nullptr;
802
803
0
    json_object *poObjSrs = OGRGeoJSONFindMemberByName(poObj, "crs");
804
0
    if (nullptr != poObjSrs)
805
0
    {
806
0
        json_object *poObjSrsType =
807
0
            OGRGeoJSONFindMemberByName(poObjSrs, "type");
808
0
        if (poObjSrsType == nullptr)
809
0
            return nullptr;
810
811
0
        const char *pszSrsType = json_object_get_string(poObjSrsType);
812
813
        // TODO: Add URL and URN types support.
814
0
        if (STARTS_WITH_CI(pszSrsType, "NAME"))
815
0
        {
816
0
            json_object *poObjSrsProps =
817
0
                OGRGeoJSONFindMemberByName(poObjSrs, "properties");
818
0
            if (poObjSrsProps == nullptr)
819
0
                return nullptr;
820
821
0
            json_object *poNameURL =
822
0
                OGRGeoJSONFindMemberByName(poObjSrsProps, "name");
823
0
            if (poNameURL == nullptr)
824
0
                return nullptr;
825
826
0
            const char *pszName = json_object_get_string(poNameURL);
827
828
            // Mostly to emulate GDAL 2.x behavior
829
            // See https://github.com/OSGeo/gdal/issues/2035
830
0
            if (EQUAL(pszName, "urn:ogc:def:crs:OGC:1.3:CRS84"))
831
0
                pszName = "EPSG:4326";
832
833
0
            poSRS = new OGRSpatialReference();
834
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
835
0
            if (OGRERR_NONE !=
836
0
                poSRS->SetFromUserInput(
837
0
                    pszName,
838
0
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()))
839
0
            {
840
0
                delete poSRS;
841
0
                poSRS = nullptr;
842
0
            }
843
0
        }
844
845
0
        else if (STARTS_WITH_CI(pszSrsType, "EPSG"))
846
0
        {
847
0
            json_object *poObjSrsProps =
848
0
                OGRGeoJSONFindMemberByName(poObjSrs, "properties");
849
0
            if (poObjSrsProps == nullptr)
850
0
                return nullptr;
851
852
0
            json_object *poObjCode =
853
0
                OGRGeoJSONFindMemberByName(poObjSrsProps, "code");
854
0
            if (poObjCode == nullptr)
855
0
                return nullptr;
856
857
0
            int nEPSG = json_object_get_int(poObjCode);
858
859
0
            poSRS = new OGRSpatialReference();
860
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
861
0
            if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
862
0
            {
863
0
                delete poSRS;
864
0
                poSRS = nullptr;
865
0
            }
866
0
        }
867
868
0
        else if (STARTS_WITH_CI(pszSrsType, "URL") ||
869
0
                 STARTS_WITH_CI(pszSrsType, "LINK"))
870
0
        {
871
0
            json_object *poObjSrsProps =
872
0
                OGRGeoJSONFindMemberByName(poObjSrs, "properties");
873
0
            if (poObjSrsProps == nullptr)
874
0
                return nullptr;
875
876
0
            json_object *poObjURL =
877
0
                OGRGeoJSONFindMemberByName(poObjSrsProps, "url");
878
879
0
            if (nullptr == poObjURL)
880
0
            {
881
0
                poObjURL = OGRGeoJSONFindMemberByName(poObjSrsProps, "href");
882
0
            }
883
0
            if (poObjURL == nullptr)
884
0
                return nullptr;
885
886
0
            const char *pszURL = json_object_get_string(poObjURL);
887
888
0
            poSRS = new OGRSpatialReference();
889
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
890
0
            if (OGRERR_NONE != poSRS->importFromUrl(pszURL))
891
0
            {
892
0
                delete poSRS;
893
0
                poSRS = nullptr;
894
0
            }
895
0
        }
896
897
0
        else if (EQUAL(pszSrsType, "OGC"))
898
0
        {
899
0
            json_object *poObjSrsProps =
900
0
                OGRGeoJSONFindMemberByName(poObjSrs, "properties");
901
0
            if (poObjSrsProps == nullptr)
902
0
                return nullptr;
903
904
0
            json_object *poObjURN =
905
0
                OGRGeoJSONFindMemberByName(poObjSrsProps, "urn");
906
0
            if (poObjURN == nullptr)
907
0
                return nullptr;
908
909
0
            poSRS = new OGRSpatialReference();
910
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
911
0
            if (OGRERR_NONE !=
912
0
                poSRS->importFromURN(json_object_get_string(poObjURN)))
913
0
            {
914
0
                delete poSRS;
915
0
                poSRS = nullptr;
916
0
            }
917
0
        }
918
0
    }
919
920
    // Strip AXIS, since geojson has (easting, northing) / (longitude, latitude)
921
    // order.  According to http://www.geojson.org/geojson-spec.html#id2 :
922
    // "Point coordinates are in x, y order (easting, northing for projected
923
    // coordinates, longitude, latitude for geographic coordinates)".
924
0
    if (poSRS != nullptr)
925
0
    {
926
0
        OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode("GEOGCS");
927
0
        if (poGEOGCS != nullptr)
928
0
            poGEOGCS->StripNodes("AXIS");
929
0
    }
930
931
0
    return poSRS;
932
0
}
933
934
/************************************************************************/
935
/*                       OGR_G_CreateGeometryFromJson                   */
936
/************************************************************************/
937
938
/** Create a OGR geometry from a GeoJSON geometry object */
939
OGRGeometryH OGR_G_CreateGeometryFromJson(const char *pszJson)
940
0
{
941
0
    if (nullptr == pszJson)
942
0
    {
943
        // Translation failed.
944
0
        return nullptr;
945
0
    }
946
947
0
    json_object *poObj = nullptr;
948
0
    if (!OGRJSonParse(pszJson, &poObj))
949
0
        return nullptr;
950
951
0
    OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj);
952
953
    // Release JSON tree.
954
0
    json_object_put(poObj);
955
956
0
    return OGRGeometry::ToHandle(poGeometry);
957
0
}
958
959
/*! @endcond */