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 232 : OGRJSONFGReader::~OGRJSONFGReader()
30 : {
31 232 : if (poObject_)
32 202 : json_object_put(poObject_);
33 232 : }
34 :
35 : /************************************************************************/
36 : /* OGRJSONFGReader::Load() */
37 : /************************************************************************/
38 :
39 76 : bool OGRJSONFGReader::Load(OGRJSONFGDataset *poDS, const char *pszText,
40 : const std::string &osDefaultLayerName)
41 : {
42 76 : if (!OGRJSonParse(pszText, &poObject_))
43 0 : return false;
44 :
45 76 : poDS_ = poDS;
46 76 : osDefaultLayerName_ = osDefaultLayerName;
47 :
48 76 : GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
49 :
50 76 : 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 76 : if (!GenerateLayerDefns())
63 0 : return false;
64 :
65 76 : 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 46 : else if (objType == GeoJSONObject::eFeatureCollection)
78 : {
79 : const bool bHasM =
80 46 : OGRJSONFGHasMeasure(poObject_, /* bUpperLevelMValue = */ false);
81 : json_object *poObjFeatures =
82 46 : OGRGeoJSONFindMemberByName(poObject_, "features");
83 92 : if (nullptr != poObjFeatures &&
84 46 : json_type_array == json_object_get_type(poObjFeatures))
85 : {
86 46 : const auto nFeatures = json_object_array_length(poObjFeatures);
87 94 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
88 : {
89 : json_object *poObjFeature =
90 48 : json_object_array_get_idx(poObjFeatures, i);
91 48 : OGRJSONFGMemLayer *poLayer = nullptr;
92 : auto poFeat = ReadFeature(poObjFeature, nullptr, bHasM,
93 48 : &poLayer, nullptr);
94 48 : if (!poFeat)
95 0 : return false;
96 48 : poLayer->AddFeature(std::move(poFeat));
97 : }
98 : }
99 : }
100 : else
101 : {
102 0 : return false;
103 : }
104 :
105 46 : return true;
106 : }
107 :
108 : /************************************************************************/
109 : /* OGRJSONFGReadCoordRefSys() */
110 : /************************************************************************/
111 :
112 : static std::unique_ptr<OGRSpatialReference>
113 169 : OGRJSONFGReadCoordRefSys(json_object *poCoordRefSys, bool bCanRecurse = true)
114 : {
115 169 : const auto eType = json_object_get_type(poCoordRefSys);
116 169 : if (eType == json_type_string)
117 : {
118 136 : const char *pszStr = json_object_get_string(poCoordRefSys);
119 136 : 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 90 : 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 172 : auto poSRS = std::make_unique<OGRSpatialReference>();
146 86 : if (poSRS->importFromCRSURL(pszStr) != OGRERR_NONE)
147 : {
148 1 : return nullptr;
149 : }
150 85 : 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 76 : bool OGRJSONFGReader::GenerateLayerDefns()
357 : {
358 76 : const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
359 76 : if (objType == GeoJSONObject::eFeature)
360 : {
361 30 : if (!GenerateLayerDefnFromFeature(poObject_))
362 0 : return false;
363 : }
364 46 : else if (objType == GeoJSONObject::eFeatureCollection)
365 : {
366 : json_object *poObjFeatures =
367 46 : OGRGeoJSONFindMemberByName(poObject_, "features");
368 92 : if (nullptr != poObjFeatures &&
369 46 : json_type_array == json_object_get_type(poObjFeatures))
370 : {
371 46 : const auto nFeatures = json_object_array_length(poObjFeatures);
372 94 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
373 : {
374 : json_object *poObjFeature =
375 48 : json_object_array_get_idx(poObjFeatures, i);
376 48 : 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 76 : return FinalizeGenerateLayerDefns(false);
398 : }
399 :
400 : /************************************************************************/
401 : /* OGRJSONFGReader::FinalizeGenerateLayerDefns() */
402 : /************************************************************************/
403 :
404 202 : bool OGRJSONFGReader::FinalizeGenerateLayerDefns(bool bStreamedLayer)
405 : {
406 202 : json_object *poName = CPL_json_object_object_get(poObject_, "featureType");
407 202 : 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 182 : 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 202 : json_object *poCoordRefSys = nullptr;
431 0 : std::unique_ptr<OGRSpatialReference> poSRSTopLevel;
432 202 : bool bInvalidCRS = false;
433 202 : bool bSwapPlacesXYTopLevel = false;
434 273 : if (json_object_object_get_ex(poObject_, "coordRefSys", &poCoordRefSys) &&
435 71 : eGeometryElement_ != GeometryElement::GEOMETRY)
436 : {
437 68 : poSRSTopLevel = OGRJSONFGReadCoordRefSys(poCoordRefSys);
438 68 : if (poSRSTopLevel)
439 : {
440 46 : poSRSTopLevel->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
441 46 : bSwapPlacesXYTopLevel = OGRJSONFGMustSwapXY(poSRSTopLevel.get());
442 : }
443 : else
444 : {
445 22 : bInvalidCRS = true;
446 : }
447 : }
448 :
449 202 : json_object *poMeasures = nullptr;
450 206 : 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 408 : for (auto &oBuildContextIter : oMapBuildContext_)
477 : {
478 206 : const char *pszLayerName = oBuildContextIter.first.c_str();
479 206 : auto &oBuildContext = oBuildContextIter.second;
480 :
481 206 : FinalizeBuildContext(oBuildContext, pszLayerName, bStreamedLayer,
482 : bInvalidCRS, bSwapPlacesXYTopLevel,
483 : poSRSTopLevel.get());
484 : }
485 :
486 404 : return true;
487 : }
488 :
489 : /************************************************************************/
490 : /* OGRJSONFGReader::FinalizeBuildContext() */
491 : /************************************************************************/
492 :
493 206 : 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 412 : OGRSpatialReference::GetWGS84SRS()->Clone());
502 206 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
503 :
504 206 : OGRSpatialReference *poSRSLayer = nullptr;
505 206 : if (oBuildContext.poCRSAtFeatureLevel)
506 : {
507 64 : poSRSLayer = oBuildContext.poCRSAtFeatureLevel.get();
508 64 : oBuildContext.bSwapPlacesXY = OGRJSONFGMustSwapXY(poSRSLayer);
509 : }
510 142 : else if (poSRSTopLevel)
511 : {
512 38 : poSRSLayer = poSRSTopLevel;
513 38 : oBuildContext.bSwapPlacesXY = bSwapPlacesXYTopLevel;
514 : }
515 206 : if (!bInvalidCRS)
516 : {
517 184 : if (!poSRSLayer && !oBuildContext.bHasCoordRefSysAtFeatureLevel)
518 : {
519 : // No coordRefSys member found anywhere ? Fallback to WGS 84
520 81 : poSRSLayer = poSRSWGS84.get();
521 : }
522 :
523 184 : if (poSRSLayer && poSRSLayer->IsSame(poSRSWGS84.get()))
524 : {
525 89 : oBuildContext.bLayerCRSIsWGS84 = true;
526 : }
527 95 : else if (poSRSLayer)
528 : {
529 94 : const char *pszAuthName = poSRSLayer->GetAuthorityName(nullptr);
530 94 : if (!(pszAuthName && STARTS_WITH(pszAuthName, "IAU")))
531 : {
532 94 : oBuildContext.poCTWGS84ToLayerCRS.reset(
533 94 : OGRCreateCoordinateTransformation(poSRSWGS84.get(),
534 : poSRSLayer));
535 : }
536 : }
537 : }
538 :
539 206 : std::unique_ptr<OGRJSONFGMemLayer> poMemLayer;
540 206 : std::unique_ptr<OGRJSONFGStreamedLayer> poStreamedLayer;
541 : OGRLayer *poLayer;
542 206 : if (bStreamedLayer)
543 : {
544 130 : poStreamedLayer = std::make_unique<OGRJSONFGStreamedLayer>(
545 130 : poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
546 130 : poLayer = poStreamedLayer.get();
547 : }
548 : else
549 : {
550 76 : poMemLayer = std::make_unique<OGRJSONFGMemLayer>(
551 76 : poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
552 76 : poLayer = poMemLayer.get();
553 : }
554 :
555 : // Note: the current strategy will not produce stable output, depending
556 : // on the order of features, if there are conflicting order / cycles.
557 : // See https://github.com/OSGeo/gdal/pull/4552 for a number of potential
558 : // resolutions if that has to be solved in the future.
559 206 : OGRFeatureDefn *poLayerDefn = poLayer->GetLayerDefn();
560 412 : auto oTemporaryUnsealer(poLayerDefn->GetTemporaryUnsealer());
561 :
562 206 : if (poLayer->GetLayerDefn()->GetGeomType() != wkbNone)
563 : {
564 412 : OGRGeoJSONWriteOptions options;
565 :
566 206 : json_object *poXYRes = CPL_json_object_object_get(
567 : poObject_, "xy_coordinate_resolution_place");
568 206 : if (poXYRes && (json_object_get_type(poXYRes) == json_type_double ||
569 0 : json_object_get_type(poXYRes) == json_type_int))
570 : {
571 2 : auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
572 : OGRGeomCoordinatePrecision oCoordPrec(
573 4 : poGeomFieldDefn->GetCoordinatePrecision());
574 2 : oCoordPrec.dfXYResolution = json_object_get_double(poXYRes);
575 2 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
576 : }
577 :
578 206 : json_object *poZRes = CPL_json_object_object_get(
579 : poObject_, "z_coordinate_resolution_place");
580 206 : if (poZRes && (json_object_get_type(poZRes) == json_type_double ||
581 0 : json_object_get_type(poZRes) == json_type_int))
582 : {
583 2 : auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
584 : OGRGeomCoordinatePrecision oCoordPrec(
585 4 : poGeomFieldDefn->GetCoordinatePrecision());
586 2 : oCoordPrec.dfZResolution = json_object_get_double(poZRes);
587 2 : poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
588 : }
589 : }
590 :
591 412 : std::set<std::string> oSetFieldNames;
592 329 : for (const auto &poFieldDefn : oBuildContext.apoFieldDefn)
593 123 : oSetFieldNames.insert(poFieldDefn->GetNameRef());
594 :
595 : auto AddTimeField =
596 88 : [poLayerDefn, &oSetFieldNames](const char *pszName, OGRFieldType eType)
597 : {
598 22 : if (oSetFieldNames.find(pszName) == oSetFieldNames.end())
599 : {
600 42 : OGRFieldDefn oFieldDefn(pszName, eType);
601 21 : poLayerDefn->AddFieldDefn(&oFieldDefn);
602 : }
603 : else
604 : {
605 2 : OGRFieldDefn oFieldDefn((std::string("jsonfg_") + pszName).c_str(),
606 2 : eType);
607 1 : poLayerDefn->AddFieldDefn(&oFieldDefn);
608 : }
609 22 : return poLayerDefn->GetFieldCount() - 1;
610 206 : };
611 :
612 206 : if (oBuildContext.bHasTimeTimestamp)
613 : {
614 2 : oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDateTime);
615 : }
616 204 : else if (oBuildContext.bHasTimeDate)
617 : {
618 2 : oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDate);
619 : }
620 :
621 206 : if (oBuildContext.bHasTimeIntervalStartDate ||
622 203 : oBuildContext.bHasTimeIntervalStartTimestamp ||
623 199 : oBuildContext.bHasTimeIntervalEndDate ||
624 198 : oBuildContext.bHasTimeIntervalEndTimestamp)
625 : {
626 : // Mix of Date/DateTime for start/end is not supposed to happen,
627 : // but be tolerant to that
628 9 : if (oBuildContext.bHasTimeIntervalStartTimestamp)
629 : {
630 5 : oBuildContext.nIdxFieldTimeStart =
631 5 : AddTimeField("time_start", OFTDateTime);
632 : }
633 4 : else if (oBuildContext.bHasTimeIntervalStartDate)
634 : {
635 2 : oBuildContext.nIdxFieldTimeStart =
636 2 : AddTimeField("time_start", OFTDate);
637 : }
638 2 : else if (oBuildContext.bHasTimeIntervalEndTimestamp)
639 : {
640 1 : oBuildContext.nIdxFieldTimeStart =
641 1 : AddTimeField("time_start", OFTDateTime);
642 : }
643 : else /* if( oBuildContext.bHasTimeIntervalEndDate ) */
644 : {
645 1 : oBuildContext.nIdxFieldTimeStart =
646 1 : AddTimeField("time_start", OFTDate);
647 : }
648 :
649 9 : if (oBuildContext.bHasTimeIntervalEndTimestamp)
650 : {
651 3 : oBuildContext.nIdxFieldTimeEnd =
652 3 : AddTimeField("time_end", OFTDateTime);
653 : }
654 6 : else if (oBuildContext.bHasTimeIntervalEndDate)
655 : {
656 2 : oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
657 : }
658 4 : else if (oBuildContext.bHasTimeIntervalStartTimestamp)
659 : {
660 3 : oBuildContext.nIdxFieldTimeEnd =
661 3 : AddTimeField("time_end", OFTDateTime);
662 : }
663 : else /* if( oBuildContext.bHasTimeIntervalStartDate ) */
664 : {
665 1 : oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
666 : }
667 : }
668 :
669 412 : const auto sortedFields = oBuildContext.dag.getTopologicalOrdering();
670 206 : CPLAssert(sortedFields.size() == oBuildContext.apoFieldDefn.size());
671 329 : for (int idx : sortedFields)
672 : {
673 123 : poLayerDefn->AddFieldDefn(oBuildContext.apoFieldDefn[idx].get());
674 : }
675 :
676 206 : if (!oBuildContext.bFeatureLevelIdAsFID)
677 : {
678 174 : const int idx = poLayerDefn->GetFieldIndexCaseSensitive("id");
679 174 : if (idx >= 0)
680 : {
681 2 : OGRFieldDefn *poFDefn = poLayerDefn->GetFieldDefn(idx);
682 4 : if (poFDefn->GetType() == OFTInteger ||
683 2 : poFDefn->GetType() == OFTInteger64)
684 : {
685 0 : if (poStreamedLayer)
686 : {
687 0 : poStreamedLayer->SetFIDColumn(
688 0 : poLayerDefn->GetFieldDefn(idx)->GetNameRef());
689 : }
690 : else
691 : {
692 0 : poMemLayer->SetFIDColumn(
693 0 : poLayerDefn->GetFieldDefn(idx)->GetNameRef());
694 : }
695 : }
696 : }
697 : }
698 :
699 206 : if (oBuildContext.bNeedFID64)
700 0 : poLayer->SetMetadataItem(OLMD_FID64, "YES");
701 :
702 412 : if (oBuildContext.bSameMeasureMetadata &&
703 206 : (!oBuildContext.osMeasureUnit.empty() ||
704 204 : !oBuildContext.osMeasureDescription.empty()))
705 : {
706 2 : if (!oBuildContext.osMeasureUnit.empty())
707 : {
708 2 : poLayer->SetMetadataItem(
709 2 : "UNIT", oBuildContext.osMeasureUnit.c_str(), "MEASURES");
710 : }
711 :
712 2 : if (!oBuildContext.osMeasureDescription.empty())
713 : {
714 2 : poLayer->SetMetadataItem("DESCRIPTION",
715 : oBuildContext.osMeasureDescription.c_str(),
716 2 : "MEASURES");
717 : }
718 : }
719 : else
720 : {
721 204 : if (!osMeasureUnit_.empty())
722 : {
723 4 : poLayer->SetMetadataItem("UNIT", osMeasureUnit_.c_str(),
724 4 : "MEASURES");
725 : }
726 :
727 204 : if (!osMeasureDescription_.empty())
728 : {
729 4 : poLayer->SetMetadataItem("DESCRIPTION",
730 4 : osMeasureDescription_.c_str(), "MEASURES");
731 : }
732 : }
733 :
734 206 : if (poStreamedLayer)
735 : {
736 130 : poStreamedLayer->SetFeatureCount(oBuildContext.nFeatureCount);
737 130 : oBuildContext.poStreamedLayer =
738 130 : poDS_->AddLayer(std::move(poStreamedLayer));
739 : }
740 : else
741 : {
742 76 : oBuildContext.poMemLayer = poDS_->AddLayer(std::move(poMemLayer));
743 : }
744 206 : }
745 :
746 : /************************************************************************/
747 : /* OGRJSONFGReader::GetLayerNameForFeature() */
748 : /************************************************************************/
749 :
750 950 : const char *OGRJSONFGReader::GetLayerNameForFeature(json_object *poObj) const
751 : {
752 950 : const char *pszName = osDefaultLayerName_.c_str();
753 950 : json_object *poName = CPL_json_object_object_get(poObj, "featureType");
754 : // The spec allows an array of strings, but we don't support that
755 950 : if (poName != nullptr && json_object_get_type(poName) == json_type_string)
756 : {
757 197 : pszName = json_object_get_string(poName);
758 : }
759 950 : return pszName;
760 : }
761 :
762 : /************************************************************************/
763 : /* OGRJSONFGGetOGRGeometryType() */
764 : /************************************************************************/
765 :
766 179 : static OGRwkbGeometryType OGRJSONFGGetOGRGeometryType(json_object *poObj,
767 : bool bHasM)
768 : {
769 179 : const auto eType = OGRGeoJSONGetOGRGeometryType(poObj, bHasM);
770 179 : if (eType != wkbUnknown)
771 173 : return eType;
772 :
773 6 : json_object *poObjType = CPL_json_object_object_get(poObj, "type");
774 6 : const char *pszType = json_object_get_string(poObjType);
775 6 : if (!pszType)
776 0 : return wkbNone;
777 :
778 6 : if (strcmp(pszType, "Polyhedron") == 0)
779 : {
780 3 : auto eRetType = wkbPolyhedralSurfaceZ;
781 :
782 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
783 :
784 3 : if (bHasM)
785 0 : eRetType = OGR_GT_SetM(eRetType);
786 3 : return eRetType;
787 : }
788 3 : else if (strcmp(pszType, "Prism") == 0)
789 : {
790 3 : auto poBase = CPL_json_object_object_get(poObj, "base");
791 3 : if (!poBase || json_object_get_type(poBase) != json_type_object)
792 : {
793 0 : return wkbNone;
794 : }
795 :
796 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
797 :
798 3 : const auto eBaseGeomType = OGRGeoJSONGetOGRGeometryType(poBase, bHasM);
799 3 : auto eRetType = wkbNone;
800 3 : if (eBaseGeomType == wkbPoint)
801 : {
802 1 : eRetType = wkbLineString25D;
803 : }
804 2 : else if (eBaseGeomType == wkbLineString)
805 : {
806 1 : eRetType = wkbMultiPolygon25D;
807 : }
808 1 : else if (eBaseGeomType == wkbPolygon)
809 : {
810 1 : eRetType = wkbPolyhedralSurfaceZ;
811 : }
812 3 : if (eRetType != wkbNone)
813 : {
814 3 : if (bHasM)
815 0 : eRetType = OGR_GT_SetM(eRetType);
816 3 : return eRetType;
817 : }
818 : }
819 0 : return wkbNone;
820 : }
821 :
822 : /************************************************************************/
823 : /* OGRJSONFGCreateNonGeoJSONGeometry() */
824 : /************************************************************************/
825 :
826 : static std::unique_ptr<OGRGeometry>
827 6 : OGRJSONFGCreateNonGeoJSONGeometry(json_object *poObj, bool bHasM, bool bWarn)
828 : {
829 6 : json_object *poObjType = CPL_json_object_object_get(poObj, "type");
830 6 : const char *pszType = json_object_get_string(poObjType);
831 6 : if (!pszType)
832 0 : return nullptr;
833 :
834 6 : if (strcmp(pszType, "Polyhedron") == 0)
835 : {
836 3 : auto poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
837 6 : if (!poCoordinates ||
838 3 : json_object_get_type(poCoordinates) != json_type_array)
839 : {
840 0 : CPLError(CE_Failure, CPLE_AppDefined,
841 : "Missing or invalid coordinates in Polyhedron");
842 0 : return nullptr;
843 : }
844 3 : if (json_object_array_length(poCoordinates) != 1)
845 : {
846 0 : if (bWarn)
847 : {
848 0 : CPLError(CE_Warning, CPLE_AppDefined,
849 : "Polyhedron with inner shells not supported");
850 : }
851 0 : return nullptr;
852 : }
853 :
854 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
855 :
856 3 : auto poJOuterShell = json_object_array_get_idx(poCoordinates, 0);
857 6 : auto poGeom = std::make_unique<OGRPolyhedralSurface>();
858 3 : const auto nPolys = json_object_array_length(poJOuterShell);
859 8 : for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
860 : {
861 5 : auto poJPoly = json_object_array_get_idx(poJOuterShell, i);
862 5 : if (!poJPoly)
863 0 : return nullptr;
864 : auto poPoly =
865 5 : OGRGeoJSONReadPolygon(poJPoly, bHasM, /*bRaw = */ true);
866 5 : if (!poPoly)
867 0 : return nullptr;
868 5 : if (poGeom->addGeometry(std::move(poPoly)) != OGRERR_NONE)
869 0 : return nullptr;
870 : }
871 3 : if (nPolys == 0)
872 1 : poGeom->set3D(true);
873 :
874 3 : return poGeom;
875 : }
876 3 : else if (strcmp(pszType, "Prism") == 0)
877 : {
878 3 : auto poBase = CPL_json_object_object_get(poObj, "base");
879 3 : if (!poBase || json_object_get_type(poBase) != json_type_object)
880 : {
881 0 : CPLError(CE_Failure, CPLE_AppDefined,
882 : "Missing or invalid base in Prism");
883 0 : return nullptr;
884 : }
885 :
886 3 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
887 :
888 3 : json_object *poLower = CPL_json_object_object_get(poObj, "lower");
889 3 : const double dfLower = poLower ? json_object_get_double(poLower) : 0.0;
890 3 : json_object *poUpper = CPL_json_object_object_get(poObj, "upper");
891 3 : const double dfUpper = poUpper ? json_object_get_double(poUpper) : 0.0;
892 :
893 : auto poBaseGeom = std::unique_ptr<OGRGeometry>(OGRGeoJSONReadGeometry(
894 6 : poBase, bHasM, /* OGRSpatialReference* = */ nullptr));
895 3 : if (!poBaseGeom)
896 0 : return nullptr;
897 3 : const auto eBaseGeomType = poBaseGeom->getGeometryType();
898 3 : if (eBaseGeomType == wkbPoint)
899 : {
900 1 : const auto poPoint = poBaseGeom.get()->toPoint();
901 2 : auto poGeom = std::make_unique<OGRLineString>();
902 1 : if (bHasM)
903 : {
904 0 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfLower,
905 : poPoint->getM());
906 0 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfUpper,
907 : poPoint->getM());
908 : }
909 : else
910 : {
911 1 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfLower);
912 1 : poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfUpper);
913 : }
914 1 : return poGeom;
915 : }
916 2 : else if (eBaseGeomType == wkbLineString)
917 : {
918 1 : const auto poLS = poBaseGeom.get()->toLineString();
919 2 : auto poGeom = std::make_unique<OGRMultiPolygon>();
920 2 : for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
921 : {
922 1 : auto poPoly = new OGRPolygon();
923 1 : auto poRing = new OGRLinearRing();
924 1 : if (bHasM)
925 : {
926 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
927 : poLS->getM(i));
928 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
929 : dfLower, poLS->getM(i + 1));
930 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
931 : dfUpper, poLS->getM(i + 1));
932 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper,
933 : poLS->getM(i));
934 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
935 : poLS->getM(i));
936 : }
937 : else
938 : {
939 1 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
940 1 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
941 : dfLower);
942 1 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
943 : dfUpper);
944 1 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
945 1 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
946 : }
947 1 : poPoly->addRingDirectly(poRing);
948 1 : poGeom->addGeometryDirectly(poPoly);
949 : }
950 1 : return poGeom;
951 : }
952 1 : else if (eBaseGeomType == wkbPolygon)
953 : {
954 1 : const auto poBasePoly = poBaseGeom.get()->toPolygon();
955 1 : if (poBasePoly->getNumInteriorRings() > 0)
956 : {
957 0 : if (bWarn)
958 : {
959 0 : CPLError(CE_Warning, CPLE_AppDefined,
960 : "Polygon with holes is not supported as the base "
961 : "for Prism");
962 : }
963 0 : return nullptr;
964 : }
965 1 : const auto poLS = poBasePoly->getExteriorRing();
966 1 : if (poLS == nullptr)
967 : {
968 0 : return nullptr;
969 : }
970 2 : auto poGeom = std::make_unique<OGRPolyhedralSurface>();
971 : // Build lower face
972 : {
973 1 : auto poPoly = new OGRPolygon();
974 1 : auto poRing = new OGRLinearRing();
975 5 : for (int i = 0; i < poLS->getNumPoints(); ++i)
976 : {
977 4 : if (bHasM)
978 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
979 : poLS->getM(i));
980 : else
981 4 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
982 : }
983 1 : poPoly->addRingDirectly(poRing);
984 1 : poGeom->addGeometryDirectly(poPoly);
985 : }
986 : // Build side faces
987 4 : for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
988 : {
989 3 : auto poPoly = new OGRPolygon();
990 3 : auto poRing = new OGRLinearRing();
991 3 : if (bHasM)
992 : {
993 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
994 : poLS->getM(i));
995 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
996 : dfLower, poLS->getM(i + 1));
997 0 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
998 : dfUpper, poLS->getM(i + 1));
999 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper,
1000 : poLS->getM(i));
1001 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower,
1002 : poLS->getM(i));
1003 : }
1004 : else
1005 : {
1006 3 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
1007 3 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
1008 : dfLower);
1009 3 : poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1),
1010 : dfUpper);
1011 3 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
1012 3 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
1013 : }
1014 3 : poPoly->addRingDirectly(poRing);
1015 3 : poGeom->addGeometryDirectly(poPoly);
1016 : }
1017 : // Build upper face
1018 : {
1019 1 : auto poPoly = new OGRPolygon();
1020 1 : auto poRing = new OGRLinearRing();
1021 5 : for (int i = 0; i < poLS->getNumPoints(); ++i)
1022 : {
1023 4 : if (bHasM)
1024 0 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper,
1025 : poLS->getM(i));
1026 : else
1027 4 : poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
1028 : }
1029 1 : poPoly->addRingDirectly(poRing);
1030 1 : poGeom->addGeometryDirectly(poPoly);
1031 : }
1032 1 : return poGeom;
1033 : }
1034 : else
1035 : {
1036 0 : if (bWarn)
1037 : {
1038 0 : CPLError(CE_Warning, CPLE_AppDefined,
1039 : "Unsupported base geometry type for Prism");
1040 : }
1041 0 : return nullptr;
1042 : }
1043 : }
1044 : else
1045 : {
1046 0 : if (bWarn)
1047 : {
1048 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unhandled place.type = %s",
1049 : pszType);
1050 : }
1051 0 : return nullptr;
1052 : }
1053 : }
1054 :
1055 : /************************************************************************/
1056 : /* OGRJSONFGReader::GenerateLayerDefnFromFeature() */
1057 : /************************************************************************/
1058 :
1059 279 : bool OGRJSONFGReader::GenerateLayerDefnFromFeature(json_object *poObj)
1060 : {
1061 279 : const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
1062 279 : if (objType != GeoJSONObject::eFeature)
1063 : {
1064 0 : CPLError(CE_Failure, CPLE_AppDefined, "Did not get a Feature");
1065 0 : return false;
1066 : }
1067 :
1068 : const bool bHasM =
1069 279 : OGRJSONFGHasMeasure(poObj, /* bUpperLevelMValue = */ false);
1070 :
1071 279 : const char *psLayerName = GetLayerNameForFeature(poObj);
1072 :
1073 279 : auto oBuildContextIter = oMapBuildContext_.find(psLayerName);
1074 279 : if (oBuildContextIter == oMapBuildContext_.end())
1075 : {
1076 206 : LayerDefnBuildContext oContext;
1077 206 : oMapBuildContext_[psLayerName] = std::move(oContext);
1078 206 : oBuildContextIter = oMapBuildContext_.find(psLayerName);
1079 : }
1080 279 : LayerDefnBuildContext *poContext = &(oBuildContextIter->second);
1081 :
1082 279 : ++poContext->nFeatureCount;
1083 :
1084 279 : json_object *poCoordRefSys = nullptr;
1085 279 : json_object *poPlace = nullptr;
1086 279 : if (eGeometryElement_ != GeometryElement::GEOMETRY)
1087 : {
1088 276 : poPlace = CPL_json_object_object_get(poObj, "place");
1089 276 : if (poPlace && json_object_get_type(poPlace) == json_type_object)
1090 : {
1091 179 : poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
1092 : }
1093 276 : if (!poCoordRefSys)
1094 250 : poCoordRefSys = CPL_json_object_object_get(poObj, "coordRefSys");
1095 :
1096 276 : if (poCoordRefSys)
1097 : {
1098 140 : std::string osVal = json_object_to_json_string(poCoordRefSys);
1099 70 : if (!poContext->bHasCoordRefSysAtFeatureLevel)
1100 : {
1101 66 : poContext->bHasCoordRefSysAtFeatureLevel = true;
1102 66 : poContext->osCoordRefSysAtFeatureLevel = std::move(osVal);
1103 : poContext->poCRSAtFeatureLevel =
1104 66 : OGRJSONFGReadCoordRefSys(poCoordRefSys);
1105 66 : if (poContext->poCRSAtFeatureLevel)
1106 : {
1107 66 : poContext->poCRSAtFeatureLevel->SetAxisMappingStrategy(
1108 : OAMS_TRADITIONAL_GIS_ORDER);
1109 : }
1110 : }
1111 4 : else if (poContext->osCoordRefSysAtFeatureLevel != osVal)
1112 : {
1113 2 : poContext->osCoordRefSysAtFeatureLevel.clear();
1114 2 : poContext->poCRSAtFeatureLevel.reset();
1115 : }
1116 : }
1117 : }
1118 :
1119 279 : if (poContext->bSameMeasureMetadata)
1120 : {
1121 279 : json_object *poMeasures = nullptr;
1122 294 : if (json_object_object_get_ex(poObj, "measures", &poMeasures) &&
1123 15 : json_object_get_type(poMeasures) == json_type_object)
1124 : {
1125 15 : json_object *poEnabled = nullptr;
1126 15 : if (json_object_object_get_ex(poMeasures, "enabled", &poEnabled) &&
1127 30 : json_object_get_type(poEnabled) == json_type_boolean &&
1128 15 : json_object_get_boolean(poEnabled))
1129 : {
1130 15 : json_object *poUnit = nullptr;
1131 17 : if (json_object_object_get_ex(poMeasures, "unit", &poUnit) &&
1132 2 : json_object_get_type(poUnit) == json_type_string)
1133 : {
1134 2 : if (poContext->osMeasureUnit.empty())
1135 : poContext->osMeasureUnit =
1136 2 : json_object_get_string(poUnit);
1137 0 : else if (poContext->osMeasureUnit !=
1138 : json_object_get_string(poUnit))
1139 0 : poContext->bSameMeasureMetadata = false;
1140 : }
1141 :
1142 15 : json_object *poDescription = nullptr;
1143 15 : if (json_object_object_get_ex(poMeasures, "description",
1144 17 : &poDescription) &&
1145 2 : json_object_get_type(poDescription) == json_type_string)
1146 : {
1147 2 : if (poContext->osMeasureDescription.empty())
1148 : poContext->osMeasureDescription =
1149 2 : json_object_get_string(poDescription);
1150 0 : else if (poContext->osMeasureDescription !=
1151 : json_object_get_string(poDescription))
1152 0 : poContext->bSameMeasureMetadata = false;
1153 : }
1154 : }
1155 : }
1156 : }
1157 :
1158 : /* -------------------------------------------------------------------- */
1159 : /* Deal with place / geometry */
1160 : /* -------------------------------------------------------------------- */
1161 :
1162 279 : if (poContext->bDetectLayerGeomType)
1163 : {
1164 279 : bool bFallbackToGeometry =
1165 279 : (eGeometryElement_ != GeometryElement::PLACE);
1166 279 : if (poPlace && json_object_get_type(poPlace) == json_type_object)
1167 : {
1168 179 : const auto eType = OGRJSONFGGetOGRGeometryType(poPlace, bHasM);
1169 179 : if (eType != wkbNone)
1170 : {
1171 179 : bFallbackToGeometry = false;
1172 179 : poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
1173 179 : poContext->bFirstGeometry, eType,
1174 179 : poContext->eLayerGeomType);
1175 : }
1176 : }
1177 :
1178 279 : if (bFallbackToGeometry)
1179 : {
1180 : json_object *poGeomObj =
1181 99 : CPL_json_object_object_get(poObj, "geometry");
1182 115 : if (poGeomObj &&
1183 16 : json_object_get_type(poGeomObj) == json_type_object)
1184 : {
1185 : const auto eType =
1186 16 : OGRGeoJSONGetOGRGeometryType(poGeomObj, bHasM);
1187 16 : poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
1188 16 : poContext->bFirstGeometry, eType,
1189 16 : poContext->eLayerGeomType);
1190 : }
1191 : }
1192 : }
1193 :
1194 : /* -------------------------------------------------------------------- */
1195 : /* Deal with time */
1196 : /* -------------------------------------------------------------------- */
1197 279 : json_object *poTime = CPL_json_object_object_get(poObj, "time");
1198 279 : if (poTime)
1199 : {
1200 15 : json_object *poDate = CPL_json_object_object_get(poTime, "date");
1201 15 : if (poDate && json_object_get_type(poDate) == json_type_string)
1202 3 : poContext->bHasTimeDate = true;
1203 :
1204 : json_object *poTimestamp =
1205 15 : CPL_json_object_object_get(poTime, "timestamp");
1206 17 : if (poTimestamp &&
1207 2 : json_object_get_type(poTimestamp) == json_type_string)
1208 2 : poContext->bHasTimeTimestamp = true;
1209 :
1210 : json_object *poInterval =
1211 15 : CPL_json_object_object_get(poTime, "interval");
1212 25 : if (poInterval && json_object_get_type(poInterval) == json_type_array &&
1213 10 : json_object_array_length(poInterval) == 2)
1214 : {
1215 10 : json_object *poStart = json_object_array_get_idx(poInterval, 0);
1216 10 : if (poStart && json_object_get_type(poStart) == json_type_string)
1217 : {
1218 10 : const char *pszStart = json_object_get_string(poStart);
1219 10 : if (strchr(pszStart, 'Z'))
1220 5 : poContext->bHasTimeIntervalStartTimestamp = true;
1221 5 : else if (strcmp(pszStart, "..") != 0)
1222 3 : poContext->bHasTimeIntervalStartDate = true;
1223 : }
1224 :
1225 10 : json_object *poEnd = json_object_array_get_idx(poInterval, 1);
1226 10 : if (poEnd && json_object_get_type(poEnd) == json_type_string)
1227 : {
1228 10 : const char *pszEnd = json_object_get_string(poEnd);
1229 10 : if (strchr(pszEnd, 'Z'))
1230 3 : poContext->bHasTimeIntervalEndTimestamp = true;
1231 7 : else if (strcmp(pszEnd, "..") != 0)
1232 3 : poContext->bHasTimeIntervalEndDate = true;
1233 : }
1234 : }
1235 : }
1236 :
1237 : /* -------------------------------------------------------------------- */
1238 : /* Read collection of properties. */
1239 : /* -------------------------------------------------------------------- */
1240 279 : json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
1241 :
1242 279 : int nPrevFieldIdx = -1;
1243 :
1244 : // First deal with id, either at top level or in properties["id"]
1245 279 : OGRGeoJSONGenerateFeatureDefnDealWithID(
1246 279 : poObj, poObjProps, nPrevFieldIdx, poContext->oMapFieldNameToIdx,
1247 279 : poContext->apoFieldDefn, poContext->dag,
1248 279 : poContext->bFeatureLevelIdAsFID, poContext->bFeatureLevelIdAsAttribute,
1249 279 : poContext->bNeedFID64);
1250 :
1251 530 : if (nullptr != poObjProps &&
1252 251 : json_object_get_type(poObjProps) == json_type_object)
1253 : {
1254 : json_object_iter it;
1255 251 : it.key = nullptr;
1256 251 : it.val = nullptr;
1257 251 : it.entry = nullptr;
1258 502 : std::vector<int> anCurFieldIndices;
1259 589 : json_object_object_foreachC(poObjProps, it)
1260 : {
1261 338 : anCurFieldIndices.clear();
1262 338 : OGRGeoJSONReaderAddOrUpdateField(
1263 338 : anCurFieldIndices, poContext->oMapFieldNameToIdx,
1264 338 : poContext->apoFieldDefn, it.key, it.val,
1265 338 : bFlattenNestedAttributes_, chNestedAttributeSeparator_,
1266 338 : bArrayAsString_, bDateAsString_,
1267 338 : poContext->aoSetUndeterminedTypeFields);
1268 676 : for (int idx : anCurFieldIndices)
1269 : {
1270 676 : poContext->dag.addNode(
1271 338 : idx, poContext->apoFieldDefn[idx]->GetNameRef());
1272 338 : if (nPrevFieldIdx != -1)
1273 : {
1274 241 : poContext->dag.addEdge(nPrevFieldIdx, idx);
1275 : }
1276 338 : nPrevFieldIdx = idx;
1277 : }
1278 : }
1279 : }
1280 :
1281 279 : return true;
1282 : }
1283 :
1284 : /************************************************************************/
1285 : /* OGRJSONFGReader::ReadFeature() */
1286 : /************************************************************************/
1287 :
1288 : std::unique_ptr<OGRFeature>
1289 671 : OGRJSONFGReader::ReadFeature(json_object *poObj, const char *pszRequestedLayer,
1290 : bool bHasM, OGRJSONFGMemLayer **pOutMemLayer,
1291 : OGRJSONFGStreamedLayer **pOutStreamedLayer)
1292 : {
1293 671 : const char *pszLayerName = GetLayerNameForFeature(poObj);
1294 671 : if (pszRequestedLayer && strcmp(pszLayerName, pszRequestedLayer) != 0)
1295 3 : return nullptr;
1296 :
1297 668 : bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
1298 :
1299 668 : auto oBuildContextIter = oMapBuildContext_.find(pszLayerName);
1300 668 : CPLAssert(oBuildContextIter != oMapBuildContext_.end());
1301 668 : auto &oBuildContext = oBuildContextIter->second;
1302 668 : OGRLayer *poLayer =
1303 668 : oBuildContext.poStreamedLayer
1304 668 : ? static_cast<OGRLayer *>(oBuildContext.poStreamedLayer)
1305 : : static_cast<OGRLayer *>(oBuildContext.poMemLayer);
1306 :
1307 668 : if (pOutMemLayer)
1308 78 : *pOutMemLayer = oBuildContext.poMemLayer;
1309 590 : else if (pOutStreamedLayer)
1310 590 : *pOutStreamedLayer = oBuildContext.poStreamedLayer;
1311 :
1312 668 : OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
1313 1336 : auto poFeature = std::make_unique<OGRFeature>(poFDefn);
1314 :
1315 : /* -------------------------------------------------------------------- */
1316 : /* Translate GeoJSON "properties" object to feature attributes. */
1317 : /* -------------------------------------------------------------------- */
1318 :
1319 668 : json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
1320 1308 : if (nullptr != poObjProps &&
1321 640 : json_object_get_type(poObjProps) == json_type_object)
1322 : {
1323 : json_object_iter it;
1324 640 : it.key = nullptr;
1325 640 : it.val = nullptr;
1326 640 : it.entry = nullptr;
1327 2204 : json_object_object_foreachC(poObjProps, it)
1328 : {
1329 1564 : const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
1330 1564 : if (nField < 0 &&
1331 0 : !(bFlattenNestedAttributes_ && it.val != nullptr &&
1332 0 : json_object_get_type(it.val) == json_type_object))
1333 : {
1334 0 : CPLDebug("JSONFG", "Cannot find field %s", it.key);
1335 : }
1336 : else
1337 : {
1338 1564 : OGRGeoJSONReaderSetField(
1339 1564 : poLayer, poFeature.get(), nField, it.key, it.val,
1340 1564 : bFlattenNestedAttributes_, chNestedAttributeSeparator_);
1341 : }
1342 : }
1343 : }
1344 :
1345 : /* -------------------------------------------------------------------- */
1346 : /* Try to use feature-level ID if available */
1347 : /* and of integral type. Otherwise, leave unset (-1) then index */
1348 : /* in features sequence will be used as FID. */
1349 : /* -------------------------------------------------------------------- */
1350 668 : json_object *poObjId = CPL_json_object_object_get(poObj, "id");
1351 668 : if (nullptr != poObjId && oBuildContext.bFeatureLevelIdAsFID)
1352 : {
1353 32 : poFeature->SetFID(static_cast<GIntBig>(json_object_get_int64(poObjId)));
1354 : }
1355 :
1356 : /* -------------------------------------------------------------------- */
1357 : /* Handle the case where the special id is in a regular field. */
1358 : /* -------------------------------------------------------------------- */
1359 636 : else if (nullptr != poObjId)
1360 : {
1361 2 : const int nIdx = poFDefn->GetFieldIndexCaseSensitive("id");
1362 2 : if (nIdx >= 0 && !poFeature->IsFieldSet(nIdx))
1363 : {
1364 2 : poFeature->SetField(nIdx, json_object_get_string(poObjId));
1365 : }
1366 : }
1367 :
1368 : /* -------------------------------------------------------------------- */
1369 : /* Deal with time */
1370 : /* -------------------------------------------------------------------- */
1371 668 : json_object *poTime = CPL_json_object_object_get(poObj, "time");
1372 668 : if (poTime)
1373 : {
1374 15 : json_object *poDate = CPL_json_object_object_get(poTime, "date");
1375 15 : if (poDate && json_object_get_type(poDate) == json_type_string)
1376 : {
1377 3 : poFeature->SetField(oBuildContext.nIdxFieldTime,
1378 : json_object_get_string(poDate));
1379 : }
1380 :
1381 : json_object *poTimestamp =
1382 15 : CPL_json_object_object_get(poTime, "timestamp");
1383 17 : if (poTimestamp &&
1384 2 : json_object_get_type(poTimestamp) == json_type_string)
1385 : {
1386 2 : poFeature->SetField(oBuildContext.nIdxFieldTime,
1387 : json_object_get_string(poTimestamp));
1388 : }
1389 :
1390 : json_object *poInterval =
1391 15 : CPL_json_object_object_get(poTime, "interval");
1392 25 : if (poInterval && json_object_get_type(poInterval) == json_type_array &&
1393 10 : json_object_array_length(poInterval) == 2)
1394 : {
1395 10 : json_object *poStart = json_object_array_get_idx(poInterval, 0);
1396 10 : if (poStart && json_object_get_type(poStart) == json_type_string)
1397 : {
1398 10 : const char *pszStart = json_object_get_string(poStart);
1399 10 : if (strcmp(pszStart, "..") != 0)
1400 8 : poFeature->SetField(oBuildContext.nIdxFieldTimeStart,
1401 : pszStart);
1402 : }
1403 :
1404 10 : json_object *poEnd = json_object_array_get_idx(poInterval, 1);
1405 10 : if (poEnd && json_object_get_type(poEnd) == json_type_string)
1406 : {
1407 10 : const char *pszEnd = json_object_get_string(poEnd);
1408 10 : if (strcmp(pszEnd, "..") != 0)
1409 6 : poFeature->SetField(oBuildContext.nIdxFieldTimeEnd, pszEnd);
1410 : }
1411 : }
1412 : }
1413 :
1414 : /* -------------------------------------------------------------------- */
1415 : /* Translate "place" (and fallback to "geometry") sub-object */
1416 : /* -------------------------------------------------------------------- */
1417 668 : json_object *poPlace = nullptr;
1418 668 : bool bFallbackToGeometry = (eGeometryElement_ != GeometryElement::PLACE);
1419 :
1420 668 : if (eGeometryElement_ != GeometryElement::GEOMETRY)
1421 : {
1422 665 : poPlace = CPL_json_object_object_get(poObj, "place");
1423 : }
1424 668 : if (poPlace && json_object_get_type(poPlace) == json_type_object)
1425 : {
1426 608 : bHasM = OGRJSONFGHasMeasure(poPlace, bHasM);
1427 608 : json_object *poCoordRefSys = nullptr;
1428 608 : if (!oBuildContext.poCRSAtFeatureLevel)
1429 : {
1430 551 : poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
1431 551 : if (!poCoordRefSys)
1432 : {
1433 : poCoordRefSys =
1434 551 : CPL_json_object_object_get(poObj, "coordRefSys");
1435 : }
1436 : }
1437 :
1438 608 : std::unique_ptr<OGRGeometry> poGeometry;
1439 608 : json_object *poObjType = CPL_json_object_object_get(poPlace, "type");
1440 608 : const char *pszType = json_object_get_string(poObjType);
1441 608 : if (pszType && (strcmp(pszType, "Polyhedron") == 0 ||
1442 605 : strcmp(pszType, "Prism") == 0))
1443 : {
1444 12 : poGeometry = OGRJSONFGCreateNonGeoJSONGeometry(poPlace, bHasM,
1445 6 : /* bWarn=*/false);
1446 : }
1447 : else
1448 : {
1449 602 : poGeometry = OGRGeoJSONReadGeometry(poPlace, bHasM, nullptr);
1450 : }
1451 608 : if (poGeometry)
1452 604 : bFallbackToGeometry = false;
1453 :
1454 608 : auto poLayerSRS = poLayer->GetSpatialRef();
1455 608 : if (!poGeometry)
1456 : {
1457 : // nothing to do
1458 : }
1459 604 : else if (poCoordRefSys)
1460 : {
1461 8 : auto poFeatureCRS = OGRJSONFGReadCoordRefSys(poCoordRefSys);
1462 4 : if (poFeatureCRS)
1463 : {
1464 4 : poFeatureCRS->SetAxisMappingStrategy(
1465 : OAMS_TRADITIONAL_GIS_ORDER);
1466 : const bool bFeatureCRSNeedSwapXY =
1467 4 : OGRJSONFGMustSwapXY(poFeatureCRS.get());
1468 4 : if (poLayerSRS)
1469 : {
1470 : // Both feature and layer-level CRS. Reproject if needed
1471 2 : if (!poFeatureCRS->IsSame(poLayerSRS))
1472 : {
1473 : auto poCT =
1474 : std::unique_ptr<OGRCoordinateTransformation>(
1475 : OGRCreateCoordinateTransformation(
1476 4 : poFeatureCRS.get(), poLayerSRS));
1477 2 : if (poCT)
1478 : {
1479 2 : if (bFeatureCRSNeedSwapXY)
1480 1 : poGeometry->swapXY();
1481 2 : if (poGeometry->transform(poCT.get()) ==
1482 : OGRERR_NONE)
1483 : {
1484 2 : poGeometry->assignSpatialReference(poLayerSRS);
1485 2 : poFeature->SetGeometryDirectly(
1486 : poGeometry.release());
1487 : }
1488 : }
1489 : }
1490 : else
1491 : {
1492 0 : poGeometry->assignSpatialReference(poLayerSRS);
1493 0 : if (oBuildContext.bSwapPlacesXY)
1494 0 : poGeometry->swapXY();
1495 0 : poFeature->SetGeometryDirectly(poGeometry.release());
1496 : }
1497 : }
1498 : else
1499 : {
1500 : // No layer-level CRS
1501 2 : auto poFeatureCRSBorrowed = poFeatureCRS.release();
1502 2 : poGeometry->assignSpatialReference(poFeatureCRSBorrowed);
1503 2 : poFeatureCRSBorrowed->Release();
1504 2 : if (bFeatureCRSNeedSwapXY)
1505 1 : poGeometry->swapXY();
1506 2 : poFeature->SetGeometryDirectly(poGeometry.release());
1507 : }
1508 : }
1509 : }
1510 : else
1511 : {
1512 600 : poGeometry->assignSpatialReference(poLayerSRS);
1513 600 : if (oBuildContext.bSwapPlacesXY)
1514 5 : poGeometry->swapXY();
1515 600 : poFeature->SetGeometryDirectly(poGeometry.release());
1516 : }
1517 : }
1518 :
1519 778 : if (bFallbackToGeometry &&
1520 110 : (oBuildContext.poCTWGS84ToLayerCRS || oBuildContext.bLayerCRSIsWGS84))
1521 : {
1522 41 : json_object *poGeomObj = CPL_json_object_object_get(poObj, "geometry");
1523 41 : if (nullptr != poGeomObj)
1524 : {
1525 : auto poGeometry =
1526 : std::unique_ptr<OGRGeometry>(OGRGeoJSONReadGeometry(
1527 22 : poGeomObj, /* bHasM = */ false, nullptr));
1528 11 : if (poGeometry)
1529 : {
1530 11 : if (oBuildContext.poCTWGS84ToLayerCRS)
1531 : {
1532 2 : if (poGeometry->transform(
1533 2 : oBuildContext.poCTWGS84ToLayerCRS.get()) ==
1534 : OGRERR_NONE)
1535 : {
1536 2 : poGeometry->assignSpatialReference(
1537 1 : poLayer->GetSpatialRef());
1538 1 : poFeature->SetGeometryDirectly(poGeometry.release());
1539 : }
1540 : }
1541 : else /* if (oBuildContext.bLayerCRSIsWGS84) */
1542 : {
1543 20 : poGeometry->assignSpatialReference(
1544 10 : poLayer->GetSpatialRef());
1545 10 : poFeature->SetGeometryDirectly(poGeometry.release());
1546 : }
1547 : }
1548 : }
1549 : }
1550 :
1551 668 : return poFeature;
1552 : }
|