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 : OGRSpatialReference *poSRS = nullptr);
23 :
24 : /************************************************************************/
25 : /* OGRGeoJSONGetType */
26 : /************************************************************************/
27 :
28 2990 : GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj)
29 : {
30 2990 : if (nullptr == poObj)
31 0 : return GeoJSONObject::eUnknown;
32 :
33 2990 : json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
34 2990 : if (nullptr == poObjType)
35 2 : return GeoJSONObject::eUnknown;
36 :
37 2988 : const char *name = json_object_get_string(poObjType);
38 2988 : if (EQUAL(name, "Point"))
39 388 : return GeoJSONObject::ePoint;
40 2600 : else if (EQUAL(name, "LineString"))
41 67 : return GeoJSONObject::eLineString;
42 2533 : else if (EQUAL(name, "Polygon"))
43 1025 : return GeoJSONObject::ePolygon;
44 1508 : else if (EQUAL(name, "MultiPoint"))
45 153 : return GeoJSONObject::eMultiPoint;
46 1355 : else if (EQUAL(name, "MultiLineString"))
47 52 : return GeoJSONObject::eMultiLineString;
48 1303 : else if (EQUAL(name, "MultiPolygon"))
49 436 : return GeoJSONObject::eMultiPolygon;
50 867 : else if (EQUAL(name, "GeometryCollection"))
51 14 : return GeoJSONObject::eGeometryCollection;
52 853 : else if (EQUAL(name, "Feature"))
53 567 : return GeoJSONObject::eFeature;
54 286 : else if (EQUAL(name, "FeatureCollection"))
55 286 : return GeoJSONObject::eFeatureCollection;
56 : else
57 0 : return GeoJSONObject::eUnknown;
58 : }
59 :
60 : /************************************************************************/
61 : /* OGRGeoJSONGetOGRGeometryType() */
62 : /************************************************************************/
63 :
64 4627 : OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj)
65 : {
66 4627 : if (nullptr == poObj)
67 1 : return wkbUnknown;
68 :
69 4626 : json_object *poObjType = CPL_json_object_object_get(poObj, "type");
70 4626 : if (nullptr == poObjType)
71 0 : return wkbUnknown;
72 :
73 4626 : OGRwkbGeometryType eType = wkbUnknown;
74 4626 : const char *name = json_object_get_string(poObjType);
75 4626 : if (EQUAL(name, "Point"))
76 337 : eType = wkbPoint;
77 4289 : else if (EQUAL(name, "LineString"))
78 53 : eType = wkbLineString;
79 4236 : else if (EQUAL(name, "Polygon"))
80 3936 : eType = wkbPolygon;
81 300 : else if (EQUAL(name, "MultiPoint"))
82 25 : eType = wkbMultiPoint;
83 275 : else if (EQUAL(name, "MultiLineString"))
84 28 : eType = wkbMultiLineString;
85 247 : else if (EQUAL(name, "MultiPolygon"))
86 223 : eType = wkbMultiPolygon;
87 24 : else if (EQUAL(name, "GeometryCollection"))
88 19 : eType = wkbGeometryCollection;
89 : else
90 5 : return wkbUnknown;
91 :
92 : json_object *poCoordinates;
93 4621 : if (eType == wkbGeometryCollection)
94 : {
95 : json_object *poGeometries =
96 19 : CPL_json_object_object_get(poObj, "geometries");
97 37 : if (poGeometries &&
98 37 : json_object_get_type(poGeometries) == json_type_array &&
99 18 : 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 4602 : poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
109 9198 : if (poCoordinates &&
110 9198 : json_object_get_type(poCoordinates) == json_type_array &&
111 4596 : json_object_array_length(poCoordinates) > 0)
112 : {
113 : while (true)
114 : {
115 13226 : auto poChild = json_object_array_get_idx(poCoordinates, 0);
116 26442 : if (!(poChild &&
117 13216 : json_object_get_type(poChild) == json_type_array &&
118 8639 : json_object_array_length(poChild) > 0))
119 : {
120 4591 : if (json_object_array_length(poCoordinates) == 3)
121 111 : eType = OGR_GT_SetZ(eType);
122 4591 : break;
123 : }
124 8635 : poCoordinates = poChild;
125 8635 : }
126 : }
127 : }
128 :
129 4621 : return eType;
130 : }
131 :
132 : /************************************************************************/
133 : /* OGRGeoJSONReadGeometry */
134 : /************************************************************************/
135 :
136 1938 : OGRGeometry *OGRGeoJSONReadGeometry(json_object *poObj,
137 : OGRSpatialReference *poParentSRS)
138 : {
139 :
140 1938 : OGRGeometry *poGeometry = nullptr;
141 1938 : OGRSpatialReference *poSRS = nullptr;
142 1938 : lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, "crs");
143 1938 : 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 1938 : OGRSpatialReference *poSRSToAssign = nullptr;
154 1938 : if (entry != nullptr)
155 : {
156 4 : poSRSToAssign = poSRS;
157 : }
158 1934 : else if (poParentSRS)
159 : {
160 1319 : poSRSToAssign = poParentSRS;
161 : }
162 : else
163 : {
164 : // Assign WGS84 if no CRS defined on geometry.
165 615 : poSRSToAssign = OGRSpatialReference::GetWGS84SRS();
166 : }
167 :
168 1938 : GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
169 1938 : if (GeoJSONObject::ePoint == objType)
170 353 : poGeometry = OGRGeoJSONReadPoint(poObj);
171 1585 : else if (GeoJSONObject::eMultiPoint == objType)
172 63 : poGeometry = OGRGeoJSONReadMultiPoint(poObj);
173 1522 : else if (GeoJSONObject::eLineString == objType)
174 53 : poGeometry = OGRGeoJSONReadLineString(poObj);
175 1469 : else if (GeoJSONObject::eMultiLineString == objType)
176 38 : poGeometry = OGRGeoJSONReadMultiLineString(poObj);
177 1431 : else if (GeoJSONObject::ePolygon == objType)
178 1009 : poGeometry = OGRGeoJSONReadPolygon(poObj);
179 422 : else if (GeoJSONObject::eMultiPolygon == objType)
180 412 : poGeometry = OGRGeoJSONReadMultiPolygon(poObj);
181 10 : else if (GeoJSONObject::eGeometryCollection == objType)
182 10 : 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 1938 : if (poGeometry && GeoJSONObject::eGeometryCollection != objType)
191 1883 : poGeometry->assignSpatialReference(poSRSToAssign);
192 :
193 1938 : if (poSRS)
194 3 : poSRS->Release();
195 :
196 1938 : 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 180319 : static double OGRGeoJSONGetCoordinate(json_object *poObj,
230 : const char *pszCoordName, int nIndex,
231 : bool &bValid)
232 : {
233 180319 : json_object *poObjCoord = json_object_array_get_idx(poObj, nIndex);
234 180319 : 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 180313 : const json_type eType = json_object_get_type(poObjCoord);
242 180313 : 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 180307 : return json_object_get_double(poObjCoord);
254 : }
255 :
256 : /************************************************************************/
257 : /* OGRGeoJSONReadRawPoint */
258 : /************************************************************************/
259 :
260 89988 : static bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point)
261 : {
262 89988 : if (json_type_array == json_object_get_type(poObj))
263 : {
264 89976 : const int nSize = static_cast<int>(json_object_array_length(poObj));
265 :
266 89976 : 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 89973 : bool bValid = true;
277 89973 : const double dfX = OGRGeoJSONGetCoordinate(poObj, "x", 0, bValid);
278 89973 : const double dfY = OGRGeoJSONGetCoordinate(poObj, "y", 1, bValid);
279 89973 : point.setX(dfX);
280 89973 : point.setY(dfY);
281 :
282 : // Read Z coordinate.
283 89973 : 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 89600 : point.flattenTo2D();
302 : }
303 89973 : 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 353 : OGRPoint *OGRGeoJSONReadPoint(json_object *poObj)
322 : {
323 353 : if (!poObj)
324 : {
325 0 : CPLError(CE_Failure, CPLE_AppDefined,
326 : "OGRGeoJSONReadPoint(): invalid Point object. Got null.");
327 0 : return nullptr;
328 : }
329 353 : json_object *poObjCoords = OGRGeoJSONFindMemberByName(poObj, "coordinates");
330 353 : 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 698 : auto poPoint = std::make_unique<OGRPoint>();
339 349 : if (!OGRGeoJSONReadRawPoint(poObjCoords, *poPoint))
340 : {
341 10 : return nullptr;
342 : }
343 :
344 339 : return poPoint.release();
345 : }
346 :
347 : /************************************************************************/
348 : /* OGRGeoJSONReadMultiPoint */
349 : /************************************************************************/
350 :
351 63 : OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj)
352 : {
353 63 : if (!poObj)
354 : {
355 0 : CPLError(
356 : CE_Failure, CPLE_AppDefined,
357 : "OGRGeoJSONReadMultiPoint(): invalid MultiPoint object. Got null.");
358 0 : return nullptr;
359 : }
360 63 : json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
361 63 : 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 60 : std::unique_ptr<OGRMultiPoint> poMultiPoint;
370 60 : if (json_type_array == json_object_get_type(poObjPoints))
371 : {
372 59 : const auto nPoints = json_object_array_length(poObjPoints);
373 :
374 59 : poMultiPoint = std::make_unique<OGRMultiPoint>();
375 :
376 257 : for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
377 : {
378 : json_object *poObjCoords =
379 202 : json_object_array_get_idx(poObjPoints, i);
380 :
381 202 : OGRPoint pt;
382 202 : if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
383 : {
384 4 : return nullptr;
385 : }
386 198 : 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 56 : return poMultiPoint.release();
399 : }
400 :
401 : /************************************************************************/
402 : /* OGRGeoJSONReadLineString */
403 : /************************************************************************/
404 :
405 111 : OGRLineString *OGRGeoJSONReadLineString(json_object *poObj, bool bRaw)
406 : {
407 111 : if (!poObj)
408 : {
409 2 : CPLError(
410 : CE_Failure, CPLE_AppDefined,
411 : "OGRGeoJSONReadLineString(): invalid LineString object. Got null.");
412 2 : return nullptr;
413 : }
414 109 : json_object *poObjPoints = nullptr;
415 :
416 109 : if (!bRaw)
417 : {
418 53 : poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
419 53 : 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 56 : poObjPoints = poObj;
430 : }
431 :
432 106 : std::unique_ptr<OGRLineString> poLine;
433 :
434 106 : if (json_type_array == json_object_get_type(poObjPoints))
435 : {
436 104 : const auto nPoints = json_object_array_length(poObjPoints);
437 :
438 104 : poLine = std::make_unique<OGRLineString>();
439 104 : poLine->setNumPoints(static_cast<int>(nPoints));
440 :
441 299 : for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
442 : {
443 : json_object *poObjCoords =
444 203 : json_object_array_get_idx(poObjPoints, i);
445 :
446 203 : OGRPoint pt;
447 203 : if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
448 : {
449 8 : return nullptr;
450 : }
451 195 : if (pt.getCoordinateDimension() == 2)
452 : {
453 169 : 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 98 : return poLine.release();
472 : }
473 :
474 : /************************************************************************/
475 : /* OGRGeoJSONReadMultiLineString */
476 : /************************************************************************/
477 :
478 38 : OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj)
479 : {
480 38 : CPLAssert(nullptr != poObj);
481 :
482 38 : json_object *poObjLines = OGRGeoJSONFindMemberByName(poObj, "coordinates");
483 38 : 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 35 : std::unique_ptr<OGRMultiLineString> poMultiLine;
492 :
493 35 : if (json_type_array == json_object_get_type(poObjLines))
494 : {
495 34 : const auto nLines = json_object_array_length(poObjLines);
496 :
497 34 : poMultiLine = std::make_unique<OGRMultiLineString>();
498 :
499 92 : for (auto i = decltype(nLines){0}; i < nLines; ++i)
500 : {
501 58 : json_object *poObjLine = json_object_array_get_idx(poObjLines, i);
502 :
503 58 : OGRLineString *poLine = OGRGeoJSONReadLineString(poObjLine, true);
504 58 : if (poLine)
505 : {
506 51 : 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 35 : return poMultiLine.release();
520 : }
521 :
522 : /************************************************************************/
523 : /* OGRGeoJSONReadLinearRing */
524 : /************************************************************************/
525 :
526 1695 : OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj)
527 : {
528 1695 : CPLAssert(nullptr != poObj);
529 :
530 1695 : std::unique_ptr<OGRLinearRing> poRing;
531 :
532 1695 : if (json_type_array == json_object_get_type(poObj))
533 : {
534 1693 : const auto nPoints = json_object_array_length(poObj);
535 :
536 1693 : poRing = std::make_unique<OGRLinearRing>();
537 1693 : poRing->setNumPoints(static_cast<int>(nPoints));
538 :
539 90923 : for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
540 : {
541 89234 : json_object *poObjCoords = json_object_array_get_idx(poObj, i);
542 :
543 89234 : OGRPoint pt;
544 89234 : if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
545 : {
546 4 : return nullptr;
547 : }
548 :
549 89230 : if (2 == pt.getCoordinateDimension())
550 89089 : 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 1691 : return poRing.release();
566 : }
567 :
568 : /************************************************************************/
569 : /* OGRGeoJSONReadPolygon */
570 : /************************************************************************/
571 :
572 1666 : OGRPolygon *OGRGeoJSONReadPolygon(json_object *poObj, bool bRaw)
573 : {
574 1666 : if (!poObj)
575 : {
576 0 : CPLError(CE_Failure, CPLE_AppDefined,
577 : "OGRGeoJSONReadPolygon(): invalid Polygon object. Got null.");
578 0 : return nullptr;
579 : }
580 1666 : json_object *poObjRings = nullptr;
581 :
582 1666 : if (!bRaw)
583 : {
584 1009 : poObjRings = OGRGeoJSONFindMemberByName(poObj, "coordinates");
585 1009 : 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 657 : poObjRings = poObj;
596 : }
597 :
598 1663 : std::unique_ptr<OGRPolygon> poPolygon;
599 :
600 1663 : if (json_type_array == json_object_get_type(poObjRings))
601 : {
602 1661 : const auto nRings = json_object_array_length(poObjRings);
603 1661 : if (nRings > 0)
604 : {
605 1657 : json_object *poObjPoints = json_object_array_get_idx(poObjRings, 0);
606 1657 : if (!poObjPoints)
607 : {
608 2 : poPolygon = std::make_unique<OGRPolygon>();
609 : }
610 : else
611 : {
612 1655 : OGRLinearRing *poRing = OGRGeoJSONReadLinearRing(poObjPoints);
613 1655 : if (poRing)
614 : {
615 1649 : poPolygon = std::make_unique<OGRPolygon>();
616 1649 : poPolygon->addRingDirectly(poRing);
617 : }
618 : }
619 :
620 1698 : for (auto i = decltype(nRings){1};
621 1698 : 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 1663 : return poPolygon.release();
650 : }
651 :
652 : /************************************************************************/
653 : /* OGRGeoJSONReadMultiPolygon */
654 : /************************************************************************/
655 :
656 412 : OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj)
657 : {
658 412 : CPLAssert(nullptr != poObj);
659 :
660 412 : json_object *poObjPolys = OGRGeoJSONFindMemberByName(poObj, "coordinates");
661 412 : 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 409 : std::unique_ptr<OGRMultiPolygon> poMultiPoly;
670 :
671 409 : if (json_type_array == json_object_get_type(poObjPolys))
672 : {
673 408 : const auto nPolys = json_object_array_length(poObjPolys);
674 :
675 408 : poMultiPoly = std::make_unique<OGRMultiPolygon>();
676 :
677 1064 : for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
678 : {
679 656 : json_object *poObjPoly = json_object_array_get_idx(poObjPolys, i);
680 656 : if (!poObjPoly)
681 : {
682 6 : poMultiPoly->addGeometryDirectly(
683 6 : std::make_unique<OGRPolygon>().release());
684 : }
685 : else
686 : {
687 653 : OGRPolygon *poPoly = OGRGeoJSONReadPolygon(poObjPoly, true);
688 653 : if (poPoly)
689 : {
690 649 : 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 409 : return poMultiPoly.release();
705 : }
706 :
707 : /************************************************************************/
708 : /* OGRGeoJSONReadGeometryCollection */
709 : /************************************************************************/
710 :
711 : OGRGeometryCollection *
712 10 : OGRGeoJSONReadGeometryCollection(json_object *poObj, OGRSpatialReference *poSRS)
713 : {
714 10 : CPLAssert(nullptr != poObj);
715 :
716 10 : json_object *poObjGeoms = OGRGeoJSONFindMemberByName(poObj, "geometries");
717 10 : if (nullptr == poObjGeoms)
718 : {
719 1 : CPLError(CE_Failure, CPLE_AppDefined,
720 : "Invalid GeometryCollection object. "
721 : "Missing \'geometries\' member.");
722 1 : return nullptr;
723 : }
724 :
725 9 : std::unique_ptr<OGRGeometryCollection> poCollection;
726 :
727 9 : if (json_type_array == json_object_get_type(poObjGeoms))
728 : {
729 9 : poCollection = std::make_unique<OGRGeometryCollection>();
730 9 : poCollection->assignSpatialReference(poSRS);
731 :
732 9 : const auto nGeoms = json_object_array_length(poObjGeoms);
733 25 : for (auto i = decltype(nGeoms){0}; i < nGeoms; ++i)
734 : {
735 16 : json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
736 16 : if (!poObjGeom)
737 : {
738 3 : CPLError(CE_Warning, CPLE_AppDefined,
739 : "OGRGeoJSONReadGeometryCollection(): skipping null "
740 : "sub-geometry");
741 3 : continue;
742 : }
743 :
744 13 : OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObjGeom, poSRS);
745 13 : if (poGeometry)
746 : {
747 13 : poCollection->addGeometryDirectly(poGeometry);
748 : }
749 : }
750 : }
751 : else
752 : {
753 0 : CPLError(CE_Warning, CPLE_AppDefined,
754 : "OGRGeoJSONReadGeometryCollection(): unexpected type of JSON "
755 : "construct %s for '%s'. Expected array.",
756 : GetJSONConstructName(json_object_get_type(poObjGeoms)),
757 : json_object_to_json_string(poObjGeoms));
758 : }
759 :
760 9 : return poCollection.release();
761 : }
762 :
763 : /************************************************************************/
764 : /* OGRGeoJSONGetGeometryName() */
765 : /************************************************************************/
766 :
767 1666 : const char *OGRGeoJSONGetGeometryName(OGRGeometry const *poGeometry)
768 : {
769 1666 : CPLAssert(nullptr != poGeometry);
770 :
771 1666 : const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
772 :
773 1666 : if (wkbPoint == eType)
774 161 : return "Point";
775 1505 : else if (wkbLineString == eType)
776 56 : return "LineString";
777 1449 : else if (wkbPolygon == eType)
778 1282 : return "Polygon";
779 167 : else if (wkbMultiPoint == eType)
780 64 : return "MultiPoint";
781 103 : else if (wkbMultiLineString == eType)
782 34 : return "MultiLineString";
783 69 : else if (wkbMultiPolygon == eType)
784 43 : return "MultiPolygon";
785 26 : else if (wkbGeometryCollection == eType)
786 25 : return "GeometryCollection";
787 :
788 1 : return "Unknown";
789 : }
790 :
791 : /************************************************************************/
792 : /* OGRGeoJSONReadSpatialReference */
793 : /************************************************************************/
794 :
795 495 : OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj)
796 : {
797 :
798 : /* -------------------------------------------------------------------- */
799 : /* Read spatial reference definition. */
800 : /* -------------------------------------------------------------------- */
801 495 : OGRSpatialReference *poSRS = nullptr;
802 :
803 495 : json_object *poObjSrs = OGRGeoJSONFindMemberByName(poObj, "crs");
804 495 : if (nullptr != poObjSrs)
805 : {
806 : json_object *poObjSrsType =
807 70 : OGRGeoJSONFindMemberByName(poObjSrs, "type");
808 70 : if (poObjSrsType == nullptr)
809 1 : return nullptr;
810 :
811 69 : const char *pszSrsType = json_object_get_string(poObjSrsType);
812 :
813 : // TODO: Add URL and URN types support.
814 69 : if (STARTS_WITH_CI(pszSrsType, "NAME"))
815 : {
816 : json_object *poObjSrsProps =
817 49 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
818 49 : if (poObjSrsProps == nullptr)
819 2 : return nullptr;
820 :
821 : json_object *poNameURL =
822 47 : OGRGeoJSONFindMemberByName(poObjSrsProps, "name");
823 47 : if (poNameURL == nullptr)
824 2 : return nullptr;
825 :
826 45 : 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 45 : if (EQUAL(pszName, "urn:ogc:def:crs:OGC:1.3:CRS84"))
831 9 : pszName = "EPSG:4326";
832 :
833 45 : poSRS = new OGRSpatialReference();
834 45 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
835 45 : if (OGRERR_NONE !=
836 45 : poSRS->SetFromUserInput(
837 : pszName,
838 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()))
839 : {
840 2 : delete poSRS;
841 2 : poSRS = nullptr;
842 : }
843 : }
844 :
845 20 : else if (STARTS_WITH_CI(pszSrsType, "EPSG"))
846 : {
847 : json_object *poObjSrsProps =
848 7 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
849 7 : if (poObjSrsProps == nullptr)
850 2 : return nullptr;
851 :
852 : json_object *poObjCode =
853 5 : OGRGeoJSONFindMemberByName(poObjSrsProps, "code");
854 5 : if (poObjCode == nullptr)
855 2 : return nullptr;
856 :
857 3 : int nEPSG = json_object_get_int(poObjCode);
858 :
859 3 : poSRS = new OGRSpatialReference();
860 3 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
861 3 : if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
862 : {
863 2 : delete poSRS;
864 2 : poSRS = nullptr;
865 : }
866 : }
867 :
868 13 : else if (STARTS_WITH_CI(pszSrsType, "URL") ||
869 13 : STARTS_WITH_CI(pszSrsType, "LINK"))
870 : {
871 : json_object *poObjSrsProps =
872 6 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
873 6 : if (poObjSrsProps == nullptr)
874 2 : return nullptr;
875 :
876 : json_object *poObjURL =
877 4 : OGRGeoJSONFindMemberByName(poObjSrsProps, "url");
878 :
879 4 : if (nullptr == poObjURL)
880 : {
881 4 : poObjURL = OGRGeoJSONFindMemberByName(poObjSrsProps, "href");
882 : }
883 4 : if (poObjURL == nullptr)
884 2 : return nullptr;
885 :
886 2 : const char *pszURL = json_object_get_string(poObjURL);
887 :
888 2 : poSRS = new OGRSpatialReference();
889 2 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
890 2 : if (OGRERR_NONE != poSRS->importFromUrl(pszURL))
891 : {
892 2 : delete poSRS;
893 2 : poSRS = nullptr;
894 2 : }
895 : }
896 :
897 7 : else if (EQUAL(pszSrsType, "OGC"))
898 : {
899 : json_object *poObjSrsProps =
900 7 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
901 7 : if (poObjSrsProps == nullptr)
902 2 : return nullptr;
903 :
904 : json_object *poObjURN =
905 5 : OGRGeoJSONFindMemberByName(poObjSrsProps, "urn");
906 5 : if (poObjURN == nullptr)
907 2 : return nullptr;
908 :
909 3 : poSRS = new OGRSpatialReference();
910 3 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
911 3 : if (OGRERR_NONE !=
912 3 : poSRS->importFromURN(json_object_get_string(poObjURN)))
913 : {
914 2 : delete poSRS;
915 2 : poSRS = nullptr;
916 : }
917 : }
918 : }
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 478 : if (poSRS != nullptr)
925 : {
926 45 : OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode("GEOGCS");
927 45 : if (poGEOGCS != nullptr)
928 45 : poGEOGCS->StripNodes("AXIS");
929 : }
930 :
931 478 : return poSRS;
932 : }
933 :
934 : /************************************************************************/
935 : /* OGR_G_CreateGeometryFromJson */
936 : /************************************************************************/
937 :
938 : /** Create a OGR geometry from a GeoJSON geometry object */
939 46 : OGRGeometryH OGR_G_CreateGeometryFromJson(const char *pszJson)
940 : {
941 46 : if (nullptr == pszJson)
942 : {
943 : // Translation failed.
944 0 : return nullptr;
945 : }
946 :
947 46 : json_object *poObj = nullptr;
948 46 : if (!OGRJSonParse(pszJson, &poObj))
949 0 : return nullptr;
950 :
951 46 : OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj);
952 :
953 : // Release JSON tree.
954 46 : json_object_put(poObj);
955 :
956 46 : return OGRGeometry::ToHandle(poGeometry);
957 : }
958 :
959 : /*! @endcond */
|