Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implementation of OGC Features and Geometries JSON (JSON-FG)
5 : * Author: Even Rouault <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_jsonfg.h"
14 :
15 : #include "ogrgeojsonreader.h"
16 : #include "ogrgeojsonutils.h"
17 : #include "ogrlibjsonutils.h"
18 : #include "ogrgeojsongeometry.h"
19 : #include "ogr_geojson.h"
20 :
21 : #include "cpl_vsi_virtual.h"
22 :
23 : #include <json.h> // JSON-C
24 :
25 : /************************************************************************/
26 : /* OGRJSONFGReader::~OGRJSONFGReader() */
27 : /************************************************************************/
28 :
29 233 : OGRJSONFGReader::~OGRJSONFGReader()
30 : {
31 233 : if (poObject_)
32 203 : json_object_put(poObject_);
33 233 : }
34 :
35 : /************************************************************************/
36 : /* OGRJSONFGReader::Load() */
37 : /************************************************************************/
38 :
39 77 : bool OGRJSONFGReader::Load(OGRJSONFGDataset *poDS, const char *pszText,
40 : const std::string &osDefaultLayerName)
41 : {
42 77 : if (!OGRJSonParse(pszText, &poObject_))
43 0 : return false;
44 :
45 77 : poDS_ = poDS;
46 77 : osDefaultLayerName_ = osDefaultLayerName;
47 :
48 77 : GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
49 :
50 77 : if (objType != GeoJSONObject::eFeature &&
51 25 : objType != GeoJSONObject::eFeatureCollection &&
52 : objType != GeoJSONObject::eUnknown)
53 : {
54 25 : json_object *poObj = json_object_new_object();
55 25 : json_object_object_add(poObj, "type",
56 : json_object_new_string("Feature"));
57 25 : json_object_object_add(poObj, "place", poObject_);
58 25 : poObject_ = poObj;
59 25 : objType = GeoJSONObject::eFeature;
60 : }
61 :
62 77 : if (!GenerateLayerDefns())
63 0 : return false;
64 :
65 77 : if (objType == GeoJSONObject::eFeature)
66 : {
67 30 : OGRJSONFGMemLayer *poLayer = nullptr;
68 : auto poFeat = ReadFeature(poObject_, nullptr, /* bHasM=*/false,
69 60 : &poLayer, nullptr);
70 30 : if (poFeat)
71 : {
72 30 : poLayer->AddFeature(std::move(poFeat));
73 30 : return true;
74 : }
75 0 : return false;
76 : }
77 47 : else if (objType == GeoJSONObject::eFeatureCollection)
78 : {
79 : const bool bHasM =
80 47 : OGRJSONFGHasMeasure(poObject_, /* bUpperLevelMValue = */ false);
81 : json_object *poObjFeatures =
82 47 : OGRGeoJSONFindMemberByName(poObject_, "features");
83 94 : if (nullptr != poObjFeatures &&
84 47 : json_type_array == json_object_get_type(poObjFeatures))
85 : {
86 47 : const auto nFeatures = json_object_array_length(poObjFeatures);
87 96 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
88 : {
89 : json_object *poObjFeature =
90 49 : json_object_array_get_idx(poObjFeatures, i);
91 49 : OGRJSONFGMemLayer *poLayer = nullptr;
92 : auto poFeat = ReadFeature(poObjFeature, nullptr, bHasM,
93 49 : &poLayer, nullptr);
94 49 : if (!poFeat)
95 0 : return false;
96 49 : poLayer->AddFeature(std::move(poFeat));
97 : }
98 : }
99 : }
100 : else
101 : {
102 0 : return false;
103 : }
104 :
105 47 : return true;
106 : }
107 :
108 : /************************************************************************/
109 : /* OGRJSONFGReadCoordRefSys() */
110 : /************************************************************************/
111 :
112 : static std::unique_ptr<OGRSpatialReference>
113 170 : OGRJSONFGReadCoordRefSys(json_object *poCoordRefSys, bool bCanRecurse = true)
114 : {
115 170 : const auto eType = json_object_get_type(poCoordRefSys);
116 170 : if (eType == json_type_string)
117 : {
118 137 : const char *pszStr = json_object_get_string(poCoordRefSys);
119 137 : if (pszStr[0] == '[' && pszStr[strlen(pszStr) - 1] == ']')
120 : {
121 : // Safe CURIE, e.g. "[EPSG:4326]" (removed in JSONFG 0.3)
122 46 : const char *pszColon = strchr(pszStr + 1, ':');
123 46 : if (!pszColon)
124 : {
125 2 : CPLError(CE_Failure, CPLE_AppDefined,
126 : "Invalid coordRefSys string: %s", pszStr);
127 2 : return nullptr;
128 : }
129 88 : std::string osURL("http://www.opengis.net/def/crs/");
130 44 : osURL.append(pszStr + 1, pszColon - (pszStr + 1));
131 44 : osURL += "/0/";
132 : osURL.append(pszColon + 1,
133 44 : (pszStr + strlen(pszStr) - 1) - (pszColon + 1));
134 88 : auto poSRS = std::make_unique<OGRSpatialReference>();
135 44 : if (poSRS->importFromCRSURL(osURL.c_str()) != OGRERR_NONE)
136 : {
137 3 : return nullptr;
138 : }
139 41 : return poSRS;
140 : }
141 91 : else if (STARTS_WITH(pszStr, "http://www.opengis.net/def/crs/") ||
142 4 : STARTS_WITH(pszStr, "https://www.opengis.net/def/crs/"))
143 : {
144 : // OGC URI, e.g. "http://www.opengis.net/def/crs/EPSG/0/4326"
145 174 : auto poSRS = std::make_unique<OGRSpatialReference>();
146 87 : if (poSRS->importFromCRSURL(pszStr) != OGRERR_NONE)
147 : {
148 1 : return nullptr;
149 : }
150 86 : return poSRS;
151 : }
152 : else
153 : {
154 4 : CPLError(CE_Failure, CPLE_AppDefined,
155 : "Invalid coordRefSys string: %s", pszStr);
156 4 : return nullptr;
157 : }
158 : }
159 33 : else if (eType == json_type_object)
160 : {
161 : /* Things like
162 : {
163 : "type": "Reference",
164 : "href": "http://www.opengis.net/def/crs/EPSG/0/4258",
165 : "epoch": 2016.47
166 : }
167 : */
168 :
169 17 : json_object *poType = CPL_json_object_object_get(poCoordRefSys, "type");
170 17 : if (!poType)
171 : {
172 2 : CPLError(CE_Failure, CPLE_AppDefined,
173 : "Missing type member in coordRefSys object");
174 2 : return nullptr;
175 : }
176 15 : if (json_object_get_type(poType) != json_type_string)
177 : {
178 1 : CPLError(CE_Failure, CPLE_AppDefined,
179 : "Type member of coordRefSys object is not a string");
180 1 : return nullptr;
181 : }
182 14 : const char *pszType = json_object_get_string(poType);
183 14 : std::unique_ptr<OGRSpatialReference> poSRS;
184 14 : if (strcmp(pszType, "Reference") == 0)
185 : {
186 : json_object *poHRef =
187 12 : CPL_json_object_object_get(poCoordRefSys, "href");
188 12 : if (!poHRef)
189 : {
190 2 : CPLError(CE_Failure, CPLE_AppDefined,
191 : "Missing href member in coordRefSys object");
192 2 : return nullptr;
193 : }
194 :
195 10 : poSRS = OGRJSONFGReadCoordRefSys(poHRef);
196 : }
197 2 : else if (strcmp(pszType, "PROJJSON") == 0)
198 : {
199 : json_object *poValue =
200 1 : CPL_json_object_object_get(poCoordRefSys, "value");
201 1 : if (!poValue)
202 : {
203 0 : CPLError(CE_Failure, CPLE_AppDefined,
204 : "Missing value member in coordRefSys object");
205 0 : return nullptr;
206 : }
207 1 : if (json_object_get_type(poValue) != json_type_object)
208 : {
209 0 : CPLError(CE_Failure, CPLE_AppDefined,
210 : "Invalid type for coordRefSys.value member");
211 0 : return nullptr;
212 : }
213 :
214 1 : const char *pszPROJJSON = json_object_to_json_string(poValue);
215 1 : poSRS = std::make_unique<OGRSpatialReference>();
216 1 : if (poSRS->SetFromUserInput(
217 : pszPROJJSON,
218 : OGRSpatialReference::
219 1 : SET_FROM_USER_INPUT_LIMITATIONS_get()) != OGRERR_NONE)
220 : {
221 0 : poSRS.reset();
222 : }
223 : }
224 : else
225 : {
226 1 : CPLError(CE_Failure, CPLE_NotSupported,
227 : "Unsupported coordRefSys.type: %s", pszType);
228 1 : return nullptr;
229 : }
230 :
231 11 : if (poSRS)
232 : {
233 : json_object *poEpoch =
234 9 : CPL_json_object_object_get(poCoordRefSys, "epoch");
235 9 : if (poEpoch)
236 : {
237 6 : const auto epochType = json_object_get_type(poEpoch);
238 6 : if (epochType != json_type_int && epochType != json_type_double)
239 : {
240 1 : CPLError(CE_Failure, CPLE_AppDefined,
241 : "Wrong value type for epoch member in coordRefSys "
242 : "object");
243 1 : return nullptr;
244 : }
245 :
246 5 : poSRS->SetCoordinateEpoch(json_object_get_double(poEpoch));
247 : }
248 : }
249 :
250 10 : return poSRS;
251 : }
252 16 : else if (eType == json_type_array && bCanRecurse)
253 : {
254 13 : if (json_object_array_length(poCoordRefSys) != 2)
255 : {
256 2 : CPLError(CE_Failure, CPLE_AppDefined,
257 : "Expected 2 items in coordRefSys array");
258 2 : return nullptr;
259 : }
260 : auto poSRS1 = OGRJSONFGReadCoordRefSys(
261 : json_object_array_get_idx(poCoordRefSys, 0),
262 22 : /* bCanRecurse = */ false);
263 11 : if (!poSRS1)
264 1 : return nullptr;
265 : auto poSRS2 = OGRJSONFGReadCoordRefSys(
266 : json_object_array_get_idx(poCoordRefSys, 1),
267 20 : /* bCanRecurse = */ false);
268 10 : if (!poSRS2)
269 1 : return nullptr;
270 18 : auto poSRS = std::make_unique<OGRSpatialReference>();
271 :
272 18 : std::string osName;
273 9 : const char *pszName1 = poSRS1->GetName();
274 9 : osName = pszName1 ? pszName1 : "unnamed";
275 9 : osName += " + ";
276 9 : const char *pszName2 = poSRS2->GetName();
277 9 : osName += pszName2 ? pszName2 : "unnamed";
278 :
279 9 : if (poSRS->SetCompoundCS(osName.c_str(), poSRS1.get(), poSRS2.get()) !=
280 : OGRERR_NONE)
281 0 : return nullptr;
282 9 : const double dfEpoch = poSRS1->GetCoordinateEpoch();
283 9 : if (dfEpoch > 0)
284 2 : poSRS->SetCoordinateEpoch(dfEpoch);
285 9 : return poSRS;
286 : }
287 : else
288 : {
289 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid coordRefSys object");
290 : }
291 3 : return nullptr;
292 : }
293 :
294 : /************************************************************************/
295 : /* OGRJSONFGReader::AnalyzeWithStreamingParser() */
296 : /************************************************************************/
297 :
298 126 : bool OGRJSONFGReader::AnalyzeWithStreamingParser(
299 : OGRJSONFGDataset *poDS, VSILFILE *fp, const std::string &osDefaultLayerName,
300 : bool &bCanTryWithNonStreamingParserOut, bool &bHasTopLevelMeasures)
301 : {
302 126 : poDS_ = poDS;
303 126 : osDefaultLayerName_ = osDefaultLayerName;
304 :
305 126 : bCanTryWithNonStreamingParserOut = false;
306 : OGRJSONFGStreamingParser oParser(*this, /*bFirstPass = */ true,
307 252 : /* bHasTopLevelMeasures =*/false);
308 :
309 252 : std::vector<GByte> abyBuffer;
310 126 : abyBuffer.resize(4096 * 10);
311 : while (true)
312 : {
313 126 : size_t nRead = VSIFReadL(abyBuffer.data(), 1, abyBuffer.size(), fp);
314 126 : const bool bFinished = nRead < abyBuffer.size();
315 126 : if (!oParser.Parse(
316 : std::string_view(
317 126 : reinterpret_cast<const char *>(abyBuffer.data()), nRead),
318 252 : bFinished) ||
319 126 : oParser.ExceptionOccurred())
320 : {
321 0 : return false;
322 : }
323 126 : if (oParser.IsTypeKnown() && !oParser.IsFeatureCollection())
324 : {
325 0 : break;
326 : }
327 126 : if (bFinished)
328 126 : break;
329 0 : }
330 :
331 126 : if (!oParser.IsTypeKnown() || !oParser.IsFeatureCollection())
332 : {
333 0 : fp->Seek(0, SEEK_END);
334 0 : const vsi_l_offset nFileSize = fp->Tell();
335 : const vsi_l_offset nRAM =
336 0 : static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM());
337 0 : if (nRAM == 0 || nRAM > nFileSize * 20)
338 : {
339 : // Only try full ingestion if we have 20x more RAM than the file
340 : // size
341 0 : bCanTryWithNonStreamingParserOut = true;
342 : }
343 0 : return false;
344 : }
345 :
346 126 : poObject_ = oParser.StealRootObject();
347 126 : bHasTopLevelMeasures = oParser.HasTopLevelMeasures();
348 :
349 126 : return FinalizeGenerateLayerDefns(true);
350 : }
351 :
352 : /************************************************************************/
353 : /* OGRJSONFGReader::GenerateLayerDefns() */
354 : /************************************************************************/
355 :
356 77 : bool OGRJSONFGReader::GenerateLayerDefns()
357 : {
358 77 : const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
359 77 : if (objType == GeoJSONObject::eFeature)
360 : {
361 30 : if (!GenerateLayerDefnFromFeature(poObject_))
362 0 : return false;
363 : }
364 47 : else if (objType == GeoJSONObject::eFeatureCollection)
365 : {
366 : json_object *poObjFeatures =
367 47 : OGRGeoJSONFindMemberByName(poObject_, "features");
368 94 : if (nullptr != poObjFeatures &&
369 47 : json_type_array == json_object_get_type(poObjFeatures))
370 : {
371 47 : const auto nFeatures = json_object_array_length(poObjFeatures);
372 96 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
373 : {
374 : json_object *poObjFeature =
375 49 : json_object_array_get_idx(poObjFeatures, i);
376 49 : if (!GenerateLayerDefnFromFeature(poObjFeature))
377 : {
378 0 : return false;
379 : }
380 : }
381 : }
382 : else
383 : {
384 0 : CPLError(CE_Failure, CPLE_AppDefined,
385 : "Invalid FeatureCollection object. "
386 : "Missing \'features\' member.");
387 0 : return false;
388 : }
389 : }
390 : else
391 : {
392 0 : CPLError(CE_Failure, CPLE_AppDefined,
393 : "Missing or unhandled root type object");
394 0 : return false;
395 : }
396 :
397 77 : return FinalizeGenerateLayerDefns(false);
398 : }
399 :
400 : /************************************************************************/
401 : /* OGRJSONFGReader::FinalizeGenerateLayerDefns() */
402 : /************************************************************************/
403 :
404 203 : bool OGRJSONFGReader::FinalizeGenerateLayerDefns(bool bStreamedLayer)
405 : {
406 203 : json_object *poName = CPL_json_object_object_get(poObject_, "featureType");
407 203 : if (poName && json_object_get_type(poName) == json_type_string)
408 : {
409 : // Remap from hard-coded default layer name to the one of featureType
410 20 : auto oIter = oMapBuildContext_.find(osDefaultLayerName_);
411 20 : osDefaultLayerName_ = json_object_get_string(poName);
412 20 : if (oIter != oMapBuildContext_.end())
413 : {
414 38 : auto oBuildContext = std::move(oIter->second);
415 19 : oMapBuildContext_.erase(oIter);
416 19 : oMapBuildContext_[osDefaultLayerName_] = std::move(oBuildContext);
417 : }
418 : }
419 183 : else if (poName && json_object_get_type(poName) == json_type_array)
420 : {
421 : static bool bWarningMsgEmitted = false;
422 0 : if (!bWarningMsgEmitted)
423 : {
424 0 : CPLError(CE_Warning, CPLE_AppDefined,
425 : "featureType value as an array is not supported.");
426 0 : bWarningMsgEmitted = true;
427 : }
428 : }
429 :
430 203 : json_object *poCoordRefSys = nullptr;
431 0 : std::unique_ptr<OGRSpatialReference> poSRSTopLevel;
432 203 : bool bInvalidCRS = false;
433 203 : bool bSwapPlacesXYTopLevel = false;
434 275 : if (json_object_object_get_ex(poObject_, "coordRefSys", &poCoordRefSys) &&
435 72 : eGeometryElement_ != GeometryElement::GEOMETRY)
436 : {
437 69 : poSRSTopLevel = OGRJSONFGReadCoordRefSys(poCoordRefSys);
438 69 : if (poSRSTopLevel)
439 : {
440 47 : poSRSTopLevel->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
441 47 : bSwapPlacesXYTopLevel = OGRJSONFGMustSwapXY(poSRSTopLevel.get());
442 : }
443 : else
444 : {
445 22 : bInvalidCRS = true;
446 : }
447 : }
448 :
449 203 : json_object *poMeasures = nullptr;
450 207 : if (json_object_object_get_ex(poObject_, "measures", &poMeasures) &&
451 4 : json_object_get_type(poMeasures) == json_type_object)
452 : {
453 4 : json_object *poEnabled = nullptr;
454 4 : if (json_object_object_get_ex(poMeasures, "enabled", &poEnabled) &&
455 8 : json_object_get_type(poEnabled) == json_type_boolean &&
456 4 : json_object_get_boolean(poEnabled))
457 : {
458 4 : json_object *poUnit = nullptr;
459 8 : if (json_object_object_get_ex(poMeasures, "unit", &poUnit) &&
460 4 : json_object_get_type(poUnit) == json_type_string)
461 : {
462 4 : osMeasureUnit_ = json_object_get_string(poUnit);
463 : }
464 :
465 4 : json_object *poDescription = nullptr;
466 4 : if (json_object_object_get_ex(poMeasures, "description",
467 8 : &poDescription) &&
468 4 : json_object_get_type(poDescription) == json_type_string)
469 : {
470 4 : osMeasureDescription_ = json_object_get_string(poDescription);
471 : }
472 : }
473 : }
474 :
475 : // Finalize layer definition building and create OGRLayer objects
476 410 : for (auto &oBuildContextIter : oMapBuildContext_)
477 : {
478 207 : const char *pszLayerName = oBuildContextIter.first.c_str();
479 207 : auto &oBuildContext = oBuildContextIter.second;
480 :
481 207 : FinalizeBuildContext(oBuildContext, pszLayerName, bStreamedLayer,
482 : bInvalidCRS, bSwapPlacesXYTopLevel,
483 : poSRSTopLevel.get());
484 : }
485 :
486 406 : return true;
487 : }
488 :
489 : /************************************************************************/
490 : /* OGRJSONFGReader::FinalizeBuildContext() */
491 : /************************************************************************/
492 :
493 207 : void OGRJSONFGReader::FinalizeBuildContext(LayerDefnBuildContext &oBuildContext,
494 : const char *pszLayerName,
495 : bool bStreamedLayer,
496 : bool bInvalidCRS,
497 : bool bSwapPlacesXYTopLevel,
498 : OGRSpatialReference *poSRSTopLevel)
499 : {
500 : std::unique_ptr<OGRSpatialReference> poSRSWGS84(
501 414 : OGRSpatialReference::GetWGS84SRS()->Clone());
502 207 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
503 :
504 207 : OGRSpatialReference *poSRSLayer = nullptr;
505 207 : if (oBuildContext.poCRSAtFeatureLevel)
506 : {
507 64 : poSRSLayer = oBuildContext.poCRSAtFeatureLevel.get();
508 64 : oBuildContext.bSwapPlacesXY = OGRJSONFGMustSwapXY(poSRSLayer);
509 : }
510 143 : else if (poSRSTopLevel)
511 : {
512 39 : poSRSLayer = poSRSTopLevel;
513 39 : oBuildContext.bSwapPlacesXY = bSwapPlacesXYTopLevel;
514 : }
515 207 : if (!bInvalidCRS)
516 : {
517 185 : if (!poSRSLayer && !oBuildContext.bHasCoordRefSysAtFeatureLevel)
518 : {
519 : // No coordRefSys member found anywhere ? Fallback to WGS 84
520 81 : poSRSLayer = poSRSWGS84.get();
521 81 : oBuildContext.bLayerCRSIsWGS84 = true;
522 : }
523 104 : else if (poSRSLayer && poSRSLayer->IsSame(poSRSWGS84.get()))
524 : {
525 8 : oBuildContext.bLayerCRSIsWGS84 = true;
526 : }
527 96 : else if (poSRSLayer)
528 : {
529 95 : const char *pszAuthName = poSRSLayer->GetAuthorityName(nullptr);
530 95 : const char *pszAuthCode = poSRSLayer->GetAuthorityCode(nullptr);
531 95 : if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "OGC") &&
532 27 : EQUAL(pszAuthCode, "CRS84"))
533 : {
534 : // Normalize reported CRS to EPSG:4326
535 15 : poSRSLayer = poSRSWGS84.get();
536 15 : oBuildContext.bLayerCRSIsWGS84 = true;
537 : }
538 80 : else if (!(pszAuthName && STARTS_WITH(pszAuthName, "IAU")))
539 : {
540 80 : oBuildContext.poCTWGS84ToLayerCRS.reset(
541 80 : OGRCreateCoordinateTransformation(poSRSWGS84.get(),
542 : poSRSLayer));
543 : }
544 : }
545 : }
546 :
547 207 : std::unique_ptr<OGRJSONFGMemLayer> poMemLayer;
548 207 : std::unique_ptr<OGRJSONFGStreamedLayer> poStreamedLayer;
549 : OGRLayer *poLayer;
550 207 : if (bStreamedLayer)
551 : {
552 130 : poStreamedLayer = std::make_unique<OGRJSONFGStreamedLayer>(
553 130 : poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
554 130 : poLayer = poStreamedLayer.get();
555 : }
556 : else
557 : {
558 77 : poMemLayer = std::make_unique<OGRJSONFGMemLayer>(
559 77 : poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
560 77 : poLayer = poMemLayer.get();
561 : }
562 :
563 : // Note: the current strategy will not produce stable output, depending
564 : // on the order of features, if there are conflicting order / cycles.
565 : // See https://github.com/OSGeo/gdal/pull/4552 for a number of potential
566 : // resolutions if that has to be solved in the future.
567 207 : OGRFeatureDefn *poLayerDefn = poLayer->GetLayerDefn();
568 414 : auto oTemporaryUnsealer(poLayerDefn->GetTemporaryUnsealer());
569 :
570 207 : if (poLayer->GetLayerDefn()->GetGeomType() != wkbNone)
571 : {
572 414 : OGRGeoJSONWriteOptions options;
573 :
574 207 : json_object *poXYRes = CPL_json_object_object_get(
575 : poObject_, "xy_coordinate_resolution_place");
576 207 : if (poXYRes && (json_object_get_type(poXYRes) == json_type_double ||
577 0 : json_object_get_type(poXYRes) == json_type_int))
578 : {
579 2 : auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
580 : OGRGeomCoordinatePrecision oCoordPrec(
581 4 : poGeomFieldDefn->GetCoordinatePrecision());
582 2 : oCoordPrec.dfXYResolution = json_object_get_double(poXYRes);
583 2 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
584 : }
585 :
586 207 : json_object *poZRes = CPL_json_object_object_get(
587 : poObject_, "z_coordinate_resolution_place");
588 207 : if (poZRes && (json_object_get_type(poZRes) == json_type_double ||
589 0 : json_object_get_type(poZRes) == json_type_int))
590 : {
591 2 : auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
592 : OGRGeomCoordinatePrecision oCoordPrec(
593 4 : poGeomFieldDefn->GetCoordinatePrecision());
594 2 : oCoordPrec.dfZResolution = json_object_get_double(poZRes);
595 2 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
596 : }
597 : }
598 :
599 414 : std::set<std::string> oSetFieldNames;
600 330 : for (const auto &poFieldDefn : oBuildContext.apoFieldDefn)
601 123 : oSetFieldNames.insert(poFieldDefn->GetNameRef());
602 :
603 : auto AddTimeField =
604 88 : [poLayerDefn, &oSetFieldNames](const char *pszName, OGRFieldType eType)
605 : {
606 22 : if (oSetFieldNames.find(pszName) == oSetFieldNames.end())
607 : {
608 42 : OGRFieldDefn oFieldDefn(pszName, eType);
609 21 : poLayerDefn->AddFieldDefn(&oFieldDefn);
610 : }
611 : else
612 : {
613 2 : OGRFieldDefn oFieldDefn((std::string("jsonfg_") + pszName).c_str(),
614 2 : eType);
615 1 : poLayerDefn->AddFieldDefn(&oFieldDefn);
616 : }
617 22 : return poLayerDefn->GetFieldCount() - 1;
618 207 : };
619 :
620 207 : if (oBuildContext.bHasTimeTimestamp)
621 : {
622 2 : oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDateTime);
623 : }
624 205 : else if (oBuildContext.bHasTimeDate)
625 : {
626 2 : oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDate);
627 : }
628 :
629 207 : if (oBuildContext.bHasTimeIntervalStartDate ||
630 204 : oBuildContext.bHasTimeIntervalStartTimestamp ||
631 200 : oBuildContext.bHasTimeIntervalEndDate ||
632 199 : oBuildContext.bHasTimeIntervalEndTimestamp)
633 : {
634 : // Mix of Date/DateTime for start/end is not supposed to happen,
635 : // but be tolerant to that
636 9 : if (oBuildContext.bHasTimeIntervalStartTimestamp)
637 : {
638 5 : oBuildContext.nIdxFieldTimeStart =
639 5 : AddTimeField("time_start", OFTDateTime);
640 : }
641 4 : else if (oBuildContext.bHasTimeIntervalStartDate)
642 : {
643 2 : oBuildContext.nIdxFieldTimeStart =
644 2 : AddTimeField("time_start", OFTDate);
645 : }
646 2 : else if (oBuildContext.bHasTimeIntervalEndTimestamp)
647 : {
648 1 : oBuildContext.nIdxFieldTimeStart =
649 1 : AddTimeField("time_start", OFTDateTime);
650 : }
651 : else /* if( oBuildContext.bHasTimeIntervalEndDate ) */
652 : {
653 1 : oBuildContext.nIdxFieldTimeStart =
654 1 : AddTimeField("time_start", OFTDate);
655 : }
656 :
657 9 : if (oBuildContext.bHasTimeIntervalEndTimestamp)
658 : {
659 3 : oBuildContext.nIdxFieldTimeEnd =
660 3 : AddTimeField("time_end", OFTDateTime);
661 : }
662 6 : else if (oBuildContext.bHasTimeIntervalEndDate)
663 : {
664 2 : oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
665 : }
666 4 : else if (oBuildContext.bHasTimeIntervalStartTimestamp)
667 : {
668 3 : oBuildContext.nIdxFieldTimeEnd =
669 3 : AddTimeField("time_end", OFTDateTime);
670 : }
671 : else /* if( oBuildContext.bHasTimeIntervalStartDate ) */
672 : {
673 1 : oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
674 : }
675 : }
676 :
677 414 : const auto sortedFields = oBuildContext.dag.getTopologicalOrdering();
678 207 : CPLAssert(sortedFields.size() == oBuildContext.apoFieldDefn.size());
679 330 : for (int idx : sortedFields)
680 : {
681 123 : poLayerDefn->AddFieldDefn(oBuildContext.apoFieldDefn[idx].get());
682 : }
683 :
684 207 : if (!oBuildContext.bFeatureLevelIdAsFID)
685 : {
686 175 : const int idx = poLayerDefn->GetFieldIndexCaseSensitive("id");
687 175 : if (idx >= 0)
688 : {
689 2 : OGRFieldDefn *poFDefn = poLayerDefn->GetFieldDefn(idx);
690 4 : if (poFDefn->GetType() == OFTInteger ||
691 2 : poFDefn->GetType() == OFTInteger64)
692 : {
693 0 : if (poStreamedLayer)
694 : {
695 0 : poStreamedLayer->SetFIDColumn(
696 0 : poLayerDefn->GetFieldDefn(idx)->GetNameRef());
697 : }
698 : else
699 : {
700 0 : poMemLayer->SetFIDColumn(
701 0 : poLayerDefn->GetFieldDefn(idx)->GetNameRef());
702 : }
703 : }
704 : }
705 : }
706 :
707 207 : if (oBuildContext.bNeedFID64)
708 0 : poLayer->SetMetadataItem(OLMD_FID64, "YES");
709 :
710 414 : if (oBuildContext.bSameMeasureMetadata &&
711 207 : (!oBuildContext.osMeasureUnit.empty() ||
712 205 : !oBuildContext.osMeasureDescription.empty()))
713 : {
714 2 : if (!oBuildContext.osMeasureUnit.empty())
715 : {
716 2 : poLayer->SetMetadataItem(
717 2 : "UNIT", oBuildContext.osMeasureUnit.c_str(), "MEASURES");
718 : }
719 :
720 2 : if (!oBuildContext.osMeasureDescription.empty())
721 : {
722 2 : poLayer->SetMetadataItem("DESCRIPTION",
723 : oBuildContext.osMeasureDescription.c_str(),
724 2 : "MEASURES");
725 : }
726 : }
727 : else
728 : {
729 205 : if (!osMeasureUnit_.empty())
730 : {
731 4 : poLayer->SetMetadataItem("UNIT", osMeasureUnit_.c_str(),
732 4 : "MEASURES");
733 : }
734 :
735 205 : if (!osMeasureDescription_.empty())
736 : {
737 4 : poLayer->SetMetadataItem("DESCRIPTION",
738 4 : osMeasureDescription_.c_str(), "MEASURES");
739 : }
740 : }
741 :
742 207 : if (poStreamedLayer)
743 : {
744 130 : poStreamedLayer->SetFeatureCount(oBuildContext.nFeatureCount);
745 130 : oBuildContext.poStreamedLayer =
746 130 : poDS_->AddLayer(std::move(poStreamedLayer));
747 : }
748 : else
749 : {
750 77 : oBuildContext.poMemLayer = poDS_->AddLayer(std::move(poMemLayer));
751 : }
752 207 : }
753 :
754 : /************************************************************************/
755 : /* OGRJSONFGReader::GetLayerNameForFeature() */
756 : /************************************************************************/
757 :
758 952 : const char *OGRJSONFGReader::GetLayerNameForFeature(json_object *poObj) const
759 : {
760 952 : const char *pszName = osDefaultLayerName_.c_str();
761 952 : json_object *poName = CPL_json_object_object_get(poObj, "featureType");
762 : // The spec allows an array of strings, but we don't support that
763 952 : if (poName != nullptr && json_object_get_type(poName) == json_type_string)
764 : {
765 197 : pszName = json_object_get_string(poName);
766 : }
767 952 : return pszName;
768 : }
769 :
770 : /************************************************************************/
771 : /* OGRJSONFGGetOGRGeometryType() */
772 : /************************************************************************/
773 :
774 180 : static OGRwkbGeometryType OGRJSONFGGetOGRGeometryType(json_object *poObj,
775 : bool bHasM)
776 : {
777 180 : const auto eType = OGRGeoJSONGetOGRGeometryType(poObj, bHasM);
778 180 : if (eType != wkbUnknown)
779 174 : return eType;
780 :
781 6 : json_object *poObjType = CPL_json_object_object_get(poObj, "type");
782 6 : const char *pszType = json_object_get_string(poObjType);
783 6 : if (!pszType)
784 0 : return wkbNone;
785 :
786 6 : if (strcmp(pszType, "Polyhedron") == 0)
787 : {
788 3 : auto eRetType = wkbPolyhedralSurfaceZ;
789 :
790 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
791 :
792 3 : if (bHasM)
793 0 : eRetType = OGR_GT_SetM(eRetType);
794 3 : return eRetType;
795 : }
796 3 : else if (strcmp(pszType, "Prism") == 0)
797 : {
798 3 : auto poBase = CPL_json_object_object_get(poObj, "base");
799 3 : if (!poBase || json_object_get_type(poBase) != json_type_object)
800 : {
801 0 : return wkbNone;
802 : }
803 :
804 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
805 :
806 3 : const auto eBaseGeomType = OGRGeoJSONGetOGRGeometryType(poBase, bHasM);
807 3 : auto eRetType = wkbNone;
808 3 : if (eBaseGeomType == wkbPoint)
809 : {
810 1 : eRetType = wkbLineString25D;
811 : }
812 2 : else if (eBaseGeomType == wkbLineString)
813 : {
814 1 : eRetType = wkbMultiPolygon25D;
815 : }
816 1 : else if (eBaseGeomType == wkbPolygon)
817 : {
818 1 : eRetType = wkbPolyhedralSurfaceZ;
819 : }
820 3 : if (eRetType != wkbNone)
821 : {
822 3 : if (bHasM)
823 0 : eRetType = OGR_GT_SetM(eRetType);
824 3 : return eRetType;
825 : }
826 : }
827 0 : return wkbNone;
828 : }
829 :
830 : /************************************************************************/
831 : /* OGRJSONFGCreateNonGeoJSONGeometry() */
832 : /************************************************************************/
833 :
834 : static std::unique_ptr<OGRGeometry>
835 6 : OGRJSONFGCreateNonGeoJSONGeometry(json_object *poObj, bool bHasM, bool bWarn)
836 : {
837 6 : json_object *poObjType = CPL_json_object_object_get(poObj, "type");
838 6 : const char *pszType = json_object_get_string(poObjType);
839 6 : if (!pszType)
840 0 : return nullptr;
841 :
842 6 : if (strcmp(pszType, "Polyhedron") == 0)
843 : {
844 3 : auto poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
845 6 : if (!poCoordinates ||
846 3 : json_object_get_type(poCoordinates) != json_type_array)
847 : {
848 0 : CPLError(CE_Failure, CPLE_AppDefined,
849 : "Missing or invalid coordinates in Polyhedron");
850 0 : return nullptr;
851 : }
852 3 : if (json_object_array_length(poCoordinates) != 1)
853 : {
854 0 : if (bWarn)
855 : {
856 0 : CPLError(CE_Warning, CPLE_AppDefined,
857 : "Polyhedron with inner shells not supported");
858 : }
859 0 : return nullptr;
860 : }
861 :
862 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
863 :
864 3 : auto poJOuterShell = json_object_array_get_idx(poCoordinates, 0);
865 6 : auto poGeom = std::make_unique<OGRPolyhedralSurface>();
866 3 : const auto nPolys = json_object_array_length(poJOuterShell);
867 8 : for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
868 : {
869 5 : auto poJPoly = json_object_array_get_idx(poJOuterShell, i);
870 5 : if (!poJPoly)
871 0 : return nullptr;
872 : auto poPoly =
873 5 : OGRGeoJSONReadPolygon(poJPoly, bHasM, /*bRaw = */ true);
874 5 : if (!poPoly)
875 0 : return nullptr;
876 5 : if (poGeom->addGeometry(std::move(poPoly)) != OGRERR_NONE)
877 0 : return nullptr;
878 : }
879 3 : if (nPolys == 0)
880 1 : poGeom->set3D(true);
881 :
882 3 : return poGeom;
883 : }
884 3 : else if (strcmp(pszType, "Prism") == 0)
885 : {
886 3 : auto poBase = CPL_json_object_object_get(poObj, "base");
887 3 : if (!poBase || json_object_get_type(poBase) != json_type_object)
888 : {
889 0 : CPLError(CE_Failure, CPLE_AppDefined,
890 : "Missing or invalid base in Prism");
891 0 : return nullptr;
892 : }
893 :
894 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
895 :
896 3 : json_object *poLower = CPL_json_object_object_get(poObj, "lower");
897 3 : const double dfLower = poLower ? json_object_get_double(poLower) : 0.0;
898 3 : json_object *poUpper = CPL_json_object_object_get(poObj, "upper");
899 3 : const double dfUpper = poUpper ? json_object_get_double(poUpper) : 0.0;
900 :
901 : auto poBaseGeom = std::unique_ptr<OGRGeometry>(OGRGeoJSONReadGeometry(
902 6 : poBase, bHasM, /* OGRSpatialReference* = */ nullptr));
903 3 : if (!poBaseGeom)
904 0 : return nullptr;
905 3 : const auto eBaseGeomType = poBaseGeom->getGeometryType();
906 3 : if (eBaseGeomType == wkbPoint)
907 : {
908 1 : const auto poPoint = poBaseGeom.get()->toPoint();
909 2 : auto poGeom = std::make_unique<OGRLineString>();
910 1 : if (bHasM)
911 : {
912 0 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfLower,
913 : poPoint->getM());
914 0 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfUpper,
915 : poPoint->getM());
916 : }
917 : else
918 : {
919 1 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfLower);
920 1 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfUpper);
921 : }
922 1 : return poGeom;
923 : }
924 2 : else if (eBaseGeomType == wkbLineString)
925 : {
926 1 : const auto poLS = poBaseGeom.get()->toLineString();
927 2 : auto poGeom = std::make_unique<OGRMultiPolygon>();
928 2 : for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
929 : {
930 1 : auto poPoly = new OGRPolygon();
931 1 : auto poRing = new OGRLinearRing();
932 1 : if (bHasM)
933 : {
934 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
935 : poLS->getM(i));
936 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
937 : dfLower, poLS->getM(i + 1));
938 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
939 : dfUpper, poLS->getM(i + 1));
940 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper,
941 : poLS->getM(i));
942 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
943 : poLS->getM(i));
944 : }
945 : else
946 : {
947 1 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
948 1 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
949 : dfLower);
950 1 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
951 : dfUpper);
952 1 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
953 1 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
954 : }
955 1 : poPoly->addRingDirectly(poRing);
956 1 : poGeom->addGeometryDirectly(poPoly);
957 : }
958 1 : return poGeom;
959 : }
960 1 : else if (eBaseGeomType == wkbPolygon)
961 : {
962 1 : const auto poBasePoly = poBaseGeom.get()->toPolygon();
963 1 : if (poBasePoly->getNumInteriorRings() > 0)
964 : {
965 0 : if (bWarn)
966 : {
967 0 : CPLError(CE_Warning, CPLE_AppDefined,
968 : "Polygon with holes is not supported as the base "
969 : "for Prism");
970 : }
971 0 : return nullptr;
972 : }
973 1 : const auto poLS = poBasePoly->getExteriorRing();
974 1 : if (poLS == nullptr)
975 : {
976 0 : return nullptr;
977 : }
978 2 : auto poGeom = std::make_unique<OGRPolyhedralSurface>();
979 : // Build lower face
980 : {
981 1 : auto poPoly = new OGRPolygon();
982 1 : auto poRing = new OGRLinearRing();
983 5 : for (int i = 0; i < poLS->getNumPoints(); ++i)
984 : {
985 4 : if (bHasM)
986 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
987 : poLS->getM(i));
988 : else
989 4 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
990 : }
991 1 : poPoly->addRingDirectly(poRing);
992 1 : poGeom->addGeometryDirectly(poPoly);
993 : }
994 : // Build side faces
995 4 : for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
996 : {
997 3 : auto poPoly = new OGRPolygon();
998 3 : auto poRing = new OGRLinearRing();
999 3 : if (bHasM)
1000 : {
1001 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
1002 : poLS->getM(i));
1003 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
1004 : dfLower, poLS->getM(i + 1));
1005 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
1006 : dfUpper, poLS->getM(i + 1));
1007 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper,
1008 : poLS->getM(i));
1009 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
1010 : poLS->getM(i));
1011 : }
1012 : else
1013 : {
1014 3 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
1015 3 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
1016 : dfLower);
1017 3 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
1018 : dfUpper);
1019 3 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
1020 3 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
1021 : }
1022 3 : poPoly->addRingDirectly(poRing);
1023 3 : poGeom->addGeometryDirectly(poPoly);
1024 : }
1025 : // Build upper face
1026 : {
1027 1 : auto poPoly = new OGRPolygon();
1028 1 : auto poRing = new OGRLinearRing();
1029 5 : for (int i = 0; i < poLS->getNumPoints(); ++i)
1030 : {
1031 4 : if (bHasM)
1032 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper,
1033 : poLS->getM(i));
1034 : else
1035 4 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
1036 : }
1037 1 : poPoly->addRingDirectly(poRing);
1038 1 : poGeom->addGeometryDirectly(poPoly);
1039 : }
1040 1 : return poGeom;
1041 : }
1042 : else
1043 : {
1044 0 : if (bWarn)
1045 : {
1046 0 : CPLError(CE_Warning, CPLE_AppDefined,
1047 : "Unsupported base geometry type for Prism");
1048 : }
1049 0 : return nullptr;
1050 : }
1051 : }
1052 : else
1053 : {
1054 0 : if (bWarn)
1055 : {
1056 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unhandled place.type = %s",
1057 : pszType);
1058 : }
1059 0 : return nullptr;
1060 : }
1061 : }
1062 :
1063 : /************************************************************************/
1064 : /* OGRJSONFGReader::GenerateLayerDefnFromFeature() */
1065 : /************************************************************************/
1066 :
1067 280 : bool OGRJSONFGReader::GenerateLayerDefnFromFeature(json_object *poObj)
1068 : {
1069 280 : const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
1070 280 : if (objType != GeoJSONObject::eFeature)
1071 : {
1072 0 : CPLError(CE_Failure, CPLE_AppDefined, "Did not get a Feature");
1073 0 : return false;
1074 : }
1075 :
1076 : const bool bHasM =
1077 280 : OGRJSONFGHasMeasure(poObj, /* bUpperLevelMValue = */ false);
1078 :
1079 280 : const char *psLayerName = GetLayerNameForFeature(poObj);
1080 :
1081 280 : auto oBuildContextIter = oMapBuildContext_.find(psLayerName);
1082 280 : if (oBuildContextIter == oMapBuildContext_.end())
1083 : {
1084 207 : LayerDefnBuildContext oContext;
1085 207 : oMapBuildContext_[psLayerName] = std::move(oContext);
1086 207 : oBuildContextIter = oMapBuildContext_.find(psLayerName);
1087 : }
1088 280 : LayerDefnBuildContext *poContext = &(oBuildContextIter->second);
1089 :
1090 280 : ++poContext->nFeatureCount;
1091 :
1092 280 : json_object *poCoordRefSys = nullptr;
1093 280 : json_object *poPlace = nullptr;
1094 280 : if (eGeometryElement_ != GeometryElement::GEOMETRY)
1095 : {
1096 277 : poPlace = CPL_json_object_object_get(poObj, "place");
1097 277 : if (poPlace && json_object_get_type(poPlace) == json_type_object)
1098 : {
1099 180 : poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
1100 : }
1101 277 : if (!poCoordRefSys)
1102 251 : poCoordRefSys = CPL_json_object_object_get(poObj, "coordRefSys");
1103 :
1104 277 : if (poCoordRefSys)
1105 : {
1106 140 : std::string osVal = json_object_to_json_string(poCoordRefSys);
1107 70 : if (!poContext->bHasCoordRefSysAtFeatureLevel)
1108 : {
1109 66 : poContext->bHasCoordRefSysAtFeatureLevel = true;
1110 66 : poContext->osCoordRefSysAtFeatureLevel = std::move(osVal);
1111 : poContext->poCRSAtFeatureLevel =
1112 66 : OGRJSONFGReadCoordRefSys(poCoordRefSys);
1113 66 : if (poContext->poCRSAtFeatureLevel)
1114 : {
1115 66 : poContext->poCRSAtFeatureLevel->SetAxisMappingStrategy(
1116 : OAMS_TRADITIONAL_GIS_ORDER);
1117 : }
1118 : }
1119 4 : else if (poContext->osCoordRefSysAtFeatureLevel != osVal)
1120 : {
1121 2 : poContext->osCoordRefSysAtFeatureLevel.clear();
1122 2 : poContext->poCRSAtFeatureLevel.reset();
1123 : }
1124 : }
1125 : }
1126 :
1127 280 : if (poContext->bSameMeasureMetadata)
1128 : {
1129 280 : json_object *poMeasures = nullptr;
1130 295 : if (json_object_object_get_ex(poObj, "measures", &poMeasures) &&
1131 15 : json_object_get_type(poMeasures) == json_type_object)
1132 : {
1133 15 : json_object *poEnabled = nullptr;
1134 15 : if (json_object_object_get_ex(poMeasures, "enabled", &poEnabled) &&
1135 30 : json_object_get_type(poEnabled) == json_type_boolean &&
1136 15 : json_object_get_boolean(poEnabled))
1137 : {
1138 15 : json_object *poUnit = nullptr;
1139 17 : if (json_object_object_get_ex(poMeasures, "unit", &poUnit) &&
1140 2 : json_object_get_type(poUnit) == json_type_string)
1141 : {
1142 2 : if (poContext->osMeasureUnit.empty())
1143 : poContext->osMeasureUnit =
1144 2 : json_object_get_string(poUnit);
1145 0 : else if (poContext->osMeasureUnit !=
1146 : json_object_get_string(poUnit))
1147 0 : poContext->bSameMeasureMetadata = false;
1148 : }
1149 :
1150 15 : json_object *poDescription = nullptr;
1151 15 : if (json_object_object_get_ex(poMeasures, "description",
1152 17 : &poDescription) &&
1153 2 : json_object_get_type(poDescription) == json_type_string)
1154 : {
1155 2 : if (poContext->osMeasureDescription.empty())
1156 : poContext->osMeasureDescription =
1157 2 : json_object_get_string(poDescription);
1158 0 : else if (poContext->osMeasureDescription !=
1159 : json_object_get_string(poDescription))
1160 0 : poContext->bSameMeasureMetadata = false;
1161 : }
1162 : }
1163 : }
1164 : }
1165 :
1166 : /* -------------------------------------------------------------------- */
1167 : /* Deal with place / geometry */
1168 : /* -------------------------------------------------------------------- */
1169 :
1170 280 : if (poContext->bDetectLayerGeomType)
1171 : {
1172 280 : bool bFallbackToGeometry =
1173 280 : (eGeometryElement_ != GeometryElement::PLACE);
1174 280 : if (poPlace && json_object_get_type(poPlace) == json_type_object)
1175 : {
1176 180 : const auto eType = OGRJSONFGGetOGRGeometryType(poPlace, bHasM);
1177 180 : if (eType != wkbNone)
1178 : {
1179 180 : bFallbackToGeometry = false;
1180 180 : poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
1181 180 : poContext->bFirstGeometry, eType,
1182 180 : poContext->eLayerGeomType);
1183 : }
1184 : }
1185 :
1186 280 : if (bFallbackToGeometry)
1187 : {
1188 : json_object *poGeomObj =
1189 99 : CPL_json_object_object_get(poObj, "geometry");
1190 115 : if (poGeomObj &&
1191 16 : json_object_get_type(poGeomObj) == json_type_object)
1192 : {
1193 : const auto eType =
1194 16 : OGRGeoJSONGetOGRGeometryType(poGeomObj, bHasM);
1195 16 : poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
1196 16 : poContext->bFirstGeometry, eType,
1197 16 : poContext->eLayerGeomType);
1198 : }
1199 : }
1200 : }
1201 :
1202 : /* -------------------------------------------------------------------- */
1203 : /* Deal with time */
1204 : /* -------------------------------------------------------------------- */
1205 280 : json_object *poTime = CPL_json_object_object_get(poObj, "time");
1206 280 : if (poTime)
1207 : {
1208 15 : json_object *poDate = CPL_json_object_object_get(poTime, "date");
1209 15 : if (poDate && json_object_get_type(poDate) == json_type_string)
1210 3 : poContext->bHasTimeDate = true;
1211 :
1212 : json_object *poTimestamp =
1213 15 : CPL_json_object_object_get(poTime, "timestamp");
1214 17 : if (poTimestamp &&
1215 2 : json_object_get_type(poTimestamp) == json_type_string)
1216 2 : poContext->bHasTimeTimestamp = true;
1217 :
1218 : json_object *poInterval =
1219 15 : CPL_json_object_object_get(poTime, "interval");
1220 25 : if (poInterval && json_object_get_type(poInterval) == json_type_array &&
1221 10 : json_object_array_length(poInterval) == 2)
1222 : {
1223 10 : json_object *poStart = json_object_array_get_idx(poInterval, 0);
1224 10 : if (poStart && json_object_get_type(poStart) == json_type_string)
1225 : {
1226 10 : const char *pszStart = json_object_get_string(poStart);
1227 10 : if (strchr(pszStart, 'Z'))
1228 5 : poContext->bHasTimeIntervalStartTimestamp = true;
1229 5 : else if (strcmp(pszStart, "..") != 0)
1230 3 : poContext->bHasTimeIntervalStartDate = true;
1231 : }
1232 :
1233 10 : json_object *poEnd = json_object_array_get_idx(poInterval, 1);
1234 10 : if (poEnd && json_object_get_type(poEnd) == json_type_string)
1235 : {
1236 10 : const char *pszEnd = json_object_get_string(poEnd);
1237 10 : if (strchr(pszEnd, 'Z'))
1238 3 : poContext->bHasTimeIntervalEndTimestamp = true;
1239 7 : else if (strcmp(pszEnd, "..") != 0)
1240 3 : poContext->bHasTimeIntervalEndDate = true;
1241 : }
1242 : }
1243 : }
1244 :
1245 : /* -------------------------------------------------------------------- */
1246 : /* Read collection of properties. */
1247 : /* -------------------------------------------------------------------- */
1248 280 : json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
1249 :
1250 280 : int nPrevFieldIdx = -1;
1251 :
1252 : // First deal with id, either at top level or in properties["id"]
1253 280 : OGRGeoJSONGenerateFeatureDefnDealWithID(
1254 280 : poObj, poObjProps, nPrevFieldIdx, poContext->oMapFieldNameToIdx,
1255 280 : poContext->apoFieldDefn, poContext->dag,
1256 280 : poContext->bFeatureLevelIdAsFID, poContext->bFeatureLevelIdAsAttribute,
1257 280 : poContext->bNeedFID64);
1258 :
1259 532 : if (nullptr != poObjProps &&
1260 252 : json_object_get_type(poObjProps) == json_type_object)
1261 : {
1262 : json_object_iter it;
1263 252 : it.key = nullptr;
1264 252 : it.val = nullptr;
1265 252 : it.entry = nullptr;
1266 504 : std::vector<int> anCurFieldIndices;
1267 590 : json_object_object_foreachC(poObjProps, it)
1268 : {
1269 338 : anCurFieldIndices.clear();
1270 338 : OGRGeoJSONReaderAddOrUpdateField(
1271 338 : anCurFieldIndices, poContext->oMapFieldNameToIdx,
1272 338 : poContext->apoFieldDefn, it.key, it.val,
1273 338 : bFlattenNestedAttributes_, chNestedAttributeSeparator_,
1274 338 : bArrayAsString_, bDateAsString_,
1275 338 : poContext->aoSetUndeterminedTypeFields);
1276 676 : for (int idx : anCurFieldIndices)
1277 : {
1278 676 : poContext->dag.addNode(
1279 338 : idx, poContext->apoFieldDefn[idx]->GetNameRef());
1280 338 : if (nPrevFieldIdx != -1)
1281 : {
1282 241 : poContext->dag.addEdge(nPrevFieldIdx, idx);
1283 : }
1284 338 : nPrevFieldIdx = idx;
1285 : }
1286 : }
1287 : }
1288 :
1289 280 : return true;
1290 : }
1291 :
1292 : /************************************************************************/
1293 : /* OGRJSONFGReader::ReadFeature() */
1294 : /************************************************************************/
1295 :
1296 : std::unique_ptr<OGRFeature>
1297 672 : OGRJSONFGReader::ReadFeature(json_object *poObj, const char *pszRequestedLayer,
1298 : bool bHasM, OGRJSONFGMemLayer **pOutMemLayer,
1299 : OGRJSONFGStreamedLayer **pOutStreamedLayer)
1300 : {
1301 672 : const char *pszLayerName = GetLayerNameForFeature(poObj);
1302 672 : if (pszRequestedLayer && strcmp(pszLayerName, pszRequestedLayer) != 0)
1303 3 : return nullptr;
1304 :
1305 669 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
1306 :
1307 669 : auto oBuildContextIter = oMapBuildContext_.find(pszLayerName);
1308 669 : CPLAssert(oBuildContextIter != oMapBuildContext_.end());
1309 669 : auto &oBuildContext = oBuildContextIter->second;
1310 669 : OGRLayer *poLayer =
1311 669 : oBuildContext.poStreamedLayer
1312 669 : ? static_cast<OGRLayer *>(oBuildContext.poStreamedLayer)
1313 : : static_cast<OGRLayer *>(oBuildContext.poMemLayer);
1314 :
1315 669 : if (pOutMemLayer)
1316 79 : *pOutMemLayer = oBuildContext.poMemLayer;
1317 590 : else if (pOutStreamedLayer)
1318 590 : *pOutStreamedLayer = oBuildContext.poStreamedLayer;
1319 :
1320 669 : OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
1321 1338 : auto poFeature = std::make_unique<OGRFeature>(poFDefn);
1322 :
1323 : /* -------------------------------------------------------------------- */
1324 : /* Translate GeoJSON "properties" object to feature attributes. */
1325 : /* -------------------------------------------------------------------- */
1326 :
1327 669 : json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
1328 1310 : if (nullptr != poObjProps &&
1329 641 : json_object_get_type(poObjProps) == json_type_object)
1330 : {
1331 : json_object_iter it;
1332 641 : it.key = nullptr;
1333 641 : it.val = nullptr;
1334 641 : it.entry = nullptr;
1335 2205 : json_object_object_foreachC(poObjProps, it)
1336 : {
1337 1564 : const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
1338 1564 : if (nField < 0 &&
1339 0 : !(bFlattenNestedAttributes_ && it.val != nullptr &&
1340 0 : json_object_get_type(it.val) == json_type_object))
1341 : {
1342 0 : CPLDebug("JSONFG", "Cannot find field %s", it.key);
1343 : }
1344 : else
1345 : {
1346 1564 : OGRGeoJSONReaderSetField(
1347 1564 : poLayer, poFeature.get(), nField, it.key, it.val,
1348 1564 : bFlattenNestedAttributes_, chNestedAttributeSeparator_);
1349 : }
1350 : }
1351 : }
1352 :
1353 : /* -------------------------------------------------------------------- */
1354 : /* Try to use feature-level ID if available */
1355 : /* and of integral type. Otherwise, leave unset (-1) then index */
1356 : /* in features sequence will be used as FID. */
1357 : /* -------------------------------------------------------------------- */
1358 669 : json_object *poObjId = CPL_json_object_object_get(poObj, "id");
1359 669 : if (nullptr != poObjId && oBuildContext.bFeatureLevelIdAsFID)
1360 : {
1361 32 : poFeature->SetFID(static_cast<GIntBig>(json_object_get_int64(poObjId)));
1362 : }
1363 :
1364 : /* -------------------------------------------------------------------- */
1365 : /* Handle the case where the special id is in a regular field. */
1366 : /* -------------------------------------------------------------------- */
1367 637 : else if (nullptr != poObjId)
1368 : {
1369 2 : const int nIdx = poFDefn->GetFieldIndexCaseSensitive("id");
1370 2 : if (nIdx >= 0 && !poFeature->IsFieldSet(nIdx))
1371 : {
1372 2 : poFeature->SetField(nIdx, json_object_get_string(poObjId));
1373 : }
1374 : }
1375 :
1376 : /* -------------------------------------------------------------------- */
1377 : /* Deal with time */
1378 : /* -------------------------------------------------------------------- */
1379 669 : json_object *poTime = CPL_json_object_object_get(poObj, "time");
1380 669 : if (poTime)
1381 : {
1382 15 : json_object *poDate = CPL_json_object_object_get(poTime, "date");
1383 15 : if (poDate && json_object_get_type(poDate) == json_type_string)
1384 : {
1385 3 : poFeature->SetField(oBuildContext.nIdxFieldTime,
1386 : json_object_get_string(poDate));
1387 : }
1388 :
1389 : json_object *poTimestamp =
1390 15 : CPL_json_object_object_get(poTime, "timestamp");
1391 17 : if (poTimestamp &&
1392 2 : json_object_get_type(poTimestamp) == json_type_string)
1393 : {
1394 2 : poFeature->SetField(oBuildContext.nIdxFieldTime,
1395 : json_object_get_string(poTimestamp));
1396 : }
1397 :
1398 : json_object *poInterval =
1399 15 : CPL_json_object_object_get(poTime, "interval");
1400 25 : if (poInterval && json_object_get_type(poInterval) == json_type_array &&
1401 10 : json_object_array_length(poInterval) == 2)
1402 : {
1403 10 : json_object *poStart = json_object_array_get_idx(poInterval, 0);
1404 10 : if (poStart && json_object_get_type(poStart) == json_type_string)
1405 : {
1406 10 : const char *pszStart = json_object_get_string(poStart);
1407 10 : if (strcmp(pszStart, "..") != 0)
1408 8 : poFeature->SetField(oBuildContext.nIdxFieldTimeStart,
1409 : pszStart);
1410 : }
1411 :
1412 10 : json_object *poEnd = json_object_array_get_idx(poInterval, 1);
1413 10 : if (poEnd && json_object_get_type(poEnd) == json_type_string)
1414 : {
1415 10 : const char *pszEnd = json_object_get_string(poEnd);
1416 10 : if (strcmp(pszEnd, "..") != 0)
1417 6 : poFeature->SetField(oBuildContext.nIdxFieldTimeEnd, pszEnd);
1418 : }
1419 : }
1420 : }
1421 :
1422 : /* -------------------------------------------------------------------- */
1423 : /* Translate "place" (and fallback to "geometry") sub-object */
1424 : /* -------------------------------------------------------------------- */
1425 669 : json_object *poPlace = nullptr;
1426 669 : bool bFallbackToGeometry = (eGeometryElement_ != GeometryElement::PLACE);
1427 :
1428 669 : if (eGeometryElement_ != GeometryElement::GEOMETRY)
1429 : {
1430 666 : poPlace = CPL_json_object_object_get(poObj, "place");
1431 : }
1432 669 : if (poPlace && json_object_get_type(poPlace) == json_type_object)
1433 : {
1434 609 : bHasM = OGRJSONFGHasMeasure(poPlace, bHasM);
1435 609 : json_object *poCoordRefSys = nullptr;
1436 609 : if (!oBuildContext.poCRSAtFeatureLevel)
1437 : {
1438 552 : poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
1439 552 : if (!poCoordRefSys)
1440 : {
1441 : poCoordRefSys =
1442 552 : CPL_json_object_object_get(poObj, "coordRefSys");
1443 : }
1444 : }
1445 :
1446 609 : std::unique_ptr<OGRGeometry> poGeometry;
1447 609 : json_object *poObjType = CPL_json_object_object_get(poPlace, "type");
1448 609 : const char *pszType = json_object_get_string(poObjType);
1449 609 : if (pszType && (strcmp(pszType, "Polyhedron") == 0 ||
1450 606 : strcmp(pszType, "Prism") == 0))
1451 : {
1452 12 : poGeometry = OGRJSONFGCreateNonGeoJSONGeometry(poPlace, bHasM,
1453 6 : /* bWarn=*/false);
1454 : }
1455 : else
1456 : {
1457 603 : poGeometry = OGRGeoJSONReadGeometry(poPlace, bHasM, nullptr);
1458 : }
1459 609 : if (poGeometry)
1460 605 : bFallbackToGeometry = false;
1461 :
1462 609 : auto poLayerSRS = poLayer->GetSpatialRef();
1463 609 : if (!poGeometry)
1464 : {
1465 : // nothing to do
1466 : }
1467 605 : else if (poCoordRefSys)
1468 : {
1469 8 : auto poFeatureCRS = OGRJSONFGReadCoordRefSys(poCoordRefSys);
1470 4 : if (poFeatureCRS)
1471 : {
1472 4 : poFeatureCRS->SetAxisMappingStrategy(
1473 : OAMS_TRADITIONAL_GIS_ORDER);
1474 : const bool bFeatureCRSNeedSwapXY =
1475 4 : OGRJSONFGMustSwapXY(poFeatureCRS.get());
1476 4 : if (poLayerSRS)
1477 : {
1478 : // Both feature and layer-level CRS. Reproject if needed
1479 2 : if (!poFeatureCRS->IsSame(poLayerSRS))
1480 : {
1481 : auto poCT =
1482 : std::unique_ptr<OGRCoordinateTransformation>(
1483 : OGRCreateCoordinateTransformation(
1484 4 : poFeatureCRS.get(), poLayerSRS));
1485 2 : if (poCT)
1486 : {
1487 2 : if (bFeatureCRSNeedSwapXY)
1488 1 : poGeometry->swapXY();
1489 2 : if (poGeometry->transform(poCT.get()) ==
1490 : OGRERR_NONE)
1491 : {
1492 2 : poGeometry->assignSpatialReference(poLayerSRS);
1493 2 : poFeature->SetGeometryDirectly(
1494 : poGeometry.release());
1495 : }
1496 : }
1497 : }
1498 : else
1499 : {
1500 0 : poGeometry->assignSpatialReference(poLayerSRS);
1501 0 : if (oBuildContext.bSwapPlacesXY)
1502 0 : poGeometry->swapXY();
1503 0 : poFeature->SetGeometryDirectly(poGeometry.release());
1504 : }
1505 : }
1506 : else
1507 : {
1508 : // No layer-level CRS
1509 2 : auto poFeatureCRSBorrowed = poFeatureCRS.release();
1510 2 : poGeometry->assignSpatialReference(poFeatureCRSBorrowed);
1511 2 : poFeatureCRSBorrowed->Release();
1512 2 : if (bFeatureCRSNeedSwapXY)
1513 1 : poGeometry->swapXY();
1514 2 : poFeature->SetGeometryDirectly(poGeometry.release());
1515 : }
1516 : }
1517 : }
1518 : else
1519 : {
1520 601 : poGeometry->assignSpatialReference(poLayerSRS);
1521 601 : if (oBuildContext.bSwapPlacesXY)
1522 5 : poGeometry->swapXY();
1523 601 : poFeature->SetGeometryDirectly(poGeometry.release());
1524 : }
1525 : }
1526 :
1527 783 : if (bFallbackToGeometry &&
1528 114 : (oBuildContext.poCTWGS84ToLayerCRS || oBuildContext.bLayerCRSIsWGS84))
1529 : {
1530 41 : json_object *poGeomObj = CPL_json_object_object_get(poObj, "geometry");
1531 41 : if (nullptr != poGeomObj)
1532 : {
1533 : auto poGeometry =
1534 : std::unique_ptr<OGRGeometry>(OGRGeoJSONReadGeometry(
1535 22 : poGeomObj, /* bHasM = */ false, nullptr));
1536 11 : if (poGeometry)
1537 : {
1538 11 : if (oBuildContext.poCTWGS84ToLayerCRS)
1539 : {
1540 2 : if (poGeometry->transform(
1541 2 : oBuildContext.poCTWGS84ToLayerCRS.get()) ==
1542 : OGRERR_NONE)
1543 : {
1544 2 : poGeometry->assignSpatialReference(
1545 1 : poLayer->GetSpatialRef());
1546 1 : poFeature->SetGeometryDirectly(poGeometry.release());
1547 : }
1548 : }
1549 : else /* if (oBuildContext.bLayerCRSIsWGS84) */
1550 : {
1551 20 : poGeometry->assignSpatialReference(
1552 10 : poLayer->GetSpatialRef());
1553 10 : poFeature->SetGeometryDirectly(poGeometry.release());
1554 : }
1555 : }
1556 : }
1557 : }
1558 :
1559 669 : return poFeature;
1560 : }
|