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