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