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