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 std::unique_ptr<OGRPoint> OGRGeoJSONReadPoint(json_object *poObj,
14 : bool bHasM);
15 : static std::unique_ptr<OGRMultiPoint>
16 : OGRGeoJSONReadMultiPoint(json_object *poObj, bool bHasM);
17 : static std::unique_ptr<OGRLineString>
18 : OGRGeoJSONReadLineString(json_object *poObj, bool bHasM, bool bRaw);
19 : static std::unique_ptr<OGRMultiLineString>
20 : OGRGeoJSONReadMultiLineString(json_object *poObj, bool bHasM);
21 : static std::unique_ptr<OGRLinearRing>
22 : OGRGeoJSONReadLinearRing(json_object *poObj, bool bHasM);
23 : static std::unique_ptr<OGRMultiPolygon>
24 : OGRGeoJSONReadMultiPolygon(json_object *poObj, bool bHasM);
25 : static std::unique_ptr<OGRGeometryCollection>
26 : OGRGeoJSONReadGeometryCollection(json_object *poObj, bool bHasM,
27 : const OGRSpatialReference *poSRS);
28 : static std::unique_ptr<OGRCircularString>
29 : OGRGeoJSONReadCircularString(json_object *poObj, bool bHasM);
30 : static std::unique_ptr<OGRCompoundCurve>
31 : OGRGeoJSONReadCompoundCurve(json_object *poObj, bool bHasM,
32 : const OGRSpatialReference *poSRS);
33 : static std::unique_ptr<OGRCurvePolygon>
34 : OGRGeoJSONReadCurvePolygon(json_object *poObj, bool bHasM);
35 : static std::unique_ptr<OGRMultiCurve>
36 : OGRGeoJSONReadMultiCurve(json_object *poObj, bool bHasM,
37 : const OGRSpatialReference *poSRS);
38 : static std::unique_ptr<OGRMultiSurface>
39 : OGRGeoJSONReadMultiSurface(json_object *poObj, bool bHasM,
40 : const OGRSpatialReference *poSRS);
41 :
42 : /************************************************************************/
43 : /* OGRGeoJSONGetType */
44 : /************************************************************************/
45 :
46 3371 : GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj)
47 : {
48 3371 : if (nullptr == poObj)
49 0 : return GeoJSONObject::eUnknown;
50 :
51 3371 : json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
52 3371 : if (nullptr == poObjType)
53 2 : return GeoJSONObject::eUnknown;
54 :
55 3369 : const char *name = json_object_get_string(poObjType);
56 :
57 : #define ASSOC(x) \
58 : { \
59 : #x, GeoJSONObject::e##x \
60 : }
61 :
62 : static const struct
63 : {
64 : const char *pszName;
65 : GeoJSONObject::Type eType;
66 : } tabAssoc[] = {
67 : ASSOC(Point),
68 : ASSOC(LineString),
69 : ASSOC(Polygon),
70 : ASSOC(MultiPoint),
71 : ASSOC(MultiLineString),
72 : ASSOC(MultiPolygon),
73 : ASSOC(GeometryCollection),
74 : ASSOC(CircularString),
75 : ASSOC(CompoundCurve),
76 : ASSOC(CurvePolygon),
77 : ASSOC(MultiCurve),
78 : ASSOC(MultiSurface),
79 : ASSOC(Feature),
80 : ASSOC(FeatureCollection),
81 : };
82 :
83 : #undef ASSOC
84 :
85 21734 : for (const auto &assoc : tabAssoc)
86 : {
87 21734 : if (EQUAL(name, assoc.pszName))
88 3369 : return assoc.eType;
89 : }
90 :
91 0 : return GeoJSONObject::eUnknown;
92 : }
93 :
94 : /************************************************************************/
95 : /* OGRJSONFGHasMeasure() */
96 : /************************************************************************/
97 :
98 8539 : bool OGRJSONFGHasMeasure(json_object *poObj, bool bUpperLevelMValue)
99 : {
100 8539 : bool bHasM = bUpperLevelMValue;
101 8539 : if (json_object *pojMeasures =
102 8539 : CPL_json_object_object_get(poObj, "measures"))
103 : {
104 : json_object *poEnabled =
105 63 : CPL_json_object_object_get(pojMeasures, "enabled");
106 63 : bHasM = json_object_get_boolean(poEnabled);
107 : }
108 8539 : return bHasM;
109 : }
110 :
111 : /************************************************************************/
112 : /* asAssocGeometryTypes[] */
113 : /************************************************************************/
114 :
115 : #define ASSOC(x) {#x, wkb##x}
116 :
117 : static const struct
118 : {
119 : const char *pszName;
120 : OGRwkbGeometryType eType;
121 : } asAssocGeometryTypes[] = {
122 : ASSOC(Point),
123 : ASSOC(LineString),
124 : ASSOC(Polygon),
125 : ASSOC(MultiPoint),
126 : ASSOC(MultiLineString),
127 : ASSOC(MultiPolygon),
128 : ASSOC(GeometryCollection),
129 : ASSOC(CircularString),
130 : ASSOC(CompoundCurve),
131 : ASSOC(CurvePolygon),
132 : ASSOC(MultiCurve),
133 : ASSOC(MultiSurface),
134 : };
135 :
136 : #undef ASSOC
137 :
138 : /************************************************************************/
139 : /* OGRGeoJSONGetOGRGeometryType() */
140 : /************************************************************************/
141 :
142 4758 : OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj, bool bHasM)
143 : {
144 4758 : if (nullptr == poObj)
145 1 : return wkbUnknown;
146 :
147 4757 : json_object *poObjType = CPL_json_object_object_get(poObj, "type");
148 4757 : if (nullptr == poObjType)
149 0 : return wkbUnknown;
150 :
151 4757 : const char *name = json_object_get_string(poObjType);
152 :
153 4757 : OGRwkbGeometryType eType = wkbNone;
154 14939 : for (const auto &assoc : asAssocGeometryTypes)
155 : {
156 14933 : if (EQUAL(name, assoc.pszName))
157 : {
158 4751 : eType = assoc.eType;
159 4751 : break;
160 : }
161 : }
162 4757 : if (eType == wkbNone)
163 6 : return wkbUnknown;
164 :
165 4751 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
166 :
167 : json_object *poCoordinates;
168 4751 : if (eType == wkbGeometryCollection || eType == wkbMultiCurve ||
169 4713 : eType == wkbMultiSurface || eType == wkbCompoundCurve ||
170 : eType == wkbCurvePolygon)
171 : {
172 : json_object *poGeometries =
173 68 : CPL_json_object_object_get(poObj, "geometries");
174 135 : if (poGeometries &&
175 135 : json_object_get_type(poGeometries) == json_type_array &&
176 67 : json_object_array_length(poGeometries) > 0)
177 : {
178 65 : const auto subGeomType = OGRGeoJSONGetOGRGeometryType(
179 : json_object_array_get_idx(poGeometries, 0), bHasM);
180 65 : if (OGR_GT_HasZ(subGeomType))
181 29 : eType = OGR_GT_SetZ(eType);
182 68 : }
183 : }
184 : else
185 : {
186 4683 : poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
187 9360 : if (poCoordinates &&
188 9360 : json_object_get_type(poCoordinates) == json_type_array &&
189 4677 : json_object_array_length(poCoordinates) > 0)
190 : {
191 : while (true)
192 : {
193 13391 : auto poChild = json_object_array_get_idx(poCoordinates, 0);
194 26772 : if (!(poChild &&
195 13381 : json_object_get_type(poChild) == json_type_array &&
196 8723 : json_object_array_length(poChild) > 0))
197 : {
198 : const auto nLength =
199 4672 : json_object_array_length(poCoordinates);
200 4672 : if ((bHasM && nLength == 4) || (!bHasM && nLength == 3))
201 139 : eType = OGR_GT_SetZ(eType);
202 4672 : break;
203 : }
204 8719 : poCoordinates = poChild;
205 8719 : }
206 : }
207 : }
208 4751 : if (bHasM)
209 48 : eType = OGR_GT_SetM(eType);
210 :
211 4751 : return eType;
212 : }
213 :
214 : /************************************************************************/
215 : /* OGRGeoJSONGetGeometryName() */
216 : /************************************************************************/
217 :
218 1856 : const char *OGRGeoJSONGetGeometryName(OGRGeometry const *poGeometry)
219 : {
220 1856 : CPLAssert(nullptr != poGeometry);
221 :
222 1856 : const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
223 :
224 5817 : for (const auto &assoc : asAssocGeometryTypes)
225 : {
226 5816 : if (eType == assoc.eType)
227 : {
228 1855 : return assoc.pszName;
229 : }
230 : }
231 1 : return "Unknown";
232 : }
233 :
234 : /************************************************************************/
235 : /* OGRGeoJSONReadGeometry */
236 : /************************************************************************/
237 :
238 : std::unique_ptr<OGRGeometry>
239 2171 : OGRGeoJSONReadGeometry(json_object *poObj, bool bHasM,
240 : const OGRSpatialReference *poParentSRS)
241 : {
242 :
243 2171 : std::unique_ptr<OGRGeometry> poGeometry;
244 2171 : OGRSpatialReference *poSRS = nullptr;
245 2171 : lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, "crs");
246 2171 : if (entry != nullptr)
247 : {
248 4 : json_object *poObjSrs =
249 : static_cast<json_object *>(const_cast<void *>(entry->v));
250 4 : if (poObjSrs != nullptr)
251 : {
252 3 : poSRS = OGRGeoJSONReadSpatialReference(poObj);
253 : }
254 : }
255 :
256 2171 : const OGRSpatialReference *poSRSToAssign = nullptr;
257 2171 : if (entry != nullptr)
258 : {
259 4 : poSRSToAssign = poSRS;
260 : }
261 2167 : else if (poParentSRS)
262 : {
263 1433 : poSRSToAssign = poParentSRS;
264 : }
265 : else
266 : {
267 : // Assign WGS84 if no CRS defined on geometry.
268 734 : poSRSToAssign = OGRSpatialReference::GetWGS84SRS();
269 : }
270 :
271 2171 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
272 :
273 2171 : const auto objType = OGRGeoJSONGetType(poObj);
274 2171 : switch (objType)
275 : {
276 373 : case GeoJSONObject::ePoint:
277 373 : poGeometry = OGRGeoJSONReadPoint(poObj, bHasM);
278 373 : break;
279 :
280 119 : case GeoJSONObject::eLineString:
281 238 : poGeometry = OGRGeoJSONReadLineString(poObj, bHasM,
282 119 : /* bRaw = */ false);
283 119 : break;
284 :
285 1037 : case GeoJSONObject::ePolygon:
286 : poGeometry =
287 1037 : OGRGeoJSONReadPolygon(poObj, bHasM, /* bRaw = */ false);
288 1037 : break;
289 :
290 64 : case GeoJSONObject::eMultiPoint:
291 64 : poGeometry = OGRGeoJSONReadMultiPoint(poObj, bHasM);
292 64 : break;
293 :
294 39 : case GeoJSONObject::eMultiLineString:
295 39 : poGeometry = OGRGeoJSONReadMultiLineString(poObj, bHasM);
296 39 : break;
297 :
298 415 : case GeoJSONObject::eMultiPolygon:
299 415 : poGeometry = OGRGeoJSONReadMultiPolygon(poObj, bHasM);
300 415 : break;
301 :
302 11 : case GeoJSONObject::eGeometryCollection:
303 : poGeometry =
304 11 : OGRGeoJSONReadGeometryCollection(poObj, bHasM, poSRSToAssign);
305 11 : break;
306 :
307 47 : case GeoJSONObject::eCircularString:
308 47 : poGeometry = OGRGeoJSONReadCircularString(poObj, bHasM);
309 47 : break;
310 :
311 28 : case GeoJSONObject::eCompoundCurve:
312 : poGeometry =
313 28 : OGRGeoJSONReadCompoundCurve(poObj, bHasM, poSRSToAssign);
314 28 : break;
315 :
316 20 : case GeoJSONObject::eCurvePolygon:
317 20 : poGeometry = OGRGeoJSONReadCurvePolygon(poObj, bHasM);
318 20 : break;
319 :
320 9 : case GeoJSONObject::eMultiCurve:
321 9 : poGeometry = OGRGeoJSONReadMultiCurve(poObj, bHasM, poSRSToAssign);
322 9 : break;
323 :
324 9 : case GeoJSONObject::eMultiSurface:
325 : poGeometry =
326 9 : OGRGeoJSONReadMultiSurface(poObj, bHasM, poSRSToAssign);
327 9 : break;
328 :
329 0 : case GeoJSONObject::eFeature:
330 : case GeoJSONObject::eFeatureCollection:
331 : [[fallthrough]];
332 : case GeoJSONObject::eUnknown:
333 0 : CPLError(CE_Warning, CPLE_AppDefined,
334 : "Unsupported geometry type detected. "
335 : "Feature gets NULL geometry assigned.");
336 0 : break;
337 : }
338 :
339 2171 : if (poGeometry && GeoJSONObject::eGeometryCollection != objType)
340 2111 : poGeometry->assignSpatialReference(poSRSToAssign);
341 :
342 2171 : if (poSRS)
343 3 : poSRS->Release();
344 :
345 2171 : return poGeometry;
346 : }
347 :
348 : /************************************************************************/
349 : /* GetJSONConstructName() */
350 : /************************************************************************/
351 :
352 27 : static const char *GetJSONConstructName(json_type eType)
353 : {
354 27 : switch (eType)
355 : {
356 6 : case json_type_null:
357 6 : break;
358 0 : case json_type_boolean:
359 0 : return "boolean";
360 0 : case json_type_double:
361 0 : return "double";
362 0 : case json_type_int:
363 0 : return "int";
364 0 : case json_type_object:
365 0 : return "object";
366 0 : case json_type_array:
367 0 : return "array";
368 21 : case json_type_string:
369 21 : return "string";
370 : }
371 6 : return "null";
372 : }
373 :
374 : /************************************************************************/
375 : /* OGRGeoJSONGetCoordinate() */
376 : /************************************************************************/
377 :
378 185275 : static double OGRGeoJSONGetCoordinate(json_object *poObj,
379 : const char *pszCoordName, int nIndex,
380 : bool &bValid)
381 : {
382 185275 : json_object *poObjCoord = json_object_array_get_idx(poObj, nIndex);
383 185275 : if (nullptr == poObjCoord)
384 : {
385 6 : CPLDebug("GeoJSON", "Point: got null object for %s.", pszCoordName);
386 6 : bValid = false;
387 6 : return 0.0;
388 : }
389 :
390 185269 : const json_type eType = json_object_get_type(poObjCoord);
391 185269 : if (json_type_double != eType && json_type_int != eType)
392 : {
393 6 : CPLError(CE_Failure, CPLE_AppDefined,
394 : "OGRGeoJSONGetCoordinate(): invalid '%s' coordinate. "
395 : "Unexpected type %s for '%s'. Expected double or integer.",
396 : pszCoordName, GetJSONConstructName(eType),
397 : json_object_to_json_string(poObjCoord));
398 6 : bValid = false;
399 6 : return 0.0;
400 : }
401 :
402 185263 : return json_object_get_double(poObjCoord);
403 : }
404 :
405 : /************************************************************************/
406 : /* OGRGeoJSONReadRawPoint */
407 : /************************************************************************/
408 :
409 92259 : static bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point,
410 : bool bHasM)
411 : {
412 92259 : if (json_type_array == json_object_get_type(poObj))
413 : {
414 92247 : const int nSize = static_cast<int>(json_object_array_length(poObj));
415 :
416 92247 : if (nSize < GeoJSONObject::eMinCoordinateDimension)
417 : {
418 3 : CPLError(CE_Warning, CPLE_AppDefined,
419 : "OGRGeoJSONReadRawPoint(): "
420 : "Invalid coord dimension for '%s'. "
421 : "At least 2 dimensions must be present.",
422 : json_object_to_json_string(poObj));
423 3 : return false;
424 : }
425 :
426 92244 : bool bValid = true;
427 92244 : const double dfX = OGRGeoJSONGetCoordinate(poObj, "x", 0, bValid);
428 92244 : const double dfY = OGRGeoJSONGetCoordinate(poObj, "y", 1, bValid);
429 92244 : point.setX(dfX);
430 92244 : point.setY(dfY);
431 :
432 : // Read Z and/or M coordinate.
433 92244 : if (nSize > GeoJSONObject::eMinCoordinateDimension)
434 : {
435 670 : const int nMaxDim =
436 670 : bHasM ? GeoJSONObject::eMaxCoordinateDimensionJSONFG
437 : : GeoJSONObject::eMaxCoordinateDimensionGeoJSON;
438 670 : if (nSize > nMaxDim)
439 : {
440 6 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
441 : "OGRGeoJSONReadRawPoint(): too many members in "
442 : "array '%s': %d. At most %d are handled. Ignoring "
443 : "extra members.",
444 : json_object_to_json_string(poObj), nSize, nMaxDim);
445 : }
446 : // Don't *expect* mixed-dimension geometries, although the
447 : // spec doesn't explicitly forbid this.
448 670 : if (nSize == 4 || (nSize == 3 && !bHasM))
449 : {
450 : const double dfZ =
451 579 : OGRGeoJSONGetCoordinate(poObj, "z", 2, bValid);
452 579 : point.setZ(dfZ);
453 : }
454 :
455 670 : if (bHasM)
456 : {
457 : const double dfM =
458 208 : OGRGeoJSONGetCoordinate(poObj, "m", nSize - 1, bValid);
459 208 : point.setM(dfM);
460 : }
461 : }
462 : else
463 : {
464 91574 : point.flattenTo2D();
465 : }
466 92244 : return bValid;
467 : }
468 : else
469 : {
470 12 : CPLError(CE_Failure, CPLE_AppDefined,
471 : "OGRGeoJSONReadRawPoint(): invalid Point. "
472 : "Unexpected type %s for '%s'. Expected array.",
473 : GetJSONConstructName(json_object_get_type(poObj)),
474 : json_object_to_json_string(poObj));
475 : }
476 :
477 12 : return false;
478 : }
479 :
480 : /************************************************************************/
481 : /* OGRGeoJSONReadPoint */
482 : /************************************************************************/
483 :
484 373 : std::unique_ptr<OGRPoint> OGRGeoJSONReadPoint(json_object *poObj, bool bHasM)
485 : {
486 373 : if (!poObj)
487 : {
488 0 : CPLError(CE_Failure, CPLE_AppDefined,
489 : "OGRGeoJSONReadPoint(): invalid Point object. Got null.");
490 0 : return nullptr;
491 : }
492 373 : json_object *poObjCoords = OGRGeoJSONFindMemberByName(poObj, "coordinates");
493 373 : if (nullptr == poObjCoords)
494 : {
495 4 : CPLError(CE_Failure, CPLE_AppDefined,
496 : "OGRGeoJSONReadPoint(): invalid Point object. "
497 : "Missing \'coordinates\' member.");
498 4 : return nullptr;
499 : }
500 :
501 738 : auto poPoint = std::make_unique<OGRPoint>();
502 369 : if (!OGRGeoJSONReadRawPoint(poObjCoords, *poPoint, bHasM))
503 : {
504 10 : return nullptr;
505 : }
506 :
507 359 : return poPoint;
508 : }
509 :
510 : /************************************************************************/
511 : /* OGRGeoJSONReadMultiPoint */
512 : /************************************************************************/
513 :
514 64 : std::unique_ptr<OGRMultiPoint> OGRGeoJSONReadMultiPoint(json_object *poObj,
515 : bool bHasM)
516 : {
517 64 : if (!poObj)
518 : {
519 0 : CPLError(
520 : CE_Failure, CPLE_AppDefined,
521 : "OGRGeoJSONReadMultiPoint(): invalid MultiPoint object. Got null.");
522 0 : return nullptr;
523 : }
524 64 : json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
525 64 : if (nullptr == poObjPoints)
526 : {
527 3 : CPLError(CE_Failure, CPLE_AppDefined,
528 : "Invalid MultiPoint object. "
529 : "Missing \'coordinates\' member.");
530 3 : return nullptr;
531 : }
532 :
533 61 : std::unique_ptr<OGRMultiPoint> poMultiPoint;
534 61 : if (json_type_array == json_object_get_type(poObjPoints))
535 : {
536 60 : const auto nPoints = json_object_array_length(poObjPoints);
537 :
538 60 : poMultiPoint = std::make_unique<OGRMultiPoint>();
539 :
540 259 : for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
541 : {
542 : json_object *poObjCoords =
543 203 : json_object_array_get_idx(poObjPoints, i);
544 :
545 203 : OGRPoint pt;
546 203 : if (!OGRGeoJSONReadRawPoint(poObjCoords, pt, bHasM))
547 : {
548 4 : return nullptr;
549 : }
550 199 : poMultiPoint->addGeometry(&pt);
551 : }
552 : }
553 : else
554 : {
555 1 : CPLError(CE_Failure, CPLE_AppDefined,
556 : "OGRGeoJSONReadMultiPoint(): invalid MultiPoint. "
557 : "Unexpected type %s for '%s'. Expected array.",
558 : GetJSONConstructName(json_object_get_type(poObjPoints)),
559 : json_object_to_json_string(poObjPoints));
560 : }
561 :
562 57 : return poMultiPoint;
563 : }
564 :
565 : /************************************************************************/
566 : /* OGRGeoJSONReadSimpleCurve */
567 : /************************************************************************/
568 :
569 : template <class T>
570 2073 : static std::unique_ptr<T> OGRGeoJSONReadSimpleCurve(const char *pszFuncName,
571 : json_object *poObj,
572 : bool bHasM, bool bRaw)
573 : {
574 2073 : if (!poObj)
575 : {
576 2 : CPLError(CE_Failure, CPLE_AppDefined,
577 : "%s(): invalid LineString object. Got null.", pszFuncName);
578 2 : return nullptr;
579 : }
580 2071 : json_object *poObjPoints = nullptr;
581 :
582 2071 : if (!bRaw)
583 : {
584 166 : poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
585 166 : if (nullptr == poObjPoints)
586 : {
587 3 : CPLError(CE_Failure, CPLE_AppDefined,
588 : "Invalid LineString object. "
589 : "Missing \'coordinates\' member.");
590 3 : return nullptr;
591 : }
592 : }
593 : else
594 : {
595 1905 : poObjPoints = poObj;
596 : }
597 :
598 2068 : std::unique_ptr<T> poLine;
599 :
600 2068 : if (json_type_array == json_object_get_type(poObjPoints))
601 : {
602 2064 : const int nPoints =
603 2064 : static_cast<int>(json_object_array_length(poObjPoints));
604 :
605 2064 : poLine = std::make_unique<T>();
606 2064 : poLine->setNumPoints(nPoints);
607 :
608 93739 : for (int i = 0; i < nPoints; ++i)
609 : {
610 : json_object *poObjCoords =
611 91687 : json_object_array_get_idx(poObjPoints, i);
612 :
613 91687 : OGRPoint pt;
614 91687 : if (!OGRGeoJSONReadRawPoint(poObjCoords, pt, bHasM))
615 : {
616 12 : return nullptr;
617 : }
618 91675 : if (pt.Is3D())
619 371 : poLine->set3D(true);
620 91675 : if (pt.IsMeasured())
621 200 : poLine->setMeasured(true);
622 91675 : poLine->setPoint(i, &pt);
623 : }
624 : }
625 : else
626 : {
627 4 : CPLError(CE_Failure, CPLE_AppDefined,
628 : "%s(): invalid geometry. "
629 : "Unexpected type %s for '%s'. Expected array.",
630 : pszFuncName,
631 : GetJSONConstructName(json_object_get_type(poObjPoints)),
632 : json_object_to_json_string(poObjPoints));
633 : }
634 :
635 2056 : return poLine;
636 : }
637 :
638 : /************************************************************************/
639 : /* OGRGeoJSONReadLineString */
640 : /************************************************************************/
641 :
642 178 : std::unique_ptr<OGRLineString> OGRGeoJSONReadLineString(json_object *poObj,
643 : bool bHasM, bool bRaw)
644 : {
645 : return OGRGeoJSONReadSimpleCurve<OGRLineString>(__func__, poObj, bHasM,
646 178 : bRaw);
647 : }
648 :
649 : /************************************************************************/
650 : /* OGRGeoJSONReadCircularString */
651 : /************************************************************************/
652 :
653 : std::unique_ptr<OGRCircularString>
654 47 : OGRGeoJSONReadCircularString(json_object *poObj, bool bHasM)
655 : {
656 : return OGRGeoJSONReadSimpleCurve<OGRCircularString>(__func__, poObj, bHasM,
657 47 : /* bRaw = */ false);
658 : }
659 :
660 : /************************************************************************/
661 : /* OGRGeoJSONReadMultiLineString */
662 : /************************************************************************/
663 :
664 : std::unique_ptr<OGRMultiLineString>
665 39 : OGRGeoJSONReadMultiLineString(json_object *poObj, bool bHasM)
666 : {
667 39 : CPLAssert(nullptr != poObj);
668 :
669 39 : json_object *poObjLines = OGRGeoJSONFindMemberByName(poObj, "coordinates");
670 39 : if (nullptr == poObjLines)
671 : {
672 3 : CPLError(CE_Failure, CPLE_AppDefined,
673 : "Invalid MultiLineString object. "
674 : "Missing \'coordinates\' member.");
675 3 : return nullptr;
676 : }
677 :
678 36 : std::unique_ptr<OGRMultiLineString> poMultiLine;
679 :
680 36 : if (json_type_array == json_object_get_type(poObjLines))
681 : {
682 35 : const auto nLines = json_object_array_length(poObjLines);
683 :
684 35 : poMultiLine = std::make_unique<OGRMultiLineString>();
685 :
686 94 : for (auto i = decltype(nLines){0}; i < nLines; ++i)
687 : {
688 59 : json_object *poObjLine = json_object_array_get_idx(poObjLines, i);
689 :
690 : auto poLine =
691 118 : OGRGeoJSONReadLineString(poObjLine, bHasM, /* bRaw = */ true);
692 59 : if (poLine)
693 : {
694 52 : poMultiLine->addGeometry(std::move(poLine));
695 : }
696 : }
697 : }
698 : else
699 : {
700 1 : CPLError(CE_Failure, CPLE_AppDefined,
701 : "OGRGeoJSONReadLineString(): invalid LineString. "
702 : "Unexpected type %s for '%s'. Expected array.",
703 : GetJSONConstructName(json_object_get_type(poObjLines)),
704 : json_object_to_json_string(poObjLines));
705 : }
706 :
707 36 : return poMultiLine;
708 : }
709 :
710 : /************************************************************************/
711 : /* OGRGeoJSONReadLinearRing */
712 : /************************************************************************/
713 :
714 1848 : std::unique_ptr<OGRLinearRing> OGRGeoJSONReadLinearRing(json_object *poObj,
715 : bool bHasM)
716 : {
717 : return OGRGeoJSONReadSimpleCurve<OGRLinearRing>(__func__, poObj, bHasM,
718 1848 : /* bRaw = */ true);
719 : }
720 :
721 : /************************************************************************/
722 : /* OGRGeoJSONReadPolygon */
723 : /************************************************************************/
724 :
725 1698 : std::unique_ptr<OGRPolygon> OGRGeoJSONReadPolygon(json_object *poObj,
726 : bool bHasM, bool bRaw)
727 : {
728 1698 : if (!poObj)
729 : {
730 0 : CPLError(CE_Failure, CPLE_AppDefined,
731 : "OGRGeoJSONReadPolygon(): invalid Polygon object. Got null.");
732 0 : return nullptr;
733 : }
734 1698 : json_object *poObjRings = nullptr;
735 :
736 1698 : if (!bRaw)
737 : {
738 1037 : poObjRings = OGRGeoJSONFindMemberByName(poObj, "coordinates");
739 1037 : if (nullptr == poObjRings)
740 : {
741 3 : CPLError(CE_Failure, CPLE_AppDefined,
742 : "Invalid Polygon object. "
743 : "Missing \'coordinates\' member.");
744 3 : return nullptr;
745 : }
746 : }
747 : else
748 : {
749 661 : poObjRings = poObj;
750 : }
751 :
752 1695 : std::unique_ptr<OGRPolygon> poPolygon;
753 :
754 1695 : if (json_type_array == json_object_get_type(poObjRings))
755 : {
756 1693 : const auto nRings = json_object_array_length(poObjRings);
757 1693 : if (nRings > 0)
758 : {
759 1689 : json_object *poObjPoints = json_object_array_get_idx(poObjRings, 0);
760 1689 : if (!poObjPoints)
761 : {
762 2 : poPolygon = std::make_unique<OGRPolygon>();
763 : }
764 : else
765 : {
766 3374 : auto poRing = OGRGeoJSONReadLinearRing(poObjPoints, bHasM);
767 1687 : if (poRing)
768 : {
769 1681 : poPolygon = std::make_unique<OGRPolygon>();
770 1681 : poPolygon->addRing(std::move(poRing));
771 : }
772 : }
773 :
774 1851 : for (auto i = decltype(nRings){1};
775 1851 : i < nRings && nullptr != poPolygon; ++i)
776 : {
777 162 : poObjPoints = json_object_array_get_idx(poObjRings, i);
778 162 : if (poObjPoints)
779 : {
780 322 : auto poRing = OGRGeoJSONReadLinearRing(poObjPoints, bHasM);
781 161 : if (poRing)
782 : {
783 161 : poPolygon->addRing(std::move(poRing));
784 : }
785 : }
786 : }
787 : }
788 : else
789 : {
790 4 : poPolygon = std::make_unique<OGRPolygon>();
791 : }
792 : }
793 : else
794 : {
795 2 : CPLError(CE_Warning, CPLE_AppDefined,
796 : "OGRGeoJSONReadPolygon(): unexpected type of JSON construct "
797 : "%s for '%s'. Expected array.",
798 : GetJSONConstructName(json_object_get_type(poObjRings)),
799 : json_object_to_json_string(poObjRings));
800 : }
801 :
802 1695 : return poPolygon;
803 : }
804 :
805 : /************************************************************************/
806 : /* OGRGeoJSONReadMultiPolygon */
807 : /************************************************************************/
808 :
809 415 : std::unique_ptr<OGRMultiPolygon> OGRGeoJSONReadMultiPolygon(json_object *poObj,
810 : bool bHasM)
811 : {
812 415 : CPLAssert(nullptr != poObj);
813 :
814 415 : json_object *poObjPolys = OGRGeoJSONFindMemberByName(poObj, "coordinates");
815 415 : if (nullptr == poObjPolys)
816 : {
817 3 : CPLError(CE_Failure, CPLE_AppDefined,
818 : "Invalid MultiPolygon object. "
819 : "Missing \'coordinates\' member.");
820 3 : return nullptr;
821 : }
822 :
823 412 : std::unique_ptr<OGRMultiPolygon> poMultiPoly;
824 :
825 412 : if (json_type_array == json_object_get_type(poObjPolys))
826 : {
827 : const int nPolys =
828 411 : static_cast<int>(json_object_array_length(poObjPolys));
829 :
830 411 : poMultiPoly = std::make_unique<OGRMultiPolygon>();
831 :
832 1070 : for (int i = 0; i < nPolys; ++i)
833 : {
834 659 : json_object *poObjPoly = json_object_array_get_idx(poObjPolys, i);
835 659 : if (!poObjPoly)
836 : {
837 6 : poMultiPoly->addGeometryDirectly(
838 6 : std::make_unique<OGRPolygon>().release());
839 : }
840 : else
841 : {
842 : auto poPoly =
843 1312 : OGRGeoJSONReadPolygon(poObjPoly, bHasM, /* bRaw = */ true);
844 656 : if (poPoly)
845 : {
846 652 : poMultiPoly->addGeometry(std::move(poPoly));
847 : }
848 : }
849 : }
850 : }
851 : else
852 : {
853 1 : CPLError(CE_Warning, CPLE_AppDefined,
854 : "OGRGeoJSONReadMultiPolygon(): unexpected type of JSON "
855 : "construct %s for '%s'. Expected array.",
856 : GetJSONConstructName(json_object_get_type(poObjPolys)),
857 : json_object_to_json_string(poObjPolys));
858 : }
859 :
860 412 : return poMultiPoly;
861 : }
862 :
863 : /************************************************************************/
864 : /* OGRGeoJSONReadCollection */
865 : /************************************************************************/
866 :
867 : template <class T>
868 : static std::unique_ptr<T>
869 77 : OGRGeoJSONReadCollection(const char *pszFuncName, const char *pszGeomTypeName,
870 : json_object *poObj, bool bHasM,
871 : const OGRSpatialReference *poSRS)
872 : {
873 77 : CPLAssert(nullptr != poObj);
874 :
875 77 : json_object *poObjGeoms = OGRGeoJSONFindMemberByName(poObj, "geometries");
876 77 : if (nullptr == poObjGeoms)
877 : {
878 1 : CPLError(CE_Failure, CPLE_AppDefined,
879 : "Invalid %s object. "
880 : "Missing \'geometries\' member.",
881 : pszGeomTypeName);
882 1 : return nullptr;
883 : }
884 :
885 76 : std::unique_ptr<T> poCollection;
886 :
887 76 : if (json_type_array == json_object_get_type(poObjGeoms))
888 : {
889 76 : poCollection = std::make_unique<T>();
890 76 : poCollection->assignSpatialReference(poSRS);
891 :
892 76 : const int nGeoms =
893 76 : static_cast<int>(json_object_array_length(poObjGeoms));
894 229 : for (int i = 0; i < nGeoms; ++i)
895 : {
896 157 : json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
897 157 : if (!poObjGeom)
898 : {
899 3 : CPLError(CE_Warning, CPLE_AppDefined,
900 : "%s(): skipping null "
901 : "sub-geometry",
902 : pszFuncName);
903 3 : continue;
904 : }
905 :
906 154 : auto poGeometry = OGRGeoJSONReadGeometry(poObjGeom, bHasM, poSRS);
907 154 : if (poGeometry)
908 : {
909 : if constexpr (std::is_same_v<T, OGRCompoundCurve>)
910 : {
911 56 : auto eFlatType = wkbFlatten(poGeometry->getGeometryType());
912 56 : if (eFlatType == wkbLineString ||
913 : eFlatType == wkbCircularString)
914 : {
915 55 : if (poCollection->addCurve(std::unique_ptr<OGRCurve>(
916 55 : poGeometry.release()->toCurve())) !=
917 : OGRERR_NONE)
918 0 : return nullptr;
919 : }
920 : else
921 : {
922 1 : CPLError(CE_Warning, CPLE_AppDefined,
923 : "%s(): member of a CompoundCurve is not a "
924 : "LineString or CircularString.",
925 : pszFuncName);
926 1 : return nullptr;
927 : }
928 : }
929 : else if constexpr (std::is_same_v<T, OGRCurvePolygon>)
930 : {
931 41 : auto eFlatType = wkbFlatten(poGeometry->getGeometryType());
932 41 : if (eFlatType == wkbLineString ||
933 11 : eFlatType == wkbCircularString ||
934 : eFlatType == wkbCompoundCurve)
935 : {
936 40 : if (poCollection->addRing(std::unique_ptr<OGRCurve>(
937 40 : poGeometry.release()->toCurve())) !=
938 : OGRERR_NONE)
939 0 : return nullptr;
940 : }
941 : else
942 : {
943 1 : CPLError(CE_Warning, CPLE_AppDefined,
944 : "%s(): member of a CurvePolygon is not a "
945 : "LineString, CircularString or CompoundCurve.",
946 : pszFuncName);
947 1 : return nullptr;
948 : }
949 : }
950 : else
951 : {
952 57 : const auto eChildType = poGeometry->getGeometryType();
953 57 : if (poCollection->addGeometry(std::move(poGeometry)) !=
954 : OGRERR_NONE)
955 : {
956 2 : CPLError(
957 : CE_Warning, CPLE_AppDefined,
958 : "%s(): Invalid child geometry type (%s) for %s",
959 : pszFuncName, OGRToOGCGeomType(eChildType),
960 : pszGeomTypeName);
961 2 : return nullptr;
962 : }
963 : }
964 : }
965 : }
966 : }
967 : else
968 : {
969 0 : CPLError(CE_Warning, CPLE_AppDefined,
970 : "%s(): unexpected type of JSON "
971 : "construct %s for '%s'. Expected array.",
972 : pszFuncName,
973 : GetJSONConstructName(json_object_get_type(poObjGeoms)),
974 : json_object_to_json_string(poObjGeoms));
975 : }
976 :
977 72 : return poCollection;
978 : }
979 :
980 : /************************************************************************/
981 : /* OGRGeoJSONReadGeometryCollection */
982 : /************************************************************************/
983 :
984 : std::unique_ptr<OGRGeometryCollection>
985 11 : OGRGeoJSONReadGeometryCollection(json_object *poObj, bool bHasM,
986 : const OGRSpatialReference *poSRS)
987 : {
988 : return OGRGeoJSONReadCollection<OGRGeometryCollection>(
989 11 : __func__, "GeometryCollection", poObj, bHasM, poSRS);
990 : }
991 :
992 : /************************************************************************/
993 : /* OGRGeoJSONReadCompoundCurve */
994 : /************************************************************************/
995 :
996 : std::unique_ptr<OGRCompoundCurve>
997 28 : OGRGeoJSONReadCompoundCurve(json_object *poObj, bool bHasM,
998 : const OGRSpatialReference *poSRS)
999 : {
1000 : return OGRGeoJSONReadCollection<OGRCompoundCurve>(__func__, "CompoundCurve",
1001 28 : poObj, bHasM, poSRS);
1002 : }
1003 :
1004 : /************************************************************************/
1005 : /* OGRGeoJSONReadCurvePolygon */
1006 : /************************************************************************/
1007 :
1008 20 : std::unique_ptr<OGRCurvePolygon> OGRGeoJSONReadCurvePolygon(json_object *poObj,
1009 : bool bHasM)
1010 : {
1011 : return OGRGeoJSONReadCollection<OGRCurvePolygon>(
1012 20 : __func__, "CurvePolygon", poObj, bHasM, /* poSRS = */ nullptr);
1013 : }
1014 :
1015 : /************************************************************************/
1016 : /* OGRGeoJSONReadMultiCurve */
1017 : /************************************************************************/
1018 :
1019 : std::unique_ptr<OGRMultiCurve>
1020 9 : OGRGeoJSONReadMultiCurve(json_object *poObj, bool bHasM,
1021 : const OGRSpatialReference *poSRS)
1022 : {
1023 : return OGRGeoJSONReadCollection<OGRMultiCurve>(__func__, "MultiCurve",
1024 9 : poObj, bHasM, poSRS);
1025 : }
1026 :
1027 : /************************************************************************/
1028 : /* OGRGeoJSONReadMultiSurface */
1029 : /************************************************************************/
1030 :
1031 : std::unique_ptr<OGRMultiSurface>
1032 9 : OGRGeoJSONReadMultiSurface(json_object *poObj, bool bHasM,
1033 : const OGRSpatialReference *poSRS)
1034 : {
1035 : return OGRGeoJSONReadCollection<OGRMultiSurface>(__func__, "MultiSurface",
1036 9 : poObj, bHasM, poSRS);
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* OGRGeoJSONReadSpatialReference */
1041 : /************************************************************************/
1042 :
1043 520 : OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj)
1044 : {
1045 :
1046 : /* -------------------------------------------------------------------- */
1047 : /* Read spatial reference definition. */
1048 : /* -------------------------------------------------------------------- */
1049 520 : OGRSpatialReference *poSRS = nullptr;
1050 :
1051 520 : json_object *poObjSrs = OGRGeoJSONFindMemberByName(poObj, "crs");
1052 520 : if (nullptr != poObjSrs)
1053 : {
1054 : json_object *poObjSrsType =
1055 75 : OGRGeoJSONFindMemberByName(poObjSrs, "type");
1056 75 : if (poObjSrsType == nullptr)
1057 1 : return nullptr;
1058 :
1059 74 : const char *pszSrsType = json_object_get_string(poObjSrsType);
1060 :
1061 : // TODO: Add URL and URN types support.
1062 74 : if (STARTS_WITH_CI(pszSrsType, "NAME"))
1063 : {
1064 : json_object *poObjSrsProps =
1065 54 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1066 54 : if (poObjSrsProps == nullptr)
1067 2 : return nullptr;
1068 :
1069 : json_object *poNameURL =
1070 52 : OGRGeoJSONFindMemberByName(poObjSrsProps, "name");
1071 52 : if (poNameURL == nullptr)
1072 2 : return nullptr;
1073 :
1074 50 : const char *pszName = json_object_get_string(poNameURL);
1075 :
1076 50 : if (EQUAL(pszName, "urn:ogc:def:crs:OGC:1.3:CRS84"))
1077 12 : pszName = "EPSG:4326";
1078 :
1079 50 : poSRS = new OGRSpatialReference();
1080 50 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1081 50 : if (OGRERR_NONE !=
1082 50 : poSRS->SetFromUserInput(
1083 : pszName,
1084 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()))
1085 : {
1086 2 : delete poSRS;
1087 2 : poSRS = nullptr;
1088 : }
1089 : }
1090 :
1091 20 : else if (STARTS_WITH_CI(pszSrsType, "EPSG"))
1092 : {
1093 : json_object *poObjSrsProps =
1094 7 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1095 7 : if (poObjSrsProps == nullptr)
1096 2 : return nullptr;
1097 :
1098 : json_object *poObjCode =
1099 5 : OGRGeoJSONFindMemberByName(poObjSrsProps, "code");
1100 5 : if (poObjCode == nullptr)
1101 2 : return nullptr;
1102 :
1103 3 : int nEPSG = json_object_get_int(poObjCode);
1104 :
1105 3 : poSRS = new OGRSpatialReference();
1106 3 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1107 3 : if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
1108 : {
1109 2 : delete poSRS;
1110 2 : poSRS = nullptr;
1111 : }
1112 : }
1113 :
1114 13 : else if (STARTS_WITH_CI(pszSrsType, "URL") ||
1115 13 : STARTS_WITH_CI(pszSrsType, "LINK"))
1116 : {
1117 : json_object *poObjSrsProps =
1118 6 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1119 6 : if (poObjSrsProps == nullptr)
1120 2 : return nullptr;
1121 :
1122 : json_object *poObjURL =
1123 4 : OGRGeoJSONFindMemberByName(poObjSrsProps, "url");
1124 :
1125 4 : if (nullptr == poObjURL)
1126 : {
1127 4 : poObjURL = OGRGeoJSONFindMemberByName(poObjSrsProps, "href");
1128 : }
1129 4 : if (poObjURL == nullptr)
1130 2 : return nullptr;
1131 :
1132 2 : const char *pszURL = json_object_get_string(poObjURL);
1133 :
1134 2 : poSRS = new OGRSpatialReference();
1135 2 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1136 2 : if (OGRERR_NONE != poSRS->importFromUrl(pszURL))
1137 : {
1138 2 : delete poSRS;
1139 2 : poSRS = nullptr;
1140 2 : }
1141 : }
1142 :
1143 7 : else if (EQUAL(pszSrsType, "OGC"))
1144 : {
1145 : json_object *poObjSrsProps =
1146 7 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1147 7 : if (poObjSrsProps == nullptr)
1148 2 : return nullptr;
1149 :
1150 : json_object *poObjURN =
1151 5 : OGRGeoJSONFindMemberByName(poObjSrsProps, "urn");
1152 5 : if (poObjURN == nullptr)
1153 2 : return nullptr;
1154 :
1155 3 : poSRS = new OGRSpatialReference();
1156 3 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1157 3 : if (OGRERR_NONE !=
1158 3 : poSRS->importFromURN(json_object_get_string(poObjURN)))
1159 : {
1160 2 : delete poSRS;
1161 2 : poSRS = nullptr;
1162 : }
1163 : }
1164 : }
1165 :
1166 : // Strip AXIS, since geojson has (easting, northing) / (longitude, latitude)
1167 : // order. According to http://www.geojson.org/geojson-spec.html#id2 :
1168 : // "Point coordinates are in x, y order (easting, northing for projected
1169 : // coordinates, longitude, latitude for geographic coordinates)".
1170 503 : if (poSRS != nullptr)
1171 : {
1172 50 : OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode("GEOGCS");
1173 50 : if (poGEOGCS != nullptr)
1174 50 : poGEOGCS->StripNodes("AXIS");
1175 : }
1176 :
1177 503 : return poSRS;
1178 : }
1179 :
1180 : /************************************************************************/
1181 : /* OGR_G_CreateGeometryFromJson */
1182 : /************************************************************************/
1183 :
1184 : /** Create a OGR geometry from a GeoJSON geometry object */
1185 46 : OGRGeometryH OGR_G_CreateGeometryFromJson(const char *pszJson)
1186 : {
1187 46 : if (nullptr == pszJson)
1188 : {
1189 : // Translation failed.
1190 0 : return nullptr;
1191 : }
1192 :
1193 46 : json_object *poObj = nullptr;
1194 46 : if (!OGRJSonParse(pszJson, &poObj))
1195 0 : return nullptr;
1196 :
1197 : OGRGeometry *poGeometry =
1198 92 : OGRGeoJSONReadGeometry(poObj, /* bHasM = */ false,
1199 : /* OGRSpatialReference* = */ nullptr)
1200 46 : .release();
1201 :
1202 : // Release JSON tree.
1203 46 : json_object_put(poObj);
1204 :
1205 46 : return OGRGeometry::ToHandle(poGeometry);
1206 : }
1207 :
1208 : /*! @endcond */
|