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