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