Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implementation of OGRGeoJSONReader class (OGR GeoJSON Driver).
5 : * Author: Mateusz Loskot, mateusz@loskot.net
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Mateusz Loskot
9 : * Copyright (c) 2008-2017, Even Rouault <even dot rouault at spatialys dot com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogrgeojsonreader.h"
31 : #include "ogrgeojsonutils.h"
32 : #include "ogr_geojson.h"
33 : #include "ogrjsoncollectionstreamingparser.h"
34 : #include "ogr_api.h"
35 :
36 : #include <limits>
37 : #include <set>
38 : #include <functional>
39 :
40 : /************************************************************************/
41 : /* OGRGeoJSONReaderStreamingParser */
42 : /************************************************************************/
43 :
44 : class OGRGeoJSONReaderStreamingParser final
45 : : public OGRJSONCollectionStreamingParser
46 : {
47 : OGRGeoJSONReader &m_oReader;
48 : OGRGeoJSONLayer *m_poLayer = nullptr;
49 :
50 : std::vector<OGRFeature *> m_apoFeatures{};
51 : size_t m_nCurFeatureIdx = 0;
52 : bool m_bOriginalIdModifiedEmitted = false;
53 : std::set<GIntBig> m_oSetUsedFIDs{};
54 :
55 : std::map<std::string, int> m_oMapFieldNameToIdx{};
56 : std::vector<std::unique_ptr<OGRFieldDefn>> m_apoFieldDefn{};
57 : gdal::DirectedAcyclicGraph<int, std::string> m_dag{};
58 :
59 : void AnalyzeFeature();
60 :
61 : CPL_DISALLOW_COPY_ASSIGN(OGRGeoJSONReaderStreamingParser)
62 :
63 : protected:
64 : void GotFeature(json_object *poObj, bool bFirstPass,
65 : const std::string &osJson) override;
66 : void TooComplex() override;
67 :
68 : public:
69 : OGRGeoJSONReaderStreamingParser(OGRGeoJSONReader &oReader,
70 : OGRGeoJSONLayer *poLayer, bool bFirstPass,
71 : bool bStoreNativeData);
72 : ~OGRGeoJSONReaderStreamingParser();
73 :
74 : void FinalizeLayerDefn();
75 :
76 : OGRFeature *GetNextFeature();
77 :
78 84 : inline bool GetOriginalIdModifiedEmitted() const
79 : {
80 84 : return m_bOriginalIdModifiedEmitted;
81 : }
82 :
83 236 : inline void SetOriginalIdModifiedEmitted(bool b)
84 : {
85 236 : m_bOriginalIdModifiedEmitted = b;
86 236 : }
87 : };
88 :
89 : /************************************************************************/
90 : /* OGRGeoJSONBaseReader() */
91 : /************************************************************************/
92 :
93 : OGRGeoJSONBaseReader::OGRGeoJSONBaseReader() = default;
94 :
95 : /************************************************************************/
96 : /* SetPreserveGeometryType */
97 : /************************************************************************/
98 :
99 0 : void OGRGeoJSONBaseReader::SetPreserveGeometryType(bool bPreserve)
100 : {
101 0 : bGeometryPreserve_ = bPreserve;
102 0 : }
103 :
104 : /************************************************************************/
105 : /* SetSkipAttributes */
106 : /************************************************************************/
107 :
108 0 : void OGRGeoJSONBaseReader::SetSkipAttributes(bool bSkip)
109 : {
110 0 : bAttributesSkip_ = bSkip;
111 0 : }
112 :
113 : /************************************************************************/
114 : /* SetFlattenNestedAttributes */
115 : /************************************************************************/
116 :
117 407 : void OGRGeoJSONBaseReader::SetFlattenNestedAttributes(bool bFlatten,
118 : char chSeparator)
119 : {
120 407 : bFlattenNestedAttributes_ = bFlatten;
121 407 : chNestedAttributeSeparator_ = chSeparator;
122 407 : }
123 :
124 : /************************************************************************/
125 : /* SetStoreNativeData */
126 : /************************************************************************/
127 :
128 407 : void OGRGeoJSONBaseReader::SetStoreNativeData(bool bStoreNativeData)
129 : {
130 407 : bStoreNativeData_ = bStoreNativeData;
131 407 : }
132 :
133 : /************************************************************************/
134 : /* SetArrayAsString */
135 : /************************************************************************/
136 :
137 407 : void OGRGeoJSONBaseReader::SetArrayAsString(bool bArrayAsString)
138 : {
139 407 : bArrayAsString_ = bArrayAsString;
140 407 : }
141 :
142 : /************************************************************************/
143 : /* SetDateAsString */
144 : /************************************************************************/
145 :
146 407 : void OGRGeoJSONBaseReader::SetDateAsString(bool bDateAsString)
147 : {
148 407 : bDateAsString_ = bDateAsString;
149 407 : }
150 :
151 : /************************************************************************/
152 : /* OGRGeoJSONReader */
153 : /************************************************************************/
154 :
155 407 : OGRGeoJSONReader::OGRGeoJSONReader()
156 : : poGJObject_(nullptr), poStreamingParser_(nullptr), bFirstSeg_(false),
157 : bJSonPLikeWrapper_(false), fp_(nullptr), bCanEasilyAppend_(false),
158 : bFCHasBBOX_(false), nBufferSize_(0), pabyBuffer_(nullptr),
159 407 : nTotalFeatureCount_(0), nTotalOGRFeatureMemEstimate_(0)
160 : {
161 407 : }
162 :
163 : /************************************************************************/
164 : /* ~OGRGeoJSONReader */
165 : /************************************************************************/
166 :
167 407 : OGRGeoJSONReader::~OGRGeoJSONReader()
168 : {
169 407 : if (nullptr != poGJObject_)
170 : {
171 404 : json_object_put(poGJObject_);
172 : }
173 407 : if (fp_ != nullptr)
174 : {
175 241 : VSIFCloseL(fp_);
176 : }
177 407 : delete poStreamingParser_;
178 407 : CPLFree(pabyBuffer_);
179 :
180 407 : poGJObject_ = nullptr;
181 407 : }
182 :
183 : /************************************************************************/
184 : /* Parse */
185 : /************************************************************************/
186 :
187 163 : OGRErr OGRGeoJSONReader::Parse(const char *pszText)
188 : {
189 163 : if (nullptr != pszText)
190 : {
191 : // Skip UTF-8 BOM (#5630).
192 163 : const GByte *pabyData = (const GByte *)pszText;
193 163 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF)
194 : {
195 1 : CPLDebug("GeoJSON", "Skip UTF-8 BOM");
196 1 : pszText += 3;
197 : }
198 :
199 163 : if (poGJObject_ != nullptr)
200 : {
201 0 : json_object_put(poGJObject_);
202 0 : poGJObject_ = nullptr;
203 : }
204 :
205 : // JSON tree is shared for while lifetime of the reader object
206 : // and will be released in the destructor.
207 163 : if (!OGRJSonParse(pszText, &poGJObject_))
208 0 : return OGRERR_CORRUPT_DATA;
209 : }
210 :
211 163 : return OGRERR_NONE;
212 : }
213 :
214 : /************************************************************************/
215 : /* ReadLayers */
216 : /************************************************************************/
217 :
218 163 : void OGRGeoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
219 : {
220 163 : if (nullptr == poGJObject_)
221 : {
222 0 : CPLDebug("GeoJSON",
223 : "Missing parsed GeoJSON data. Forgot to call Parse()?");
224 0 : return;
225 : }
226 :
227 163 : ReadLayer(poDS, nullptr, poGJObject_);
228 : }
229 :
230 : /************************************************************************/
231 : /* OGRGeoJSONReaderStreamingParserGetMaxObjectSize() */
232 : /************************************************************************/
233 :
234 480 : static size_t OGRGeoJSONReaderStreamingParserGetMaxObjectSize()
235 : {
236 : const double dfTmp =
237 480 : CPLAtof(CPLGetConfigOption("OGR_GEOJSON_MAX_OBJ_SIZE", "200"));
238 480 : return dfTmp > 0 ? static_cast<size_t>(dfTmp * 1024 * 1024) : 0;
239 : }
240 :
241 : /************************************************************************/
242 : /* OGRGeoJSONReaderStreamingParser() */
243 : /************************************************************************/
244 :
245 480 : OGRGeoJSONReaderStreamingParser::OGRGeoJSONReaderStreamingParser(
246 : OGRGeoJSONReader &oReader, OGRGeoJSONLayer *poLayer, bool bFirstPass,
247 480 : bool bStoreNativeData)
248 : : OGRJSONCollectionStreamingParser(
249 : bFirstPass, bStoreNativeData,
250 : OGRGeoJSONReaderStreamingParserGetMaxObjectSize()),
251 480 : m_oReader(oReader), m_poLayer(poLayer)
252 : {
253 480 : }
254 :
255 : /************************************************************************/
256 : /* ~OGRGeoJSONReaderStreamingParser() */
257 : /************************************************************************/
258 :
259 711 : OGRGeoJSONReaderStreamingParser::~OGRGeoJSONReaderStreamingParser()
260 : {
261 672 : for (size_t i = 0; i < m_apoFeatures.size(); i++)
262 192 : delete m_apoFeatures[i];
263 711 : }
264 :
265 : /************************************************************************/
266 : /* GetNextFeature() */
267 : /************************************************************************/
268 :
269 1411 : OGRFeature *OGRGeoJSONReaderStreamingParser::GetNextFeature()
270 : {
271 1411 : if (m_nCurFeatureIdx < m_apoFeatures.size())
272 : {
273 806 : OGRFeature *poFeat = m_apoFeatures[m_nCurFeatureIdx];
274 806 : m_apoFeatures[m_nCurFeatureIdx] = nullptr;
275 806 : m_nCurFeatureIdx++;
276 806 : return poFeat;
277 : }
278 605 : m_nCurFeatureIdx = 0;
279 605 : m_apoFeatures.clear();
280 605 : return nullptr;
281 : }
282 :
283 : /************************************************************************/
284 : /* GotFeature() */
285 : /************************************************************************/
286 :
287 3708 : void OGRGeoJSONReaderStreamingParser::GotFeature(json_object *poObj,
288 : bool bFirstPass,
289 : const std::string &osJson)
290 : {
291 3708 : if (bFirstPass)
292 : {
293 2830 : if (!m_oReader.GenerateFeatureDefn(m_oMapFieldNameToIdx, m_apoFieldDefn,
294 2830 : m_dag, m_poLayer, poObj))
295 : {
296 : }
297 2830 : m_poLayer->IncFeatureCount();
298 : }
299 : else
300 : {
301 : OGRFeature *poFeat =
302 878 : m_oReader.ReadFeature(m_poLayer, poObj, osJson.c_str());
303 878 : if (poFeat)
304 : {
305 878 : GIntBig nFID = poFeat->GetFID();
306 878 : if (nFID == OGRNullFID)
307 : {
308 711 : nFID = static_cast<GIntBig>(m_oSetUsedFIDs.size());
309 711 : while (m_oSetUsedFIDs.find(nFID) != m_oSetUsedFIDs.end())
310 : {
311 0 : ++nFID;
312 : }
313 : }
314 167 : else if (m_oSetUsedFIDs.find(nFID) != m_oSetUsedFIDs.end())
315 : {
316 33 : if (!m_bOriginalIdModifiedEmitted)
317 : {
318 2 : CPLError(CE_Warning, CPLE_AppDefined,
319 : "Several features with id = " CPL_FRMT_GIB " have "
320 : "been found. Altering it to be unique. "
321 : "This warning will not be emitted anymore for "
322 : "this layer",
323 : nFID);
324 2 : m_bOriginalIdModifiedEmitted = true;
325 : }
326 33 : nFID = static_cast<GIntBig>(m_oSetUsedFIDs.size());
327 33 : while (m_oSetUsedFIDs.find(nFID) != m_oSetUsedFIDs.end())
328 : {
329 0 : ++nFID;
330 : }
331 : }
332 878 : m_oSetUsedFIDs.insert(nFID);
333 878 : poFeat->SetFID(nFID);
334 :
335 878 : m_apoFeatures.push_back(poFeat);
336 : }
337 : }
338 3708 : }
339 :
340 : /************************************************************************/
341 : /* FinalizeLayerDefn() */
342 : /************************************************************************/
343 :
344 241 : void OGRGeoJSONReaderStreamingParser::FinalizeLayerDefn()
345 : {
346 241 : OGRFeatureDefn *poDefn = m_poLayer->GetLayerDefn();
347 482 : auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
348 482 : const auto sortedFields = m_dag.getTopologicalOrdering();
349 241 : CPLAssert(sortedFields.size() == m_apoFieldDefn.size());
350 908 : for (int idx : sortedFields)
351 : {
352 667 : poDefn->AddFieldDefn(m_apoFieldDefn[idx].get());
353 : }
354 241 : m_dag = gdal::DirectedAcyclicGraph<int, std::string>();
355 241 : m_oMapFieldNameToIdx.clear();
356 241 : m_apoFieldDefn.clear();
357 241 : }
358 :
359 : /************************************************************************/
360 : /* TooComplex() */
361 : /************************************************************************/
362 :
363 24885 : void OGRGeoJSONReaderStreamingParser::TooComplex()
364 : {
365 24885 : if (!ExceptionOccurred())
366 1 : EmitException("GeoJSON object too complex/large. You may define the "
367 : "OGR_GEOJSON_MAX_OBJ_SIZE configuration option to "
368 : "a value in megabytes to allow "
369 : "for larger features, or 0 to remove any size limit.");
370 24885 : }
371 :
372 : /************************************************************************/
373 : /* SetCoordinatePrecision() */
374 : /************************************************************************/
375 :
376 332 : static void SetCoordinatePrecision(json_object *poRootObj,
377 : OGRGeoJSONLayer *poLayer)
378 : {
379 332 : if (poLayer->GetLayerDefn()->GetGeomType() != wkbNone)
380 : {
381 664 : OGRGeoJSONWriteOptions options;
382 :
383 : json_object *poXYRes =
384 332 : CPL_json_object_object_get(poRootObj, "xy_coordinate_resolution");
385 332 : if (poXYRes && (json_object_get_type(poXYRes) == json_type_double ||
386 0 : json_object_get_type(poXYRes) == json_type_int))
387 : {
388 8 : auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
389 : OGRGeomCoordinatePrecision oCoordPrec(
390 8 : poGeomFieldDefn->GetCoordinatePrecision());
391 8 : oCoordPrec.dfXYResolution = json_object_get_double(poXYRes);
392 8 : whileUnsealing(poGeomFieldDefn)->SetCoordinatePrecision(oCoordPrec);
393 :
394 8 : options.nXYCoordPrecision =
395 8 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
396 : oCoordPrec.dfXYResolution);
397 : }
398 :
399 : json_object *poZRes =
400 332 : CPL_json_object_object_get(poRootObj, "z_coordinate_resolution");
401 332 : if (poZRes && (json_object_get_type(poZRes) == json_type_double ||
402 0 : json_object_get_type(poZRes) == json_type_int))
403 : {
404 6 : auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
405 : OGRGeomCoordinatePrecision oCoordPrec(
406 6 : poGeomFieldDefn->GetCoordinatePrecision());
407 6 : oCoordPrec.dfZResolution = json_object_get_double(poZRes);
408 6 : whileUnsealing(poGeomFieldDefn)->SetCoordinatePrecision(oCoordPrec);
409 :
410 6 : options.nZCoordPrecision =
411 6 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
412 : oCoordPrec.dfZResolution);
413 : }
414 :
415 332 : poLayer->SetWriteOptions(options);
416 : }
417 332 : }
418 :
419 : /************************************************************************/
420 : /* FirstPassReadLayer() */
421 : /************************************************************************/
422 :
423 244 : bool OGRGeoJSONReader::FirstPassReadLayer(OGRGeoJSONDataSource *poDS,
424 : VSILFILE *fp,
425 : bool &bTryStandardReading)
426 : {
427 244 : bTryStandardReading = false;
428 244 : VSIFSeekL(fp, 0, SEEK_SET);
429 244 : bFirstSeg_ = true;
430 :
431 244 : const char *pszName = poDS->GetDescription();
432 244 : if (STARTS_WITH_CI(pszName, "GeoJSON:"))
433 0 : pszName += strlen("GeoJSON:");
434 244 : pszName = CPLGetBasename(pszName);
435 :
436 : OGRGeoJSONLayer *poLayer = new OGRGeoJSONLayer(
437 244 : pszName, nullptr, OGRGeoJSONLayer::DefaultGeometryType, poDS, this);
438 : OGRGeoJSONReaderStreamingParser oParser(*this, poLayer, true,
439 488 : bStoreNativeData_);
440 :
441 244 : vsi_l_offset nFileSize = 0;
442 314 : if (STARTS_WITH(poDS->GetDescription(), "/vsimem/") ||
443 70 : !STARTS_WITH(poDS->GetDescription(), "/vsi"))
444 : {
445 : VSIStatBufL sStatBuf;
446 238 : if (VSIStatL(poDS->GetDescription(), &sStatBuf) == 0)
447 : {
448 238 : nFileSize = sStatBuf.st_size;
449 : }
450 : }
451 :
452 244 : nBufferSize_ = 4096 * 10;
453 244 : pabyBuffer_ = static_cast<GByte *>(CPLMalloc(nBufferSize_));
454 244 : int nIter = 0;
455 244 : bool bThresholdReached = false;
456 244 : const GIntBig nMaxBytesFirstPass = CPLAtoGIntBig(
457 : CPLGetConfigOption("OGR_GEOJSON_MAX_BYTES_FIRST_PASS", "0"));
458 244 : const GIntBig nLimitFeaturesFirstPass = CPLAtoGIntBig(
459 : CPLGetConfigOption("OGR_GEOJSON_MAX_FEATURES_FIRST_PASS", "0"));
460 : while (true)
461 : {
462 293 : nIter++;
463 :
464 293 : if (nMaxBytesFirstPass > 0 &&
465 0 : static_cast<GIntBig>(nIter) * static_cast<GIntBig>(nBufferSize_) >=
466 : nMaxBytesFirstPass)
467 : {
468 0 : CPLDebug("GeoJSON", "First pass: early exit since above "
469 : "OGR_GEOJSON_MAX_BYTES_FIRST_PASS");
470 0 : bThresholdReached = true;
471 0 : break;
472 : }
473 :
474 293 : size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp);
475 293 : const bool bFinished = nRead < nBufferSize_;
476 293 : size_t nSkip = 0;
477 293 : if (bFirstSeg_)
478 : {
479 244 : bFirstSeg_ = false;
480 244 : nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
481 : }
482 293 : if (bFinished && bJSonPLikeWrapper_ && nRead - nSkip > 0)
483 1 : nRead--;
484 293 : if (!oParser.Parse(reinterpret_cast<const char *>(pabyBuffer_ + nSkip),
485 585 : nRead - nSkip, bFinished) ||
486 292 : oParser.ExceptionOccurred())
487 : {
488 : // to avoid killing ourselves during layer deletion
489 2 : poLayer->UnsetReader();
490 2 : delete poLayer;
491 2 : return false;
492 : }
493 291 : if (bFinished || (nIter % 100) == 0)
494 : {
495 242 : if (nFileSize == 0)
496 : {
497 6 : if (bFinished)
498 : {
499 6 : CPLDebug("GeoJSON", "First pass: 100.00 %%");
500 : }
501 : else
502 : {
503 0 : CPLDebug("GeoJSON",
504 : "First pass: " CPL_FRMT_GUIB " bytes read",
505 0 : static_cast<GUIntBig>(nIter) *
506 0 : static_cast<GUIntBig>(nBufferSize_) +
507 : nRead);
508 : }
509 : }
510 : else
511 : {
512 236 : CPLDebug("GeoJSON", "First pass: %.2f %%",
513 236 : 100.0 * VSIFTellL(fp) / nFileSize);
514 : }
515 : }
516 291 : if (nLimitFeaturesFirstPass > 0 &&
517 0 : poLayer->GetFeatureCount(FALSE) >= nLimitFeaturesFirstPass)
518 : {
519 0 : CPLDebug("GeoJSON", "First pass: early exit since above "
520 : "OGR_GEOJSON_MAX_FEATURES_FIRST_PASS");
521 0 : bThresholdReached = true;
522 0 : break;
523 : }
524 291 : if (oParser.IsTypeKnown() && !oParser.IsFeatureCollection())
525 0 : break;
526 291 : if (bFinished)
527 242 : break;
528 49 : }
529 :
530 242 : if (bThresholdReached)
531 : {
532 0 : poLayer->InvalidateFeatureCount();
533 : }
534 242 : else if (!oParser.IsTypeKnown() || !oParser.IsFeatureCollection())
535 : {
536 : // to avoid killing ourselves during layer deletion
537 1 : poLayer->UnsetReader();
538 1 : delete poLayer;
539 : const vsi_l_offset nRAM =
540 1 : static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM());
541 1 : if (nFileSize == 0 || nRAM == 0 || nRAM > nFileSize * 20)
542 : {
543 : // Only try full ingestion if we have 20x more RAM than the file
544 : // size
545 1 : bTryStandardReading = true;
546 : }
547 1 : return false;
548 : }
549 :
550 241 : oParser.FinalizeLayerDefn();
551 :
552 241 : CPLString osFIDColumn;
553 241 : FinalizeLayerDefn(poLayer, osFIDColumn);
554 241 : if (!osFIDColumn.empty())
555 25 : poLayer->SetFIDColumn(osFIDColumn);
556 :
557 241 : bCanEasilyAppend_ = oParser.CanEasilyAppend();
558 241 : nTotalFeatureCount_ = poLayer->GetFeatureCount(FALSE);
559 241 : nTotalOGRFeatureMemEstimate_ = oParser.GetTotalOGRFeatureMemEstimate();
560 :
561 241 : json_object *poRootObj = oParser.StealRootObject();
562 241 : if (poRootObj)
563 : {
564 241 : bFCHasBBOX_ = CPL_json_object_object_get(poRootObj, "bbox") != nullptr;
565 :
566 : // CPLDebug("GeoJSON", "%s", json_object_get_string(poRootObj));
567 :
568 241 : json_object *poName = CPL_json_object_object_get(poRootObj, "name");
569 241 : if (poName && json_object_get_type(poName) == json_type_string)
570 : {
571 102 : const char *pszValue = json_object_get_string(poName);
572 102 : whileUnsealing(poLayer->GetLayerDefn())->SetName(pszValue);
573 102 : poLayer->SetDescription(pszValue);
574 : }
575 :
576 : json_object *poDescription =
577 241 : CPL_json_object_object_get(poRootObj, "description");
578 242 : if (poDescription &&
579 1 : json_object_get_type(poDescription) == json_type_string)
580 : {
581 1 : const char *pszValue = json_object_get_string(poDescription);
582 1 : poLayer->SetMetadataItem("DESCRIPTION", pszValue);
583 : }
584 :
585 241 : OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poRootObj);
586 241 : const auto eGeomType = poLayer->GetLayerDefn()->GetGeomType();
587 241 : if (eGeomType != wkbNone && poSRS == nullptr)
588 : {
589 : // If there is none defined, we use 4326 / 4979.
590 212 : poSRS = new OGRSpatialReference();
591 212 : if (OGR_GT_HasZ(eGeomType))
592 17 : poSRS->importFromEPSG(4979);
593 : else
594 195 : poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
595 212 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
596 : }
597 241 : CPLErrorReset();
598 :
599 241 : if (eGeomType != wkbNone && poSRS != nullptr)
600 : {
601 241 : auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
602 241 : whileUnsealing(poGeomFieldDefn)->SetSpatialRef(poSRS);
603 : }
604 241 : if (poSRS)
605 241 : poSRS->Release();
606 :
607 241 : SetCoordinatePrecision(poRootObj, poLayer);
608 :
609 241 : if (bStoreNativeData_)
610 : {
611 48 : CPLString osNativeData("NATIVE_DATA=");
612 24 : osNativeData += json_object_get_string(poRootObj);
613 :
614 24 : char *apszMetadata[3] = {
615 24 : const_cast<char *>(osNativeData.c_str()),
616 : const_cast<char *>(
617 : "NATIVE_MEDIA_TYPE=application/vnd.geo+json"),
618 24 : nullptr};
619 :
620 24 : poLayer->SetMetadata(apszMetadata, "NATIVE_DATA");
621 : }
622 :
623 241 : poGJObject_ = poRootObj;
624 : }
625 :
626 241 : fp_ = fp;
627 241 : poDS->AddLayer(poLayer);
628 241 : return true;
629 : }
630 :
631 : /************************************************************************/
632 : /* SkipPrologEpilogAndUpdateJSonPLikeWrapper() */
633 : /************************************************************************/
634 :
635 480 : size_t OGRGeoJSONReader::SkipPrologEpilogAndUpdateJSonPLikeWrapper(size_t nRead)
636 : {
637 480 : size_t nSkip = 0;
638 480 : if (nRead >= 3 && pabyBuffer_[0] == 0xEF && pabyBuffer_[1] == 0xBB &&
639 0 : pabyBuffer_[2] == 0xBF)
640 : {
641 0 : CPLDebug("GeoJSON", "Skip UTF-8 BOM");
642 0 : nSkip += 3;
643 : }
644 :
645 480 : const char *const apszPrefix[] = {"loadGeoJSON(", "jsonp("};
646 1438 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszPrefix); i++)
647 : {
648 959 : if (nRead >= nSkip + strlen(apszPrefix[i]) &&
649 959 : memcmp(pabyBuffer_ + nSkip, apszPrefix[i], strlen(apszPrefix[i])) ==
650 : 0)
651 : {
652 1 : nSkip += strlen(apszPrefix[i]);
653 1 : bJSonPLikeWrapper_ = true;
654 1 : break;
655 : }
656 : }
657 :
658 480 : return nSkip;
659 : }
660 :
661 : /************************************************************************/
662 : /* ResetReading() */
663 : /************************************************************************/
664 :
665 431 : void OGRGeoJSONReader::ResetReading()
666 : {
667 431 : CPLAssert(fp_);
668 431 : if (poStreamingParser_)
669 75 : bOriginalIdModifiedEmitted_ =
670 75 : poStreamingParser_->GetOriginalIdModifiedEmitted();
671 431 : delete poStreamingParser_;
672 431 : poStreamingParser_ = nullptr;
673 431 : }
674 :
675 : /************************************************************************/
676 : /* GetNextFeature() */
677 : /************************************************************************/
678 :
679 940 : OGRFeature *OGRGeoJSONReader::GetNextFeature(OGRGeoJSONLayer *poLayer)
680 : {
681 940 : CPLAssert(fp_);
682 940 : if (poStreamingParser_ == nullptr)
683 : {
684 231 : poStreamingParser_ = new OGRGeoJSONReaderStreamingParser(
685 231 : *this, poLayer, false, bStoreNativeData_);
686 231 : poStreamingParser_->SetOriginalIdModifiedEmitted(
687 231 : bOriginalIdModifiedEmitted_);
688 231 : VSIFSeekL(fp_, 0, SEEK_SET);
689 231 : bFirstSeg_ = true;
690 231 : bJSonPLikeWrapper_ = false;
691 : }
692 :
693 940 : OGRFeature *poFeat = poStreamingParser_->GetNextFeature();
694 940 : if (poFeat)
695 487 : return poFeat;
696 :
697 : while (true)
698 : {
699 453 : size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp_);
700 453 : const bool bFinished = nRead < nBufferSize_;
701 453 : size_t nSkip = 0;
702 453 : if (bFirstSeg_)
703 : {
704 231 : bFirstSeg_ = false;
705 231 : nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
706 : }
707 453 : if (bFinished && bJSonPLikeWrapper_ && nRead - nSkip > 0)
708 0 : nRead--;
709 1359 : if (!poStreamingParser_->Parse(
710 453 : reinterpret_cast<const char *>(pabyBuffer_ + nSkip),
711 906 : nRead - nSkip, bFinished) ||
712 453 : poStreamingParser_->ExceptionOccurred())
713 : {
714 0 : break;
715 : }
716 :
717 453 : poFeat = poStreamingParser_->GetNextFeature();
718 453 : if (poFeat)
719 301 : return poFeat;
720 :
721 152 : if (bFinished)
722 152 : break;
723 0 : }
724 :
725 152 : return nullptr;
726 : }
727 :
728 : /************************************************************************/
729 : /* GetFeature() */
730 : /************************************************************************/
731 :
732 22 : OGRFeature *OGRGeoJSONReader::GetFeature(OGRGeoJSONLayer *poLayer, GIntBig nFID)
733 : {
734 22 : CPLAssert(fp_);
735 :
736 22 : if (oMapFIDToOffsetSize_.empty())
737 : {
738 5 : CPLDebug("GeoJSON",
739 : "Establishing index to features for first GetFeature() call");
740 :
741 5 : if (poStreamingParser_)
742 4 : bOriginalIdModifiedEmitted_ =
743 4 : poStreamingParser_->GetOriginalIdModifiedEmitted();
744 5 : delete poStreamingParser_;
745 5 : poStreamingParser_ = nullptr;
746 :
747 : OGRGeoJSONReaderStreamingParser oParser(*this, poLayer, false,
748 5 : bStoreNativeData_);
749 5 : oParser.SetOriginalIdModifiedEmitted(bOriginalIdModifiedEmitted_);
750 5 : VSIFSeekL(fp_, 0, SEEK_SET);
751 5 : bFirstSeg_ = true;
752 5 : bJSonPLikeWrapper_ = false;
753 5 : vsi_l_offset nCurOffset = 0;
754 5 : vsi_l_offset nFeatureOffset = 0;
755 : while (true)
756 : {
757 5 : size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp_);
758 5 : const bool bFinished = nRead < nBufferSize_;
759 5 : size_t nSkip = 0;
760 5 : if (bFirstSeg_)
761 : {
762 5 : bFirstSeg_ = false;
763 5 : nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
764 : }
765 5 : if (bFinished && bJSonPLikeWrapper_ && nRead - nSkip > 0)
766 0 : nRead--;
767 5 : auto pszPtr = reinterpret_cast<const char *>(pabyBuffer_ + nSkip);
768 1673 : for (size_t i = 0; i < nRead - nSkip; i++)
769 : {
770 1668 : oParser.ResetFeatureDetectionState();
771 1668 : if (!oParser.Parse(pszPtr + i, 1,
772 5004 : bFinished && (i + 1 == nRead - nSkip)) ||
773 1668 : oParser.ExceptionOccurred())
774 : {
775 0 : return nullptr;
776 : }
777 1668 : if (oParser.IsStartFeature())
778 : {
779 18 : nFeatureOffset = nCurOffset + i;
780 : }
781 1650 : else if (oParser.IsEndFeature())
782 : {
783 18 : vsi_l_offset nFeatureSize =
784 18 : (nCurOffset + i) - nFeatureOffset + 1;
785 18 : auto poFeat = oParser.GetNextFeature();
786 18 : if (poFeat)
787 : {
788 18 : const GIntBig nThisFID = poFeat->GetFID();
789 18 : if (oMapFIDToOffsetSize_.find(nThisFID) ==
790 36 : oMapFIDToOffsetSize_.end())
791 : {
792 18 : oMapFIDToOffsetSize_[nThisFID] =
793 36 : std::pair<vsi_l_offset, vsi_l_offset>(
794 18 : nFeatureOffset, nFeatureSize);
795 : }
796 18 : delete poFeat;
797 : }
798 : }
799 : }
800 :
801 5 : if (bFinished)
802 5 : break;
803 0 : nCurOffset += nRead;
804 0 : }
805 :
806 5 : bOriginalIdModifiedEmitted_ = oParser.GetOriginalIdModifiedEmitted();
807 : }
808 :
809 22 : auto oIter = oMapFIDToOffsetSize_.find(nFID);
810 22 : if (oIter == oMapFIDToOffsetSize_.end())
811 : {
812 5 : return nullptr;
813 : }
814 :
815 17 : VSIFSeekL(fp_, oIter->second.first, SEEK_SET);
816 17 : if (oIter->second.second > 1000 * 1000 * 1000)
817 : {
818 0 : return nullptr;
819 : }
820 17 : size_t nSize = static_cast<size_t>(oIter->second.second);
821 17 : char *pszBuffer = static_cast<char *>(VSIMalloc(nSize + 1));
822 17 : if (!pszBuffer)
823 : {
824 0 : return nullptr;
825 : }
826 17 : if (VSIFReadL(pszBuffer, 1, nSize, fp_) != nSize)
827 : {
828 0 : VSIFree(pszBuffer);
829 0 : return nullptr;
830 : }
831 17 : pszBuffer[nSize] = 0;
832 17 : json_object *poObj = nullptr;
833 17 : if (!OGRJSonParse(pszBuffer, &poObj))
834 : {
835 0 : VSIFree(pszBuffer);
836 0 : return nullptr;
837 : }
838 :
839 17 : OGRFeature *poFeat = ReadFeature(poLayer, poObj, pszBuffer);
840 17 : json_object_put(poObj);
841 17 : VSIFree(pszBuffer);
842 17 : if (!poFeat)
843 : {
844 0 : return nullptr;
845 : }
846 17 : poFeat->SetFID(nFID);
847 17 : return poFeat;
848 : }
849 :
850 : /************************************************************************/
851 : /* IngestAll() */
852 : /************************************************************************/
853 :
854 9 : bool OGRGeoJSONReader::IngestAll(OGRGeoJSONLayer *poLayer)
855 : {
856 : const vsi_l_offset nRAM =
857 9 : static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM()) / 3 * 4;
858 9 : if (nRAM && nTotalOGRFeatureMemEstimate_ > nRAM)
859 : {
860 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
861 : "Not enough memory to ingest all the layer: " CPL_FRMT_GUIB
862 : " available, " CPL_FRMT_GUIB " needed",
863 : nRAM, nTotalOGRFeatureMemEstimate_);
864 0 : return false;
865 : }
866 :
867 9 : CPLDebug("GeoJSON",
868 : "Total memory estimated for ingestion: " CPL_FRMT_GUIB " bytes",
869 : nTotalOGRFeatureMemEstimate_);
870 :
871 9 : ResetReading();
872 9 : GIntBig nCounter = 0;
873 : while (true)
874 : {
875 23 : OGRFeature *poFeature = GetNextFeature(poLayer);
876 23 : if (poFeature == nullptr)
877 9 : break;
878 14 : poLayer->AddFeature(poFeature);
879 14 : delete poFeature;
880 14 : nCounter++;
881 14 : if (((nCounter % 10000) == 0 || nCounter == nTotalFeatureCount_) &&
882 7 : nTotalFeatureCount_ > 0)
883 : {
884 7 : CPLDebug("GeoJSON", "Ingestion at %.02f %%",
885 7 : 100.0 * nCounter / nTotalFeatureCount_);
886 : }
887 14 : }
888 9 : return true;
889 : }
890 :
891 : /************************************************************************/
892 : /* ReadLayer */
893 : /************************************************************************/
894 :
895 168 : void OGRGeoJSONReader::ReadLayer(OGRGeoJSONDataSource *poDS,
896 : const char *pszName, json_object *poObj)
897 : {
898 168 : GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
899 168 : if (objType == GeoJSONObject::eUnknown)
900 : {
901 : // Check if the object contains key:value pairs where value
902 : // is a standard GeoJSON object. In which case, use key as the layer
903 : // name.
904 3 : if (json_type_object == json_object_get_type(poObj))
905 : {
906 : json_object_iter it;
907 3 : it.key = nullptr;
908 3 : it.val = nullptr;
909 3 : it.entry = nullptr;
910 8 : json_object_object_foreachC(poObj, it)
911 : {
912 5 : objType = OGRGeoJSONGetType(it.val);
913 5 : if (objType != GeoJSONObject::eUnknown)
914 5 : ReadLayer(poDS, it.key, it.val);
915 : }
916 : }
917 :
918 : // CPLError(CE_Failure, CPLE_AppDefined,
919 : // "Unrecognized GeoJSON structure.");
920 :
921 3 : return;
922 : }
923 :
924 165 : CPLErrorReset();
925 :
926 : // Figure out layer name
927 165 : if (pszName == nullptr)
928 : {
929 160 : if (GeoJSONObject::eFeatureCollection == objType)
930 : {
931 90 : json_object *poName = CPL_json_object_object_get(poObj, "name");
932 93 : if (poName != nullptr &&
933 3 : json_object_get_type(poName) == json_type_string)
934 : {
935 3 : pszName = json_object_get_string(poName);
936 : }
937 : }
938 160 : if (pszName == nullptr)
939 : {
940 157 : const char *pszDesc = poDS->GetDescription();
941 157 : if (strchr(pszDesc, '?') == nullptr &&
942 157 : strchr(pszDesc, '{') == nullptr)
943 : {
944 26 : pszName = CPLGetBasename(pszDesc);
945 : }
946 : }
947 160 : if (pszName == nullptr)
948 131 : pszName = OGRGeoJSONLayer::DefaultName;
949 : }
950 :
951 : OGRGeoJSONLayer *poLayer = new OGRGeoJSONLayer(
952 165 : pszName, nullptr, OGRGeoJSONLayer::DefaultGeometryType, poDS, nullptr);
953 :
954 165 : OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poObj);
955 165 : bool bDefaultSRS = false;
956 165 : if (poSRS == nullptr)
957 : {
958 : // If there is none defined, we use 4326 / 4979.
959 158 : poSRS = new OGRSpatialReference();
960 158 : bDefaultSRS = true;
961 : }
962 : {
963 165 : auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
964 165 : whileUnsealing(poGeomFieldDefn)->SetSpatialRef(poSRS);
965 : }
966 :
967 165 : if (!GenerateLayerDefn(poLayer, poObj))
968 : {
969 1 : CPLError(CE_Failure, CPLE_AppDefined,
970 : "Layer schema generation failed.");
971 :
972 1 : delete poLayer;
973 1 : poSRS->Release();
974 1 : return;
975 : }
976 :
977 164 : if (GeoJSONObject::eFeatureCollection == objType)
978 : {
979 : json_object *poDescription =
980 91 : CPL_json_object_object_get(poObj, "description");
981 92 : if (poDescription != nullptr &&
982 1 : json_object_get_type(poDescription) == json_type_string)
983 : {
984 1 : poLayer->SetMetadataItem("DESCRIPTION",
985 1 : json_object_get_string(poDescription));
986 : }
987 :
988 91 : SetCoordinatePrecision(poObj, poLayer);
989 : }
990 :
991 : /* -------------------------------------------------------------------- */
992 : /* Translate single geometry-only Feature object. */
993 : /* -------------------------------------------------------------------- */
994 :
995 164 : if (GeoJSONObject::ePoint == objType ||
996 117 : GeoJSONObject::eMultiPoint == objType ||
997 115 : GeoJSONObject::eLineString == objType ||
998 114 : GeoJSONObject::eMultiLineString == objType ||
999 112 : GeoJSONObject::ePolygon == objType ||
1000 107 : GeoJSONObject::eMultiPolygon == objType ||
1001 : GeoJSONObject::eGeometryCollection == objType)
1002 : {
1003 59 : OGRGeometry *poGeometry = ReadGeometry(poObj, poLayer->GetSpatialRef());
1004 59 : if (!AddFeature(poLayer, poGeometry))
1005 : {
1006 1 : CPLDebug("GeoJSON", "Translation of single geometry failed.");
1007 1 : delete poLayer;
1008 1 : poSRS->Release();
1009 1 : return;
1010 58 : }
1011 : }
1012 : /* -------------------------------------------------------------------- */
1013 : /* Translate single but complete Feature object. */
1014 : /* -------------------------------------------------------------------- */
1015 105 : else if (GeoJSONObject::eFeature == objType)
1016 : {
1017 14 : OGRFeature *poFeature = ReadFeature(poLayer, poObj, nullptr);
1018 14 : AddFeature(poLayer, poFeature);
1019 : }
1020 : /* -------------------------------------------------------------------- */
1021 : /* Translate multi-feature FeatureCollection object. */
1022 : /* -------------------------------------------------------------------- */
1023 91 : else if (GeoJSONObject::eFeatureCollection == objType)
1024 : {
1025 91 : ReadFeatureCollection(poLayer, poObj);
1026 : }
1027 :
1028 163 : if (CPLGetLastErrorType() != CE_Warning)
1029 160 : CPLErrorReset();
1030 :
1031 163 : poLayer->DetectGeometryType();
1032 :
1033 163 : if (bDefaultSRS && poLayer->GetGeomType() != wkbNone)
1034 : {
1035 156 : if (OGR_GT_HasZ(poLayer->GetGeomType()))
1036 46 : poSRS->importFromEPSG(4979);
1037 : else
1038 110 : poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
1039 156 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1040 : }
1041 163 : poSRS->Release();
1042 :
1043 163 : poDS->AddLayer(poLayer);
1044 : }
1045 :
1046 : /************************************************************************/
1047 : /* OGRGeoJSONReadSpatialReference */
1048 : /************************************************************************/
1049 :
1050 409 : OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj)
1051 : {
1052 :
1053 : /* -------------------------------------------------------------------- */
1054 : /* Read spatial reference definition. */
1055 : /* -------------------------------------------------------------------- */
1056 409 : OGRSpatialReference *poSRS = nullptr;
1057 :
1058 409 : json_object *poObjSrs = OGRGeoJSONFindMemberByName(poObj, "crs");
1059 409 : if (nullptr != poObjSrs)
1060 : {
1061 : json_object *poObjSrsType =
1062 64 : OGRGeoJSONFindMemberByName(poObjSrs, "type");
1063 64 : if (poObjSrsType == nullptr)
1064 1 : return nullptr;
1065 :
1066 63 : const char *pszSrsType = json_object_get_string(poObjSrsType);
1067 :
1068 : // TODO: Add URL and URN types support.
1069 63 : if (STARTS_WITH_CI(pszSrsType, "NAME"))
1070 : {
1071 : json_object *poObjSrsProps =
1072 43 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1073 43 : if (poObjSrsProps == nullptr)
1074 2 : return nullptr;
1075 :
1076 : json_object *poNameURL =
1077 41 : OGRGeoJSONFindMemberByName(poObjSrsProps, "name");
1078 41 : if (poNameURL == nullptr)
1079 2 : return nullptr;
1080 :
1081 39 : const char *pszName = json_object_get_string(poNameURL);
1082 :
1083 : // Mostly to emulate GDAL 2.x behavior
1084 : // See https://github.com/OSGeo/gdal/issues/2035
1085 39 : if (EQUAL(pszName, "urn:ogc:def:crs:OGC:1.3:CRS84"))
1086 9 : pszName = "EPSG:4326";
1087 :
1088 39 : poSRS = new OGRSpatialReference();
1089 39 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1090 39 : if (OGRERR_NONE !=
1091 39 : poSRS->SetFromUserInput(
1092 : pszName,
1093 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()))
1094 : {
1095 2 : delete poSRS;
1096 2 : poSRS = nullptr;
1097 : }
1098 : }
1099 :
1100 20 : else if (STARTS_WITH_CI(pszSrsType, "EPSG"))
1101 : {
1102 : json_object *poObjSrsProps =
1103 7 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1104 7 : if (poObjSrsProps == nullptr)
1105 2 : return nullptr;
1106 :
1107 : json_object *poObjCode =
1108 5 : OGRGeoJSONFindMemberByName(poObjSrsProps, "code");
1109 5 : if (poObjCode == nullptr)
1110 2 : return nullptr;
1111 :
1112 3 : int nEPSG = json_object_get_int(poObjCode);
1113 :
1114 3 : poSRS = new OGRSpatialReference();
1115 3 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1116 3 : if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
1117 : {
1118 2 : delete poSRS;
1119 2 : poSRS = nullptr;
1120 : }
1121 : }
1122 :
1123 13 : else if (STARTS_WITH_CI(pszSrsType, "URL") ||
1124 13 : STARTS_WITH_CI(pszSrsType, "LINK"))
1125 : {
1126 : json_object *poObjSrsProps =
1127 6 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1128 6 : if (poObjSrsProps == nullptr)
1129 2 : return nullptr;
1130 :
1131 : json_object *poObjURL =
1132 4 : OGRGeoJSONFindMemberByName(poObjSrsProps, "url");
1133 :
1134 4 : if (nullptr == poObjURL)
1135 : {
1136 4 : poObjURL = OGRGeoJSONFindMemberByName(poObjSrsProps, "href");
1137 : }
1138 4 : if (poObjURL == nullptr)
1139 2 : return nullptr;
1140 :
1141 2 : const char *pszURL = json_object_get_string(poObjURL);
1142 :
1143 2 : poSRS = new OGRSpatialReference();
1144 2 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1145 2 : if (OGRERR_NONE != poSRS->importFromUrl(pszURL))
1146 : {
1147 2 : delete poSRS;
1148 2 : poSRS = nullptr;
1149 2 : }
1150 : }
1151 :
1152 7 : else if (EQUAL(pszSrsType, "OGC"))
1153 : {
1154 : json_object *poObjSrsProps =
1155 7 : OGRGeoJSONFindMemberByName(poObjSrs, "properties");
1156 7 : if (poObjSrsProps == nullptr)
1157 2 : return nullptr;
1158 :
1159 : json_object *poObjURN =
1160 5 : OGRGeoJSONFindMemberByName(poObjSrsProps, "urn");
1161 5 : if (poObjURN == nullptr)
1162 2 : return nullptr;
1163 :
1164 3 : poSRS = new OGRSpatialReference();
1165 3 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1166 3 : if (OGRERR_NONE !=
1167 3 : poSRS->importFromURN(json_object_get_string(poObjURN)))
1168 : {
1169 2 : delete poSRS;
1170 2 : poSRS = nullptr;
1171 : }
1172 : }
1173 : }
1174 :
1175 : // Strip AXIS, since geojson has (easting, northing) / (longitude, latitude)
1176 : // order. According to http://www.geojson.org/geojson-spec.html#id2 :
1177 : // "Point coordinates are in x, y order (easting, northing for projected
1178 : // coordinates, longitude, latitude for geographic coordinates)".
1179 392 : if (poSRS != nullptr)
1180 : {
1181 39 : OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode("GEOGCS");
1182 39 : if (poGEOGCS != nullptr)
1183 39 : poGEOGCS->StripNodes("AXIS");
1184 : }
1185 :
1186 392 : return poSRS;
1187 : }
1188 :
1189 : /************************************************************************/
1190 : /* GenerateLayerDefn() */
1191 : /************************************************************************/
1192 :
1193 165 : bool OGRGeoJSONReader::GenerateLayerDefn(OGRGeoJSONLayer *poLayer,
1194 : json_object *poGJObject)
1195 : {
1196 165 : CPLAssert(nullptr != poGJObject);
1197 165 : CPLAssert(nullptr != poLayer->GetLayerDefn());
1198 165 : CPLAssert(0 == poLayer->GetLayerDefn()->GetFieldCount());
1199 :
1200 165 : if (bAttributesSkip_)
1201 0 : return true;
1202 :
1203 : /* -------------------------------------------------------------------- */
1204 : /* Scan all features and generate layer definition. */
1205 : /* -------------------------------------------------------------------- */
1206 165 : bool bSuccess = true;
1207 :
1208 330 : std::map<std::string, int> oMapFieldNameToIdx;
1209 330 : std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
1210 330 : gdal::DirectedAcyclicGraph<int, std::string> dag;
1211 :
1212 165 : GeoJSONObject::Type objType = OGRGeoJSONGetType(poGJObject);
1213 165 : if (GeoJSONObject::eFeature == objType)
1214 : {
1215 14 : bSuccess = GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
1216 : poLayer, poGJObject);
1217 : }
1218 151 : else if (GeoJSONObject::eFeatureCollection == objType)
1219 : {
1220 : json_object *poObjFeatures =
1221 92 : OGRGeoJSONFindMemberByName(poGJObject, "features");
1222 184 : if (nullptr != poObjFeatures &&
1223 92 : json_type_array == json_object_get_type(poObjFeatures))
1224 : {
1225 92 : const auto nFeatures = json_object_array_length(poObjFeatures);
1226 207 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
1227 : {
1228 : json_object *poObjFeature =
1229 115 : json_object_array_get_idx(poObjFeatures, i);
1230 115 : if (!GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
1231 : poLayer, poObjFeature))
1232 : {
1233 1 : CPLDebug("GeoJSON", "Create feature schema failure.");
1234 1 : bSuccess = false;
1235 : }
1236 : }
1237 : }
1238 : else
1239 : {
1240 0 : CPLError(CE_Failure, CPLE_AppDefined,
1241 : "Invalid FeatureCollection object. "
1242 : "Missing \'features\' member.");
1243 0 : bSuccess = false;
1244 : }
1245 : }
1246 :
1247 : // Note: the current strategy will not produce stable output, depending
1248 : // on the order of features, if there are conflicting order / cycles.
1249 : // See https://github.com/OSGeo/gdal/pull/4552 for a number of potential
1250 : // resolutions if that has to be solved in the future.
1251 165 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
1252 330 : const auto sortedFields = dag.getTopologicalOrdering();
1253 165 : CPLAssert(sortedFields.size() == apoFieldDefn.size());
1254 : {
1255 330 : auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
1256 266 : for (int idx : sortedFields)
1257 : {
1258 101 : poDefn->AddFieldDefn(apoFieldDefn[idx].get());
1259 : }
1260 : }
1261 :
1262 165 : CPLString osFIDColumn;
1263 165 : FinalizeLayerDefn(poLayer, osFIDColumn);
1264 165 : if (!osFIDColumn.empty())
1265 8 : poLayer->SetFIDColumn(osFIDColumn);
1266 :
1267 165 : return bSuccess;
1268 : }
1269 :
1270 : /************************************************************************/
1271 : /* FinalizeLayerDefn() */
1272 : /************************************************************************/
1273 :
1274 465 : void OGRGeoJSONBaseReader::FinalizeLayerDefn(OGRLayer *poLayer,
1275 : CPLString &osFIDColumn)
1276 : {
1277 : /* -------------------------------------------------------------------- */
1278 : /* Validate and add FID column if necessary. */
1279 : /* -------------------------------------------------------------------- */
1280 465 : osFIDColumn.clear();
1281 465 : OGRFeatureDefn *poLayerDefn = poLayer->GetLayerDefn();
1282 465 : CPLAssert(nullptr != poLayerDefn);
1283 :
1284 465 : whileUnsealing(poLayerDefn)->SetGeomType(m_eLayerGeomType);
1285 :
1286 465 : if (m_bNeedFID64)
1287 : {
1288 4 : poLayer->SetMetadataItem(OLMD_FID64, "YES");
1289 : }
1290 :
1291 465 : if (!bFeatureLevelIdAsFID_)
1292 : {
1293 443 : const int idx = poLayerDefn->GetFieldIndexCaseSensitive("id");
1294 443 : if (idx >= 0)
1295 : {
1296 64 : OGRFieldDefn *poFDefn = poLayerDefn->GetFieldDefn(idx);
1297 97 : if (poFDefn->GetType() == OFTInteger ||
1298 33 : poFDefn->GetType() == OFTInteger64)
1299 : {
1300 33 : osFIDColumn = poLayerDefn->GetFieldDefn(idx)->GetNameRef();
1301 : }
1302 : }
1303 : }
1304 465 : }
1305 :
1306 : /************************************************************************/
1307 : /* OGRGeoJSONReaderAddOrUpdateField() */
1308 : /************************************************************************/
1309 :
1310 4630 : void OGRGeoJSONReaderAddOrUpdateField(
1311 : std::vector<int> &retIndices,
1312 : std::map<std::string, int> &oMapFieldNameToIdx,
1313 : std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
1314 : const char *pszKey, json_object *poVal, bool bFlattenNestedAttributes,
1315 : char chNestedAttributeSeparator, bool bArrayAsString, bool bDateAsString,
1316 : std::set<int> &aoSetUndeterminedTypeFields)
1317 : {
1318 4630 : const auto jType = json_object_get_type(poVal);
1319 4630 : if (bFlattenNestedAttributes && poVal != nullptr &&
1320 : jType == json_type_object)
1321 : {
1322 : json_object_iter it;
1323 2 : it.key = nullptr;
1324 2 : it.val = nullptr;
1325 2 : it.entry = nullptr;
1326 6 : json_object_object_foreachC(poVal, it)
1327 : {
1328 4 : char szSeparator[2] = {chNestedAttributeSeparator, '\0'};
1329 :
1330 : CPLString osAttrName(
1331 8 : CPLSPrintf("%s%s%s", pszKey, szSeparator, it.key));
1332 8 : if (it.val != nullptr &&
1333 4 : json_object_get_type(it.val) == json_type_object)
1334 : {
1335 0 : OGRGeoJSONReaderAddOrUpdateField(
1336 : retIndices, oMapFieldNameToIdx, apoFieldDefn, osAttrName,
1337 : it.val, true, chNestedAttributeSeparator, bArrayAsString,
1338 : bDateAsString, aoSetUndeterminedTypeFields);
1339 : }
1340 : else
1341 : {
1342 4 : OGRGeoJSONReaderAddOrUpdateField(
1343 : retIndices, oMapFieldNameToIdx, apoFieldDefn, osAttrName,
1344 : it.val, false, 0, bArrayAsString, bDateAsString,
1345 : aoSetUndeterminedTypeFields);
1346 : }
1347 : }
1348 2 : return;
1349 : }
1350 :
1351 4628 : auto oMapFieldNameToIdxIter = oMapFieldNameToIdx.find(pszKey);
1352 4628 : if (oMapFieldNameToIdxIter == oMapFieldNameToIdx.end())
1353 : {
1354 : OGRFieldSubType eSubType;
1355 : const OGRFieldType eType =
1356 970 : GeoJSONPropertyToFieldType(poVal, eSubType, bArrayAsString);
1357 1940 : auto poFieldDefn = std::make_unique<OGRFieldDefn>(pszKey, eType);
1358 970 : poFieldDefn->SetSubType(eSubType);
1359 970 : if (eSubType == OFSTBoolean)
1360 34 : poFieldDefn->SetWidth(1);
1361 970 : if (poFieldDefn->GetType() == OFTString && !bDateAsString)
1362 : {
1363 470 : int nTZFlag = 0;
1364 470 : poFieldDefn->SetType(
1365 : GeoJSONStringPropertyToFieldType(poVal, nTZFlag));
1366 470 : poFieldDefn->SetTZFlag(nTZFlag);
1367 : }
1368 970 : apoFieldDefn.emplace_back(std::move(poFieldDefn));
1369 970 : const int nIndex = static_cast<int>(apoFieldDefn.size()) - 1;
1370 970 : retIndices.emplace_back(nIndex);
1371 970 : oMapFieldNameToIdx[pszKey] = nIndex;
1372 970 : if (poVal == nullptr)
1373 7 : aoSetUndeterminedTypeFields.insert(nIndex);
1374 : }
1375 3658 : else if (poVal)
1376 : {
1377 3643 : const int nIndex = oMapFieldNameToIdxIter->second;
1378 3643 : retIndices.emplace_back(nIndex);
1379 : // If there is a null value: do not update field definition.
1380 3643 : OGRFieldDefn *poFDefn = apoFieldDefn[nIndex].get();
1381 3643 : const OGRFieldType eType = poFDefn->GetType();
1382 3643 : const OGRFieldSubType eSubType = poFDefn->GetSubType();
1383 : OGRFieldSubType eNewSubType;
1384 : OGRFieldType eNewType =
1385 3643 : GeoJSONPropertyToFieldType(poVal, eNewSubType, bArrayAsString);
1386 : const bool bNewIsEmptyArray =
1387 3643 : (jType == json_type_array && json_object_array_length(poVal) == 0);
1388 3643 : if (aoSetUndeterminedTypeFields.find(nIndex) !=
1389 7286 : aoSetUndeterminedTypeFields.end())
1390 : {
1391 6 : poFDefn->SetSubType(OFSTNone);
1392 6 : poFDefn->SetType(eNewType);
1393 6 : if (poFDefn->GetType() == OFTString && !bDateAsString)
1394 : {
1395 2 : int nTZFlag = 0;
1396 2 : poFDefn->SetType(
1397 : GeoJSONStringPropertyToFieldType(poVal, nTZFlag));
1398 2 : poFDefn->SetTZFlag(nTZFlag);
1399 : }
1400 6 : poFDefn->SetSubType(eNewSubType);
1401 6 : aoSetUndeterminedTypeFields.erase(nIndex);
1402 : }
1403 3637 : else if (eType == OFTInteger)
1404 : {
1405 2200 : if (eNewType == OFTInteger && eSubType == OFSTBoolean &&
1406 5 : eNewSubType != OFSTBoolean)
1407 : {
1408 2 : poFDefn->SetSubType(OFSTNone);
1409 : }
1410 2198 : else if (eNewType == OFTInteger64 || eNewType == OFTReal ||
1411 2184 : eNewType == OFTInteger64List || eNewType == OFTRealList ||
1412 : eNewType == OFTStringList)
1413 : {
1414 18 : poFDefn->SetSubType(OFSTNone);
1415 18 : poFDefn->SetType(eNewType);
1416 : }
1417 2180 : else if (eNewType == OFTIntegerList)
1418 : {
1419 4 : if (eSubType == OFSTBoolean && eNewSubType != OFSTBoolean)
1420 : {
1421 1 : poFDefn->SetSubType(OFSTNone);
1422 : }
1423 4 : poFDefn->SetType(eNewType);
1424 : }
1425 2176 : else if (eNewType != OFTInteger)
1426 : {
1427 14 : poFDefn->SetSubType(OFSTNone);
1428 14 : poFDefn->SetType(OFTString);
1429 14 : poFDefn->SetSubType(OFSTJSON);
1430 : }
1431 : }
1432 1437 : else if (eType == OFTInteger64)
1433 : {
1434 15 : if (eNewType == OFTReal)
1435 : {
1436 2 : poFDefn->SetSubType(OFSTNone);
1437 2 : poFDefn->SetType(eNewType);
1438 : }
1439 13 : else if (eNewType == OFTIntegerList || eNewType == OFTInteger64List)
1440 : {
1441 3 : poFDefn->SetSubType(OFSTNone);
1442 3 : poFDefn->SetType(OFTInteger64List);
1443 : }
1444 10 : else if (eNewType == OFTRealList || eNewType == OFTStringList)
1445 : {
1446 2 : poFDefn->SetSubType(OFSTNone);
1447 2 : poFDefn->SetType(eNewType);
1448 : }
1449 8 : else if (eNewType != OFTInteger && eNewType != OFTInteger64)
1450 : {
1451 2 : poFDefn->SetSubType(OFSTNone);
1452 2 : poFDefn->SetType(OFTString);
1453 2 : poFDefn->SetSubType(OFSTJSON);
1454 : }
1455 : }
1456 1422 : else if (eType == OFTReal)
1457 : {
1458 395 : if (eNewType == OFTIntegerList || eNewType == OFTInteger64List ||
1459 : eNewType == OFTRealList)
1460 : {
1461 4 : poFDefn->SetSubType(OFSTNone);
1462 4 : poFDefn->SetType(OFTRealList);
1463 : }
1464 391 : else if (eNewType == OFTStringList)
1465 : {
1466 1 : poFDefn->SetSubType(OFSTNone);
1467 1 : poFDefn->SetType(OFTStringList);
1468 : }
1469 390 : else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
1470 : eNewType != OFTReal)
1471 : {
1472 2 : poFDefn->SetSubType(OFSTNone);
1473 2 : poFDefn->SetType(OFTString);
1474 2 : poFDefn->SetSubType(OFSTJSON);
1475 : }
1476 : }
1477 1027 : else if (eType == OFTString)
1478 : {
1479 754 : if (eSubType == OFSTNone)
1480 : {
1481 740 : if (eNewType == OFTStringList)
1482 : {
1483 1 : poFDefn->SetType(OFTStringList);
1484 : }
1485 739 : else if (eNewType != OFTString)
1486 : {
1487 15 : poFDefn->SetSubType(OFSTJSON);
1488 : }
1489 : }
1490 : }
1491 273 : else if (eType == OFTIntegerList)
1492 : {
1493 50 : if (eNewType == OFTString)
1494 : {
1495 6 : if (!bNewIsEmptyArray)
1496 : {
1497 6 : poFDefn->SetSubType(OFSTNone);
1498 6 : poFDefn->SetType(eNewType);
1499 6 : poFDefn->SetSubType(OFSTJSON);
1500 : }
1501 : }
1502 44 : else if (eNewType == OFTInteger64List || eNewType == OFTRealList ||
1503 : eNewType == OFTStringList)
1504 : {
1505 19 : poFDefn->SetSubType(OFSTNone);
1506 19 : poFDefn->SetType(eNewType);
1507 : }
1508 25 : else if (eNewType == OFTInteger64)
1509 : {
1510 2 : poFDefn->SetSubType(OFSTNone);
1511 2 : poFDefn->SetType(OFTInteger64List);
1512 : }
1513 23 : else if (eNewType == OFTReal)
1514 : {
1515 2 : poFDefn->SetSubType(OFSTNone);
1516 2 : poFDefn->SetType(OFTRealList);
1517 : }
1518 21 : else if (eNewType == OFTInteger || eNewType == OFTIntegerList)
1519 : {
1520 21 : if (eSubType == OFSTBoolean && eNewSubType != OFSTBoolean)
1521 : {
1522 2 : poFDefn->SetSubType(OFSTNone);
1523 : }
1524 : }
1525 : else
1526 : {
1527 0 : poFDefn->SetSubType(OFSTNone);
1528 0 : poFDefn->SetType(OFTString);
1529 0 : poFDefn->SetSubType(OFSTJSON);
1530 : }
1531 : }
1532 223 : else if (eType == OFTInteger64List)
1533 : {
1534 24 : if (eNewType == OFTString)
1535 : {
1536 3 : if (!bNewIsEmptyArray)
1537 : {
1538 3 : poFDefn->SetSubType(OFSTNone);
1539 3 : poFDefn->SetType(eNewType);
1540 3 : poFDefn->SetSubType(OFSTJSON);
1541 : }
1542 : }
1543 21 : else if (eNewType == OFTInteger64List || eNewType == OFTRealList ||
1544 : eNewType == OFTStringList)
1545 : {
1546 9 : poFDefn->SetSubType(OFSTNone);
1547 9 : poFDefn->SetType(eNewType);
1548 : }
1549 12 : else if (eNewType == OFTReal)
1550 : {
1551 1 : poFDefn->SetSubType(OFSTNone);
1552 1 : poFDefn->SetType(OFTRealList);
1553 : }
1554 11 : else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
1555 : eNewType != OFTIntegerList)
1556 : {
1557 0 : poFDefn->SetSubType(OFSTNone);
1558 0 : poFDefn->SetType(OFTString);
1559 0 : poFDefn->SetSubType(OFSTJSON);
1560 : }
1561 : }
1562 199 : else if (eType == OFTRealList)
1563 : {
1564 24 : if (eNewType == OFTString)
1565 : {
1566 3 : if (!bNewIsEmptyArray)
1567 : {
1568 3 : poFDefn->SetSubType(OFSTNone);
1569 3 : poFDefn->SetType(eNewType);
1570 3 : poFDefn->SetSubType(OFSTJSON);
1571 : }
1572 : }
1573 21 : else if (eNewType == OFTStringList)
1574 : {
1575 1 : poFDefn->SetSubType(OFSTNone);
1576 1 : poFDefn->SetType(eNewType);
1577 : }
1578 20 : else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
1579 16 : eNewType != OFTReal && eNewType != OFTIntegerList &&
1580 4 : eNewType != OFTInteger64List && eNewType != OFTRealList)
1581 : {
1582 0 : poFDefn->SetSubType(OFSTNone);
1583 0 : poFDefn->SetType(OFTString);
1584 0 : poFDefn->SetSubType(OFSTJSON);
1585 : }
1586 : }
1587 175 : else if (eType == OFTStringList)
1588 : {
1589 30 : if (eNewType == OFTString && eNewSubType == OFSTJSON)
1590 : {
1591 1 : if (!bNewIsEmptyArray)
1592 : {
1593 1 : poFDefn->SetSubType(OFSTNone);
1594 1 : poFDefn->SetType(eNewType);
1595 1 : poFDefn->SetSubType(OFSTJSON);
1596 : }
1597 : }
1598 : }
1599 145 : else if (eType == OFTDate || eType == OFTTime || eType == OFTDateTime)
1600 : {
1601 145 : if (eNewType == OFTString && !bDateAsString &&
1602 144 : eNewSubType == OFSTNone)
1603 : {
1604 143 : int nTZFlag = 0;
1605 143 : eNewType = GeoJSONStringPropertyToFieldType(poVal, nTZFlag);
1606 150 : if (poFDefn->GetTZFlag() > OGR_TZFLAG_UNKNOWN &&
1607 7 : nTZFlag != poFDefn->GetTZFlag())
1608 : {
1609 5 : if (nTZFlag == OGR_TZFLAG_UNKNOWN)
1610 3 : poFDefn->SetTZFlag(OGR_TZFLAG_UNKNOWN);
1611 : else
1612 2 : poFDefn->SetTZFlag(OGR_TZFLAG_MIXED_TZ);
1613 : }
1614 : }
1615 145 : if (eType != eNewType)
1616 : {
1617 8 : poFDefn->SetSubType(OFSTNone);
1618 8 : if (eNewType == OFTString)
1619 : {
1620 5 : poFDefn->SetType(eNewType);
1621 5 : poFDefn->SetSubType(eNewSubType);
1622 : }
1623 3 : else if (eType == OFTDate && eNewType == OFTDateTime)
1624 : {
1625 1 : poFDefn->SetType(OFTDateTime);
1626 : }
1627 2 : else if (!(eType == OFTDateTime && eNewType == OFTDate))
1628 : {
1629 1 : poFDefn->SetType(OFTString);
1630 1 : poFDefn->SetSubType(OFSTJSON);
1631 : }
1632 : }
1633 : }
1634 :
1635 3643 : poFDefn->SetWidth(poFDefn->GetSubType() == OFSTBoolean ? 1 : 0);
1636 : }
1637 : else
1638 : {
1639 15 : const int nIndex = oMapFieldNameToIdxIter->second;
1640 15 : retIndices.emplace_back(nIndex);
1641 : }
1642 : }
1643 :
1644 : /************************************************************************/
1645 : /* OGRGeoJSONGenerateFeatureDefnDealWithID() */
1646 : /************************************************************************/
1647 :
1648 3325 : void OGRGeoJSONGenerateFeatureDefnDealWithID(
1649 : json_object *poObj, json_object *poObjProps, int &nPrevFieldIdx,
1650 : std::map<std::string, int> &oMapFieldNameToIdx,
1651 : std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
1652 : gdal::DirectedAcyclicGraph<int, std::string> &dag,
1653 : bool &bFeatureLevelIdAsFID, bool &bFeatureLevelIdAsAttribute,
1654 : bool &bNeedFID64)
1655 : {
1656 3325 : json_object *poObjId = OGRGeoJSONFindMemberByName(poObj, "id");
1657 3325 : if (poObjId)
1658 : {
1659 154 : auto iterIdxId = oMapFieldNameToIdx.find("id");
1660 154 : if (iterIdxId == oMapFieldNameToIdx.end())
1661 : {
1662 100 : if (json_object_get_type(poObjId) == json_type_int)
1663 : {
1664 : // If the value is negative, we cannot use it as the FID
1665 : // as OGRMemLayer doesn't support negative FID. And we would
1666 : // have an ambiguity with -1 that can mean OGRNullFID
1667 : // so in that case create a regular attribute and let OGR
1668 : // attribute sequential OGR FIDs.
1669 66 : if (json_object_get_int64(poObjId) < 0)
1670 : {
1671 5 : bFeatureLevelIdAsFID = false;
1672 : }
1673 : else
1674 : {
1675 61 : bFeatureLevelIdAsFID = true;
1676 : }
1677 : }
1678 100 : if (!bFeatureLevelIdAsFID)
1679 : {
1680 : // If there's a top-level id of type string or negative int,
1681 : // and no properties.id, then declare a id field.
1682 39 : bool bHasRegularIdProp = false;
1683 76 : if (nullptr != poObjProps &&
1684 37 : json_object_get_type(poObjProps) == json_type_object)
1685 : {
1686 37 : bHasRegularIdProp =
1687 37 : CPL_json_object_object_get(poObjProps, "id") != nullptr;
1688 : }
1689 39 : if (!bHasRegularIdProp)
1690 : {
1691 36 : OGRFieldType eType = OFTString;
1692 36 : if (json_object_get_type(poObjId) == json_type_int)
1693 : {
1694 5 : if (CPL_INT64_FITS_ON_INT32(
1695 : json_object_get_int64(poObjId)))
1696 4 : eType = OFTInteger;
1697 : else
1698 1 : eType = OFTInteger64;
1699 : }
1700 : apoFieldDefn.emplace_back(
1701 36 : std::make_unique<OGRFieldDefn>("id", eType));
1702 36 : const int nIdx = static_cast<int>(apoFieldDefn.size()) - 1;
1703 36 : oMapFieldNameToIdx["id"] = nIdx;
1704 36 : nPrevFieldIdx = nIdx;
1705 36 : dag.addNode(nIdx, "id");
1706 36 : bFeatureLevelIdAsAttribute = true;
1707 : }
1708 : }
1709 : }
1710 : else
1711 : {
1712 54 : const int nIdx = iterIdxId->second;
1713 54 : nPrevFieldIdx = nIdx;
1714 71 : if (bFeatureLevelIdAsAttribute &&
1715 17 : json_object_get_type(poObjId) == json_type_int)
1716 : {
1717 3 : if (apoFieldDefn[nIdx]->GetType() == OFTInteger)
1718 : {
1719 1 : if (!CPL_INT64_FITS_ON_INT32(
1720 : json_object_get_int64(poObjId)))
1721 1 : apoFieldDefn[nIdx]->SetType(OFTInteger64);
1722 : }
1723 : }
1724 51 : else if (bFeatureLevelIdAsAttribute)
1725 : {
1726 14 : apoFieldDefn[nIdx]->SetType(OFTString);
1727 : }
1728 : }
1729 : }
1730 :
1731 3325 : if (!bNeedFID64)
1732 : {
1733 3324 : json_object *poId = CPL_json_object_object_get(poObj, "id");
1734 3324 : if (poId == nullptr)
1735 : {
1736 6297 : if (poObjProps &&
1737 3126 : json_object_get_type(poObjProps) == json_type_object)
1738 : {
1739 3125 : poId = CPL_json_object_object_get(poObjProps, "id");
1740 : }
1741 : }
1742 3324 : if (poId != nullptr && json_object_get_type(poId) == json_type_int)
1743 : {
1744 147 : GIntBig nFID = json_object_get_int64(poId);
1745 147 : if (!CPL_INT64_FITS_ON_INT32(nFID))
1746 : {
1747 4 : bNeedFID64 = true;
1748 : }
1749 : }
1750 : }
1751 3325 : }
1752 :
1753 : /************************************************************************/
1754 : /* GenerateFeatureDefn() */
1755 : /************************************************************************/
1756 3111 : bool OGRGeoJSONBaseReader::GenerateFeatureDefn(
1757 : std::map<std::string, int> &oMapFieldNameToIdx,
1758 : std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
1759 : gdal::DirectedAcyclicGraph<int, std::string> &dag, OGRLayer *poLayer,
1760 : json_object *poObj)
1761 : {
1762 : /* -------------------------------------------------------------------- */
1763 : /* Read collection of properties. */
1764 : /* -------------------------------------------------------------------- */
1765 : lh_entry *poObjPropsEntry =
1766 3111 : OGRGeoJSONFindMemberEntryByName(poObj, "properties");
1767 3111 : json_object *poObjProps =
1768 3111 : const_cast<json_object *>(static_cast<const json_object *>(
1769 : poObjPropsEntry ? poObjPropsEntry->v : nullptr));
1770 :
1771 6222 : std::vector<int> anCurFieldIndices;
1772 3111 : int nPrevFieldIdx = -1;
1773 :
1774 3111 : OGRGeoJSONGenerateFeatureDefnDealWithID(
1775 : poObj, poObjProps, nPrevFieldIdx, oMapFieldNameToIdx, apoFieldDefn, dag,
1776 3111 : bFeatureLevelIdAsFID_, bFeatureLevelIdAsAttribute_, m_bNeedFID64);
1777 :
1778 3111 : json_object *poGeomObj = CPL_json_object_object_get(poObj, "geometry");
1779 3111 : if (poGeomObj && json_object_get_type(poGeomObj) == json_type_object)
1780 : {
1781 2888 : const auto eType = OGRGeoJSONGetOGRGeometryType(poGeomObj);
1782 :
1783 2888 : OGRGeoJSONUpdateLayerGeomType(m_bFirstGeometry, eType,
1784 2888 : m_eLayerGeomType);
1785 :
1786 2888 : if (eType != wkbNone && eType != wkbUnknown)
1787 : {
1788 : // This is maybe too optimistic: it assumes that the geometry
1789 : // coordinates array is in the correct format
1790 2888 : m_bExtentRead |= OGRGeoJSONGetExtent3D(poGeomObj, &m_oEnvelope3D);
1791 : }
1792 : }
1793 :
1794 3111 : bool bSuccess = false;
1795 :
1796 6178 : if (nullptr != poObjProps &&
1797 3067 : json_object_get_type(poObjProps) == json_type_object)
1798 : {
1799 3066 : if (bIsGeocouchSpatiallistFormat)
1800 : {
1801 3 : poObjProps = CPL_json_object_object_get(poObjProps, "properties");
1802 6 : if (nullptr == poObjProps ||
1803 3 : json_object_get_type(poObjProps) != json_type_object)
1804 : {
1805 2 : return true;
1806 : }
1807 : }
1808 :
1809 : json_object_iter it;
1810 3066 : it.key = nullptr;
1811 3066 : it.val = nullptr;
1812 3066 : it.entry = nullptr;
1813 7348 : json_object_object_foreachC(poObjProps, it)
1814 : {
1815 8559 : if (!bIsGeocouchSpatiallistFormat &&
1816 8559 : oMapFieldNameToIdx.find(it.key) == oMapFieldNameToIdx.end())
1817 : {
1818 : // Detect the special kind of GeoJSON output by a spatiallist of
1819 : // GeoCouch such as:
1820 : // http://gd.iriscouch.com/cphosm/_design/geo/_rewrite/data?bbox=12.53%2C55.73%2C12.54%2C55.73
1821 841 : if (strcmp(it.key, "_id") == 0)
1822 : {
1823 2 : bFoundGeocouchId = true;
1824 : }
1825 839 : else if (bFoundGeocouchId && strcmp(it.key, "_rev") == 0)
1826 : {
1827 2 : bFoundRev = true;
1828 : }
1829 4 : else if (bFoundRev && strcmp(it.key, "type") == 0 &&
1830 2 : it.val != nullptr &&
1831 843 : json_object_get_type(it.val) == json_type_string &&
1832 2 : strcmp(json_object_get_string(it.val), "Feature") == 0)
1833 : {
1834 2 : bFoundTypeFeature = true;
1835 : }
1836 1672 : else if (bFoundTypeFeature &&
1837 2 : strcmp(it.key, "properties") == 0 &&
1838 839 : it.val != nullptr &&
1839 2 : json_object_get_type(it.val) == json_type_object)
1840 : {
1841 2 : if (bFlattenGeocouchSpatiallistFormat < 0)
1842 2 : bFlattenGeocouchSpatiallistFormat =
1843 2 : CPLTestBool(CPLGetConfigOption(
1844 : "GEOJSON_FLATTEN_GEOCOUCH", "TRUE"));
1845 2 : if (bFlattenGeocouchSpatiallistFormat)
1846 : {
1847 2 : auto typeIter = oMapFieldNameToIdx.find("type");
1848 2 : if (typeIter != oMapFieldNameToIdx.end())
1849 : {
1850 2 : const int nIdx = typeIter->second;
1851 2 : apoFieldDefn.erase(apoFieldDefn.begin() + nIdx);
1852 2 : oMapFieldNameToIdx.erase(typeIter);
1853 2 : dag.removeNode(nIdx);
1854 : }
1855 :
1856 2 : bIsGeocouchSpatiallistFormat = true;
1857 2 : return GenerateFeatureDefn(oMapFieldNameToIdx,
1858 : apoFieldDefn, dag, poLayer,
1859 2 : poObj);
1860 : }
1861 : }
1862 : }
1863 :
1864 4282 : anCurFieldIndices.clear();
1865 4282 : OGRGeoJSONReaderAddOrUpdateField(
1866 4282 : anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, it.key,
1867 4282 : it.val, bFlattenNestedAttributes_, chNestedAttributeSeparator_,
1868 4282 : bArrayAsString_, bDateAsString_, aoSetUndeterminedTypeFields_);
1869 8566 : for (int idx : anCurFieldIndices)
1870 : {
1871 4284 : dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
1872 4284 : if (nPrevFieldIdx != -1)
1873 : {
1874 2004 : dag.addEdge(nPrevFieldIdx, idx);
1875 : }
1876 4284 : nPrevFieldIdx = idx;
1877 : }
1878 : }
1879 :
1880 3064 : bSuccess = true; // SUCCESS
1881 : }
1882 46 : else if (nullptr != poObjPropsEntry &&
1883 1 : (poObjProps == nullptr ||
1884 1 : (json_object_get_type(poObjProps) == json_type_array &&
1885 1 : json_object_array_length(poObjProps) == 0)))
1886 : {
1887 : // Ignore "properties": null and "properties": []
1888 13 : bSuccess = true;
1889 : }
1890 63 : else if (poObj != nullptr &&
1891 31 : json_object_get_type(poObj) == json_type_object)
1892 : {
1893 : json_object_iter it;
1894 31 : it.key = nullptr;
1895 31 : it.val = nullptr;
1896 31 : it.entry = nullptr;
1897 97 : json_object_object_foreachC(poObj, it)
1898 : {
1899 66 : if (strcmp(it.key, "type") != 0 &&
1900 35 : strcmp(it.key, "geometry") != 0 &&
1901 4 : strcmp(it.key, "centroid") != 0 &&
1902 4 : strcmp(it.key, "bbox") != 0 && strcmp(it.key, "center") != 0)
1903 : {
1904 4 : if (oMapFieldNameToIdx.find(it.key) == oMapFieldNameToIdx.end())
1905 : {
1906 4 : anCurFieldIndices.clear();
1907 4 : OGRGeoJSONReaderAddOrUpdateField(
1908 : anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn,
1909 4 : it.key, it.val, bFlattenNestedAttributes_,
1910 4 : chNestedAttributeSeparator_, bArrayAsString_,
1911 4 : bDateAsString_, aoSetUndeterminedTypeFields_);
1912 8 : for (int idx : anCurFieldIndices)
1913 : {
1914 4 : dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
1915 4 : if (nPrevFieldIdx != -1)
1916 : {
1917 0 : dag.addEdge(nPrevFieldIdx, idx);
1918 : }
1919 4 : nPrevFieldIdx = idx;
1920 : }
1921 : }
1922 : }
1923 : }
1924 :
1925 31 : bSuccess = true; // SUCCESS
1926 : // CPLError(CE_Failure, CPLE_AppDefined,
1927 : // "Invalid Feature object. "
1928 : // "Missing \'properties\' member." );
1929 : }
1930 :
1931 3109 : return bSuccess;
1932 : }
1933 :
1934 : /************************************************************************/
1935 : /* OGRGeoJSONUpdateLayerGeomType() */
1936 : /************************************************************************/
1937 :
1938 3092 : bool OGRGeoJSONUpdateLayerGeomType(bool &bFirstGeom,
1939 : OGRwkbGeometryType eGeomType,
1940 : OGRwkbGeometryType &eLayerGeomType)
1941 : {
1942 3092 : if (bFirstGeom)
1943 : {
1944 399 : eLayerGeomType = eGeomType;
1945 399 : bFirstGeom = false;
1946 : }
1947 2704 : else if (OGR_GT_HasZ(eGeomType) && !OGR_GT_HasZ(eLayerGeomType) &&
1948 11 : wkbFlatten(eGeomType) == wkbFlatten(eLayerGeomType))
1949 : {
1950 2 : eLayerGeomType = eGeomType;
1951 : }
1952 2694 : else if (!OGR_GT_HasZ(eGeomType) && OGR_GT_HasZ(eLayerGeomType) &&
1953 3 : wkbFlatten(eGeomType) == wkbFlatten(eLayerGeomType))
1954 : {
1955 : // ok
1956 : }
1957 2689 : else if (eGeomType != eLayerGeomType)
1958 : {
1959 92 : CPLDebug("GeoJSON", "Detected layer of mixed-geometry type features.");
1960 92 : eLayerGeomType = wkbUnknown;
1961 92 : return false;
1962 : }
1963 3000 : return true;
1964 : }
1965 :
1966 : /************************************************************************/
1967 : /* AddFeature */
1968 : /************************************************************************/
1969 :
1970 59 : bool OGRGeoJSONReader::AddFeature(OGRGeoJSONLayer *poLayer,
1971 : OGRGeometry *poGeometry)
1972 : {
1973 59 : bool bAdded = false;
1974 :
1975 : // TODO: Should we check if geometry is of type of wkbGeometryCollection?
1976 :
1977 59 : if (nullptr != poGeometry)
1978 : {
1979 58 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
1980 58 : poFeature->SetGeometryDirectly(poGeometry);
1981 :
1982 58 : bAdded = AddFeature(poLayer, poFeature);
1983 : }
1984 :
1985 59 : return bAdded;
1986 : }
1987 :
1988 : /************************************************************************/
1989 : /* AddFeature */
1990 : /************************************************************************/
1991 :
1992 186 : bool OGRGeoJSONReader::AddFeature(OGRGeoJSONLayer *poLayer,
1993 : OGRFeature *poFeature)
1994 : {
1995 186 : if (poFeature == nullptr)
1996 0 : return false;
1997 :
1998 186 : poLayer->AddFeature(poFeature);
1999 186 : delete poFeature;
2000 :
2001 186 : return true;
2002 : }
2003 :
2004 : /************************************************************************/
2005 : /* ReadGeometry */
2006 : /************************************************************************/
2007 :
2008 871 : OGRGeometry *OGRGeoJSONBaseReader::ReadGeometry(json_object *poObj,
2009 : OGRSpatialReference *poLayerSRS)
2010 : {
2011 871 : OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj, poLayerSRS);
2012 :
2013 : /* -------------------------------------------------------------------- */
2014 : /* Wrap geometry with GeometryCollection as a common denominator. */
2015 : /* Sometimes a GeoJSON text may consist of objects of different */
2016 : /* geometry types. Users may request wrapping all geometries with */
2017 : /* OGRGeometryCollection type by using option */
2018 : /* GEOMETRY_AS_COLLECTION=NO|YES (NO is default). */
2019 : /* -------------------------------------------------------------------- */
2020 871 : if (nullptr != poGeometry)
2021 : {
2022 856 : if (!bGeometryPreserve_ &&
2023 0 : wkbGeometryCollection != poGeometry->getGeometryType())
2024 : {
2025 0 : OGRGeometryCollection *poMetaGeometry = new OGRGeometryCollection();
2026 0 : poMetaGeometry->addGeometryDirectly(poGeometry);
2027 0 : return poMetaGeometry;
2028 : }
2029 : }
2030 :
2031 871 : return poGeometry;
2032 : }
2033 :
2034 : /************************************************************************/
2035 : /* OGRGeoJSONReaderSetFieldNestedAttribute() */
2036 : /************************************************************************/
2037 :
2038 2 : static void OGRGeoJSONReaderSetFieldNestedAttribute(OGRLayer *poLayer,
2039 : OGRFeature *poFeature,
2040 : const char *pszAttrPrefix,
2041 : char chSeparator,
2042 : json_object *poVal)
2043 : {
2044 : json_object_iter it;
2045 2 : it.key = nullptr;
2046 2 : it.val = nullptr;
2047 2 : it.entry = nullptr;
2048 6 : json_object_object_foreachC(poVal, it)
2049 : {
2050 4 : const char szSeparator[2] = {chSeparator, '\0'};
2051 : const CPLString osAttrName(
2052 8 : CPLSPrintf("%s%s%s", pszAttrPrefix, szSeparator, it.key));
2053 8 : if (it.val != nullptr &&
2054 4 : json_object_get_type(it.val) == json_type_object)
2055 : {
2056 0 : OGRGeoJSONReaderSetFieldNestedAttribute(
2057 : poLayer, poFeature, osAttrName, chSeparator, it.val);
2058 : }
2059 : else
2060 : {
2061 : const int nField =
2062 4 : poFeature->GetDefnRef()->GetFieldIndexCaseSensitive(osAttrName);
2063 4 : OGRGeoJSONReaderSetField(poLayer, poFeature, nField, osAttrName,
2064 : it.val, false, 0);
2065 : }
2066 : }
2067 2 : }
2068 :
2069 : /************************************************************************/
2070 : /* OGRGeoJSONReaderSetField() */
2071 : /************************************************************************/
2072 :
2073 4769 : void OGRGeoJSONReaderSetField(OGRLayer *poLayer, OGRFeature *poFeature,
2074 : int nField, const char *pszAttrPrefix,
2075 : json_object *poVal, bool bFlattenNestedAttributes,
2076 : char chNestedAttributeSeparator)
2077 : {
2078 4773 : if (bFlattenNestedAttributes && poVal != nullptr &&
2079 4 : json_object_get_type(poVal) == json_type_object)
2080 : {
2081 2 : OGRGeoJSONReaderSetFieldNestedAttribute(
2082 : poLayer, poFeature, pszAttrPrefix, chNestedAttributeSeparator,
2083 : poVal);
2084 2 : return;
2085 : }
2086 4767 : if (nField < 0)
2087 0 : return;
2088 :
2089 4767 : OGRFieldDefn *poFieldDefn = poFeature->GetFieldDefnRef(nField);
2090 4767 : CPLAssert(nullptr != poFieldDefn);
2091 4767 : OGRFieldType eType = poFieldDefn->GetType();
2092 :
2093 4767 : if (poVal == nullptr)
2094 : {
2095 85 : poFeature->SetFieldNull(nField);
2096 : }
2097 4682 : else if (OFTInteger == eType)
2098 : {
2099 1286 : poFeature->SetField(nField, json_object_get_int(poVal));
2100 :
2101 : // Check if FID available and set correct value.
2102 1286 : if (EQUAL(poFieldDefn->GetNameRef(), poLayer->GetFIDColumn()))
2103 144 : poFeature->SetFID(json_object_get_int(poVal));
2104 : }
2105 3396 : else if (OFTInteger64 == eType)
2106 : {
2107 26 : poFeature->SetField(nField, (GIntBig)json_object_get_int64(poVal));
2108 :
2109 : // Check if FID available and set correct value.
2110 26 : if (EQUAL(poFieldDefn->GetNameRef(), poLayer->GetFIDColumn()))
2111 0 : poFeature->SetFID(
2112 0 : static_cast<GIntBig>(json_object_get_int64(poVal)));
2113 : }
2114 3370 : else if (OFTReal == eType)
2115 : {
2116 1148 : poFeature->SetField(nField, json_object_get_double(poVal));
2117 : }
2118 2222 : else if (OFTIntegerList == eType)
2119 : {
2120 58 : const enum json_type eJSonType(json_object_get_type(poVal));
2121 58 : if (eJSonType == json_type_array)
2122 : {
2123 50 : const auto nLength = json_object_array_length(poVal);
2124 50 : int *panVal = static_cast<int *>(CPLMalloc(sizeof(int) * nLength));
2125 105 : for (auto i = decltype(nLength){0}; i < nLength; i++)
2126 : {
2127 55 : json_object *poRow = json_object_array_get_idx(poVal, i);
2128 55 : panVal[i] = json_object_get_int(poRow);
2129 : }
2130 50 : poFeature->SetField(nField, static_cast<int>(nLength), panVal);
2131 50 : CPLFree(panVal);
2132 : }
2133 8 : else if (eJSonType == json_type_boolean || eJSonType == json_type_int)
2134 : {
2135 8 : poFeature->SetField(nField, json_object_get_int(poVal));
2136 : }
2137 : }
2138 2164 : else if (OFTInteger64List == eType)
2139 : {
2140 69 : const enum json_type eJSonType(json_object_get_type(poVal));
2141 69 : if (eJSonType == json_type_array)
2142 : {
2143 59 : const auto nLength = json_object_array_length(poVal);
2144 : GIntBig *panVal =
2145 59 : static_cast<GIntBig *>(CPLMalloc(sizeof(GIntBig) * nLength));
2146 125 : for (auto i = decltype(nLength){0}; i < nLength; i++)
2147 : {
2148 66 : json_object *poRow = json_object_array_get_idx(poVal, i);
2149 66 : panVal[i] = static_cast<GIntBig>(json_object_get_int64(poRow));
2150 : }
2151 59 : poFeature->SetField(nField, static_cast<int>(nLength), panVal);
2152 59 : CPLFree(panVal);
2153 : }
2154 10 : else if (eJSonType == json_type_boolean || eJSonType == json_type_int)
2155 : {
2156 10 : poFeature->SetField(
2157 10 : nField, static_cast<GIntBig>(json_object_get_int64(poVal)));
2158 : }
2159 : }
2160 2095 : else if (OFTRealList == eType)
2161 : {
2162 95 : const enum json_type eJSonType(json_object_get_type(poVal));
2163 95 : if (eJSonType == json_type_array)
2164 : {
2165 81 : const auto nLength = json_object_array_length(poVal);
2166 : double *padfVal =
2167 81 : static_cast<double *>(CPLMalloc(sizeof(double) * nLength));
2168 172 : for (auto i = decltype(nLength){0}; i < nLength; i++)
2169 : {
2170 91 : json_object *poRow = json_object_array_get_idx(poVal, i);
2171 91 : padfVal[i] = json_object_get_double(poRow);
2172 : }
2173 81 : poFeature->SetField(nField, static_cast<int>(nLength), padfVal);
2174 81 : CPLFree(padfVal);
2175 : }
2176 14 : else if (eJSonType == json_type_boolean || eJSonType == json_type_int ||
2177 : eJSonType == json_type_double)
2178 : {
2179 14 : poFeature->SetField(nField, json_object_get_double(poVal));
2180 : }
2181 : }
2182 2000 : else if (OFTStringList == eType)
2183 : {
2184 79 : const enum json_type eJSonType(json_object_get_type(poVal));
2185 79 : if (eJSonType == json_type_array)
2186 : {
2187 69 : auto nLength = json_object_array_length(poVal);
2188 : char **papszVal =
2189 69 : (char **)CPLMalloc(sizeof(char *) * (nLength + 1));
2190 69 : decltype(nLength) i = 0; // Used after for.
2191 141 : for (; i < nLength; i++)
2192 : {
2193 72 : json_object *poRow = json_object_array_get_idx(poVal, i);
2194 72 : const char *pszVal = json_object_get_string(poRow);
2195 72 : if (pszVal == nullptr)
2196 0 : break;
2197 72 : papszVal[i] = CPLStrdup(pszVal);
2198 : }
2199 69 : papszVal[i] = nullptr;
2200 69 : poFeature->SetField(nField, papszVal);
2201 69 : CSLDestroy(papszVal);
2202 : }
2203 : else
2204 : {
2205 10 : poFeature->SetField(nField, json_object_get_string(poVal));
2206 : }
2207 : }
2208 : else
2209 : {
2210 1921 : poFeature->SetField(nField, json_object_get_string(poVal));
2211 : }
2212 : }
2213 :
2214 : /************************************************************************/
2215 : /* ReadFeature() */
2216 : /************************************************************************/
2217 :
2218 1158 : OGRFeature *OGRGeoJSONBaseReader::ReadFeature(OGRLayer *poLayer,
2219 : json_object *poObj,
2220 : const char *pszSerializedObj)
2221 : {
2222 1158 : CPLAssert(nullptr != poObj);
2223 :
2224 1158 : OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
2225 1158 : OGRFeature *poFeature = new OGRFeature(poFDefn);
2226 :
2227 1158 : if (bStoreNativeData_)
2228 : {
2229 56 : poFeature->SetNativeData(pszSerializedObj
2230 : ? pszSerializedObj
2231 10 : : json_object_to_json_string(poObj));
2232 46 : poFeature->SetNativeMediaType("application/vnd.geo+json");
2233 : }
2234 :
2235 : /* -------------------------------------------------------------------- */
2236 : /* Translate GeoJSON "properties" object to feature attributes. */
2237 : /* -------------------------------------------------------------------- */
2238 1158 : CPLAssert(nullptr != poFeature);
2239 :
2240 1158 : json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
2241 2276 : if (!bAttributesSkip_ && nullptr != poObjProps &&
2242 1118 : json_object_get_type(poObjProps) == json_type_object)
2243 : {
2244 1117 : if (bIsGeocouchSpatiallistFormat)
2245 : {
2246 3 : json_object *poId = CPL_json_object_object_get(poObjProps, "_id");
2247 6 : if (poId != nullptr &&
2248 3 : json_object_get_type(poId) == json_type_string)
2249 3 : poFeature->SetField("_id", json_object_get_string(poId));
2250 :
2251 3 : json_object *poRev = CPL_json_object_object_get(poObjProps, "_rev");
2252 6 : if (poRev != nullptr &&
2253 3 : json_object_get_type(poRev) == json_type_string)
2254 : {
2255 3 : poFeature->SetField("_rev", json_object_get_string(poRev));
2256 : }
2257 :
2258 3 : poObjProps = CPL_json_object_object_get(poObjProps, "properties");
2259 6 : if (nullptr == poObjProps ||
2260 3 : json_object_get_type(poObjProps) != json_type_object)
2261 : {
2262 0 : return poFeature;
2263 : }
2264 : }
2265 :
2266 : json_object_iter it;
2267 1117 : it.key = nullptr;
2268 1117 : it.val = nullptr;
2269 1117 : it.entry = nullptr;
2270 4373 : json_object_object_foreachC(poObjProps, it)
2271 : {
2272 3256 : const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
2273 3258 : if (nField < 0 &&
2274 2 : !(bFlattenNestedAttributes_ && it.val != nullptr &&
2275 2 : json_object_get_type(it.val) == json_type_object))
2276 : {
2277 0 : CPLDebug("GeoJSON", "Cannot find field %s", it.key);
2278 : }
2279 : else
2280 : {
2281 3256 : OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key,
2282 3256 : it.val, bFlattenNestedAttributes_,
2283 3256 : chNestedAttributeSeparator_);
2284 : }
2285 : }
2286 : }
2287 :
2288 1158 : if (!bAttributesSkip_ && nullptr == poObjProps)
2289 : {
2290 : json_object_iter it;
2291 40 : it.key = nullptr;
2292 40 : it.val = nullptr;
2293 40 : it.entry = nullptr;
2294 134 : json_object_object_foreachC(poObj, it)
2295 : {
2296 94 : const int nFldIndex = poFDefn->GetFieldIndexCaseSensitive(it.key);
2297 94 : if (nFldIndex >= 0)
2298 : {
2299 4 : if (it.val)
2300 4 : poFeature->SetField(nFldIndex,
2301 : json_object_get_string(it.val));
2302 : else
2303 0 : poFeature->SetFieldNull(nFldIndex);
2304 : }
2305 : }
2306 : }
2307 :
2308 : /* -------------------------------------------------------------------- */
2309 : /* Try to use feature-level ID if available */
2310 : /* and of integral type. Otherwise, leave unset (-1) then index */
2311 : /* in features sequence will be used as FID. */
2312 : /* -------------------------------------------------------------------- */
2313 1158 : json_object *poObjId = OGRGeoJSONFindMemberByName(poObj, "id");
2314 1158 : if (nullptr != poObjId && bFeatureLevelIdAsFID_)
2315 : {
2316 51 : poFeature->SetFID(static_cast<GIntBig>(json_object_get_int64(poObjId)));
2317 : }
2318 :
2319 : /* -------------------------------------------------------------------- */
2320 : /* Handle the case where the special id is in a regular field. */
2321 : /* -------------------------------------------------------------------- */
2322 1107 : else if (nullptr != poObjId)
2323 : {
2324 43 : const int nIdx = poFDefn->GetFieldIndexCaseSensitive("id");
2325 43 : if (nIdx >= 0 && !poFeature->IsFieldSet(nIdx))
2326 : {
2327 39 : poFeature->SetField(nIdx, json_object_get_string(poObjId));
2328 : }
2329 : }
2330 :
2331 : /* -------------------------------------------------------------------- */
2332 : /* Translate geometry sub-object of GeoJSON Feature. */
2333 : /* -------------------------------------------------------------------- */
2334 1158 : json_object *poObjGeom = nullptr;
2335 1158 : json_object *poTmp = poObj;
2336 : json_object_iter it;
2337 1158 : it.key = nullptr;
2338 1158 : it.val = nullptr;
2339 1158 : it.entry = nullptr;
2340 4361 : json_object_object_foreachC(poTmp, it)
2341 : {
2342 3532 : if (EQUAL(it.key, "geometry"))
2343 : {
2344 1137 : if (it.val != nullptr)
2345 808 : poObjGeom = it.val;
2346 : // Done. They had 'geometry':null.
2347 : else
2348 329 : return poFeature;
2349 : }
2350 : }
2351 :
2352 829 : if (nullptr != poObjGeom)
2353 : {
2354 : // NOTE: If geometry can not be parsed or read correctly
2355 : // then NULL geometry is assigned to a feature and
2356 : // geometry type for layer is classified as wkbUnknown.
2357 : OGRGeometry *poGeometry =
2358 808 : ReadGeometry(poObjGeom, poLayer->GetSpatialRef());
2359 808 : if (nullptr != poGeometry)
2360 : {
2361 794 : poFeature->SetGeometryDirectly(poGeometry);
2362 : }
2363 : }
2364 : else
2365 : {
2366 : static bool bWarned = false;
2367 21 : if (!bWarned)
2368 : {
2369 1 : bWarned = true;
2370 1 : CPLDebug(
2371 : "GeoJSON",
2372 : "Non conformant Feature object. Missing \'geometry\' member.");
2373 : }
2374 : }
2375 :
2376 829 : return poFeature;
2377 : }
2378 :
2379 : /************************************************************************/
2380 : /* Extent getters */
2381 : /************************************************************************/
2382 :
2383 26 : bool OGRGeoJSONBaseReader::ExtentRead() const
2384 : {
2385 26 : return m_bExtentRead;
2386 : }
2387 :
2388 20 : OGREnvelope3D OGRGeoJSONBaseReader::GetExtent3D() const
2389 : {
2390 20 : return m_oEnvelope3D;
2391 : }
2392 :
2393 : /************************************************************************/
2394 : /* ReadFeatureCollection() */
2395 : /************************************************************************/
2396 :
2397 91 : void OGRGeoJSONReader::ReadFeatureCollection(OGRGeoJSONLayer *poLayer,
2398 : json_object *poObj)
2399 : {
2400 91 : json_object *poObjFeatures = OGRGeoJSONFindMemberByName(poObj, "features");
2401 91 : if (nullptr == poObjFeatures)
2402 : {
2403 0 : CPLError(CE_Failure, CPLE_AppDefined,
2404 : "Invalid FeatureCollection object. "
2405 : "Missing \'features\' member.");
2406 0 : return;
2407 : }
2408 :
2409 91 : if (json_type_array == json_object_get_type(poObjFeatures))
2410 : {
2411 91 : const auto nFeatures = json_object_array_length(poObjFeatures);
2412 205 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
2413 : {
2414 : json_object *poObjFeature =
2415 114 : json_object_array_get_idx(poObjFeatures, i);
2416 114 : OGRFeature *poFeature = ReadFeature(poLayer, poObjFeature, nullptr);
2417 114 : AddFeature(poLayer, poFeature);
2418 : }
2419 : }
2420 :
2421 : // Collect top objects except 'type' and the 'features' array.
2422 91 : if (bStoreNativeData_)
2423 : {
2424 : json_object_iter it;
2425 5 : it.key = nullptr;
2426 5 : it.val = nullptr;
2427 5 : it.entry = nullptr;
2428 10 : CPLString osNativeData;
2429 25 : json_object_object_foreachC(poObj, it)
2430 : {
2431 20 : if (strcmp(it.key, "type") == 0 || strcmp(it.key, "features") == 0)
2432 : {
2433 10 : continue;
2434 : }
2435 10 : if (osNativeData.empty())
2436 3 : osNativeData = "{ ";
2437 : else
2438 7 : osNativeData += ", ";
2439 10 : json_object *poKey = json_object_new_string(it.key);
2440 10 : osNativeData += json_object_to_json_string(poKey);
2441 10 : json_object_put(poKey);
2442 10 : osNativeData += ": ";
2443 10 : osNativeData += json_object_to_json_string(it.val);
2444 : }
2445 5 : if (osNativeData.empty())
2446 : {
2447 2 : osNativeData = "{ ";
2448 : }
2449 5 : osNativeData += " }";
2450 :
2451 5 : osNativeData = "NATIVE_DATA=" + osNativeData;
2452 :
2453 5 : char *apszMetadata[3] = {
2454 5 : const_cast<char *>(osNativeData.c_str()),
2455 : const_cast<char *>("NATIVE_MEDIA_TYPE=application/vnd.geo+json"),
2456 5 : nullptr};
2457 :
2458 5 : poLayer->SetMetadata(apszMetadata, "NATIVE_DATA");
2459 : }
2460 : }
2461 :
2462 : /************************************************************************/
2463 : /* OGRGeoJSONFindMemberByName */
2464 : /************************************************************************/
2465 :
2466 18789 : lh_entry *OGRGeoJSONFindMemberEntryByName(json_object *poObj,
2467 : const char *pszName)
2468 : {
2469 18789 : if (nullptr == pszName || nullptr == poObj)
2470 4 : return nullptr;
2471 :
2472 18785 : if (nullptr != json_object_get_object(poObj))
2473 : {
2474 18779 : lh_entry *entry = json_object_get_object(poObj)->head;
2475 46528 : while (entry != nullptr)
2476 : {
2477 40074 : if (EQUAL(static_cast<const char *>(entry->k), pszName))
2478 12325 : return entry;
2479 27749 : entry = entry->next;
2480 : }
2481 : }
2482 :
2483 6460 : return nullptr;
2484 : }
2485 :
2486 14178 : json_object *OGRGeoJSONFindMemberByName(json_object *poObj, const char *pszName)
2487 : {
2488 14178 : lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, pszName);
2489 14178 : if (nullptr == entry)
2490 4936 : return nullptr;
2491 9242 : return (json_object *)entry->v;
2492 : }
2493 :
2494 : /************************************************************************/
2495 : /* OGRGeoJSONGetType */
2496 : /************************************************************************/
2497 :
2498 2466 : GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj)
2499 : {
2500 2466 : if (nullptr == poObj)
2501 0 : return GeoJSONObject::eUnknown;
2502 :
2503 2466 : json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
2504 2466 : if (nullptr == poObjType)
2505 19 : return GeoJSONObject::eUnknown;
2506 :
2507 2447 : const char *name = json_object_get_string(poObjType);
2508 2447 : if (EQUAL(name, "Point"))
2509 360 : return GeoJSONObject::ePoint;
2510 2087 : else if (EQUAL(name, "LineString"))
2511 53 : return GeoJSONObject::eLineString;
2512 2034 : else if (EQUAL(name, "Polygon"))
2513 614 : return GeoJSONObject::ePolygon;
2514 1420 : else if (EQUAL(name, "MultiPoint"))
2515 138 : return GeoJSONObject::eMultiPoint;
2516 1282 : else if (EQUAL(name, "MultiLineString"))
2517 34 : return GeoJSONObject::eMultiLineString;
2518 1248 : else if (EQUAL(name, "MultiPolygon"))
2519 415 : return GeoJSONObject::eMultiPolygon;
2520 833 : else if (EQUAL(name, "GeometryCollection"))
2521 14 : return GeoJSONObject::eGeometryCollection;
2522 819 : else if (EQUAL(name, "Feature"))
2523 543 : return GeoJSONObject::eFeature;
2524 276 : else if (EQUAL(name, "FeatureCollection"))
2525 276 : return GeoJSONObject::eFeatureCollection;
2526 : else
2527 0 : return GeoJSONObject::eUnknown;
2528 : }
2529 :
2530 : /************************************************************************/
2531 : /* OGRGeoJSONGetOGRGeometryType() */
2532 : /************************************************************************/
2533 :
2534 3035 : OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj)
2535 : {
2536 3035 : if (nullptr == poObj)
2537 1 : return wkbUnknown;
2538 :
2539 3034 : json_object *poObjType = CPL_json_object_object_get(poObj, "type");
2540 3034 : if (nullptr == poObjType)
2541 0 : return wkbUnknown;
2542 :
2543 3034 : OGRwkbGeometryType eType = wkbUnknown;
2544 3034 : const char *name = json_object_get_string(poObjType);
2545 3034 : if (EQUAL(name, "Point"))
2546 318 : eType = wkbPoint;
2547 2716 : else if (EQUAL(name, "LineString"))
2548 49 : eType = wkbLineString;
2549 2667 : else if (EQUAL(name, "Polygon"))
2550 2367 : eType = wkbPolygon;
2551 300 : else if (EQUAL(name, "MultiPoint"))
2552 25 : eType = wkbMultiPoint;
2553 275 : else if (EQUAL(name, "MultiLineString"))
2554 28 : eType = wkbMultiLineString;
2555 247 : else if (EQUAL(name, "MultiPolygon"))
2556 223 : eType = wkbMultiPolygon;
2557 24 : else if (EQUAL(name, "GeometryCollection"))
2558 19 : eType = wkbGeometryCollection;
2559 : else
2560 5 : return wkbUnknown;
2561 :
2562 : json_object *poCoordinates;
2563 3029 : if (eType == wkbGeometryCollection)
2564 : {
2565 : json_object *poGeometries =
2566 19 : CPL_json_object_object_get(poObj, "geometries");
2567 37 : if (poGeometries &&
2568 37 : json_object_get_type(poGeometries) == json_type_array &&
2569 18 : json_object_array_length(poGeometries) > 0)
2570 : {
2571 17 : if (OGR_GT_HasZ(OGRGeoJSONGetOGRGeometryType(
2572 17 : json_object_array_get_idx(poGeometries, 0))))
2573 7 : eType = OGR_GT_SetZ(eType);
2574 : }
2575 : }
2576 : else
2577 : {
2578 3010 : poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
2579 6014 : if (poCoordinates &&
2580 6014 : json_object_get_type(poCoordinates) == json_type_array &&
2581 3004 : json_object_array_length(poCoordinates) > 0)
2582 : {
2583 : while (true)
2584 : {
2585 8492 : auto poChild = json_object_array_get_idx(poCoordinates, 0);
2586 16974 : if (!(poChild &&
2587 8482 : json_object_get_type(poChild) == json_type_array &&
2588 5497 : json_object_array_length(poChild) > 0))
2589 : {
2590 2999 : if (json_object_array_length(poCoordinates) == 3)
2591 111 : eType = OGR_GT_SetZ(eType);
2592 2999 : break;
2593 : }
2594 5493 : poCoordinates = poChild;
2595 5493 : }
2596 : }
2597 : }
2598 :
2599 3029 : return eType;
2600 : }
2601 :
2602 : /************************************************************************/
2603 : /* OGRGeoJSONReadGeometry */
2604 : /************************************************************************/
2605 :
2606 1500 : OGRGeometry *OGRGeoJSONReadGeometry(json_object *poObj,
2607 : OGRSpatialReference *poParentSRS)
2608 : {
2609 :
2610 1500 : OGRGeometry *poGeometry = nullptr;
2611 1500 : OGRSpatialReference *poSRS = nullptr;
2612 1500 : lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, "crs");
2613 1500 : if (entry != nullptr)
2614 : {
2615 4 : json_object *poObjSrs = (json_object *)entry->v;
2616 4 : if (poObjSrs != nullptr)
2617 : {
2618 3 : poSRS = OGRGeoJSONReadSpatialReference(poObj);
2619 : }
2620 : }
2621 :
2622 1500 : OGRSpatialReference *poSRSToAssign = nullptr;
2623 1500 : if (entry != nullptr)
2624 : {
2625 4 : poSRSToAssign = poSRS;
2626 : }
2627 1496 : else if (poParentSRS)
2628 : {
2629 882 : poSRSToAssign = poParentSRS;
2630 : }
2631 : else
2632 : {
2633 : // Assign WGS84 if no CRS defined on geometry.
2634 614 : poSRSToAssign = OGRSpatialReference::GetWGS84SRS();
2635 : }
2636 :
2637 1500 : GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
2638 1500 : if (GeoJSONObject::ePoint == objType)
2639 336 : poGeometry = OGRGeoJSONReadPoint(poObj);
2640 1164 : else if (GeoJSONObject::eMultiPoint == objType)
2641 58 : poGeometry = OGRGeoJSONReadMultiPoint(poObj);
2642 1106 : else if (GeoJSONObject::eLineString == objType)
2643 49 : poGeometry = OGRGeoJSONReadLineString(poObj);
2644 1057 : else if (GeoJSONObject::eMultiLineString == objType)
2645 32 : poGeometry = OGRGeoJSONReadMultiLineString(poObj);
2646 1025 : else if (GeoJSONObject::ePolygon == objType)
2647 610 : poGeometry = OGRGeoJSONReadPolygon(poObj);
2648 415 : else if (GeoJSONObject::eMultiPolygon == objType)
2649 405 : poGeometry = OGRGeoJSONReadMultiPolygon(poObj);
2650 10 : else if (GeoJSONObject::eGeometryCollection == objType)
2651 10 : poGeometry = OGRGeoJSONReadGeometryCollection(poObj, poSRSToAssign);
2652 : else
2653 : {
2654 0 : CPLDebug("GeoJSON", "Unsupported geometry type detected. "
2655 : "Feature gets NULL geometry assigned.");
2656 : }
2657 :
2658 1500 : if (poGeometry && GeoJSONObject::eGeometryCollection != objType)
2659 1476 : poGeometry->assignSpatialReference(poSRSToAssign);
2660 :
2661 1500 : if (poSRS)
2662 3 : poSRS->Release();
2663 :
2664 1500 : return poGeometry;
2665 : }
2666 :
2667 : /************************************************************************/
2668 : /* OGRGeoJSONGetCoordinate() */
2669 : /************************************************************************/
2670 :
2671 161563 : static double OGRGeoJSONGetCoordinate(json_object *poObj,
2672 : const char *pszCoordName, int nIndex,
2673 : bool &bValid)
2674 : {
2675 161563 : json_object *poObjCoord = json_object_array_get_idx(poObj, nIndex);
2676 161563 : if (nullptr == poObjCoord)
2677 : {
2678 5 : CPLDebug("GeoJSON", "Point: got null object for %s.", pszCoordName);
2679 5 : bValid = false;
2680 5 : return 0.0;
2681 : }
2682 :
2683 161558 : const int iType = json_object_get_type(poObjCoord);
2684 161558 : if (json_type_double != iType && json_type_int != iType)
2685 : {
2686 0 : CPLError(CE_Failure, CPLE_AppDefined,
2687 : "Invalid '%s' coordinate. "
2688 : "Type is not double or integer for \'%s\'.",
2689 : pszCoordName, json_object_to_json_string(poObjCoord));
2690 0 : bValid = false;
2691 0 : return 0.0;
2692 : }
2693 :
2694 161558 : return json_object_get_double(poObjCoord);
2695 : }
2696 :
2697 : /************************************************************************/
2698 : /* OGRGeoJSONReadRawPoint */
2699 : /************************************************************************/
2700 :
2701 80588 : bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point)
2702 : {
2703 80588 : CPLAssert(nullptr != poObj);
2704 :
2705 80588 : if (json_type_array == json_object_get_type(poObj))
2706 : {
2707 80588 : const auto nSize = json_object_array_length(poObj);
2708 :
2709 80588 : if (nSize < GeoJSONObject::eMinCoordinateDimension)
2710 : {
2711 1 : CPLDebug("GeoJSON", "Invalid coord dimension. "
2712 : "At least 2 dimensions must be present.");
2713 1 : return false;
2714 : }
2715 :
2716 80587 : bool bValid = true;
2717 80587 : const double dfX = OGRGeoJSONGetCoordinate(poObj, "x", 0, bValid);
2718 80587 : const double dfY = OGRGeoJSONGetCoordinate(poObj, "y", 1, bValid);
2719 80587 : point.setX(dfX);
2720 80587 : point.setY(dfY);
2721 :
2722 : // Read Z coordinate.
2723 80587 : if (nSize >= GeoJSONObject::eMaxCoordinateDimension)
2724 : {
2725 : // Don't *expect* mixed-dimension geometries, although the
2726 : // spec doesn't explicitly forbid this.
2727 389 : const double dfZ = OGRGeoJSONGetCoordinate(poObj, "z", 2, bValid);
2728 389 : point.setZ(dfZ);
2729 : }
2730 : else
2731 : {
2732 80198 : point.flattenTo2D();
2733 : }
2734 80587 : return bValid;
2735 : }
2736 :
2737 0 : return false;
2738 : }
2739 :
2740 : /************************************************************************/
2741 : /* OGRGeoJSONReadPoint */
2742 : /************************************************************************/
2743 :
2744 336 : OGRPoint *OGRGeoJSONReadPoint(json_object *poObj)
2745 : {
2746 336 : CPLAssert(nullptr != poObj);
2747 :
2748 336 : json_object *poObjCoords = OGRGeoJSONFindMemberByName(poObj, "coordinates");
2749 336 : if (nullptr == poObjCoords)
2750 : {
2751 2 : CPLError(CE_Failure, CPLE_AppDefined,
2752 : "Invalid Point object. Missing \'coordinates\' member.");
2753 2 : return nullptr;
2754 : }
2755 :
2756 334 : OGRPoint *poPoint = new OGRPoint();
2757 334 : if (!OGRGeoJSONReadRawPoint(poObjCoords, *poPoint))
2758 : {
2759 5 : CPLDebug("GeoJSON", "Point: raw point parsing failure.");
2760 5 : delete poPoint;
2761 5 : return nullptr;
2762 : }
2763 :
2764 329 : return poPoint;
2765 : }
2766 :
2767 : /************************************************************************/
2768 : /* OGRGeoJSONReadMultiPoint */
2769 : /************************************************************************/
2770 :
2771 58 : OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj)
2772 : {
2773 58 : CPLAssert(nullptr != poObj);
2774 :
2775 58 : json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
2776 58 : if (nullptr == poObjPoints)
2777 : {
2778 1 : CPLError(CE_Failure, CPLE_AppDefined,
2779 : "Invalid MultiPoint object. "
2780 : "Missing \'coordinates\' member.");
2781 1 : return nullptr;
2782 : }
2783 :
2784 57 : OGRMultiPoint *poMultiPoint = nullptr;
2785 57 : if (json_type_array == json_object_get_type(poObjPoints))
2786 : {
2787 57 : const auto nPoints = json_object_array_length(poObjPoints);
2788 :
2789 57 : poMultiPoint = new OGRMultiPoint();
2790 :
2791 259 : for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
2792 : {
2793 : json_object *poObjCoords =
2794 202 : json_object_array_get_idx(poObjPoints, i);
2795 :
2796 202 : OGRPoint pt;
2797 401 : if (poObjCoords != nullptr &&
2798 199 : !OGRGeoJSONReadRawPoint(poObjCoords, pt))
2799 : {
2800 0 : delete poMultiPoint;
2801 0 : CPLDebug("GeoJSON", "LineString: raw point parsing failure.");
2802 0 : return nullptr;
2803 : }
2804 202 : poMultiPoint->addGeometry(&pt);
2805 : }
2806 : }
2807 :
2808 57 : return poMultiPoint;
2809 : }
2810 :
2811 : /************************************************************************/
2812 : /* OGRGeoJSONReadLineString */
2813 : /************************************************************************/
2814 :
2815 102 : OGRLineString *OGRGeoJSONReadLineString(json_object *poObj, bool bRaw)
2816 : {
2817 102 : CPLAssert(nullptr != poObj);
2818 :
2819 102 : json_object *poObjPoints = nullptr;
2820 :
2821 102 : if (!bRaw)
2822 : {
2823 49 : poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
2824 49 : if (nullptr == poObjPoints)
2825 : {
2826 1 : CPLError(CE_Failure, CPLE_AppDefined,
2827 : "Invalid LineString object. "
2828 : "Missing \'coordinates\' member.");
2829 1 : return nullptr;
2830 : }
2831 : }
2832 : else
2833 : {
2834 53 : poObjPoints = poObj;
2835 : }
2836 :
2837 101 : OGRLineString *poLine = nullptr;
2838 :
2839 101 : if (json_type_array == json_object_get_type(poObjPoints))
2840 : {
2841 101 : const auto nPoints = json_object_array_length(poObjPoints);
2842 :
2843 101 : poLine = new OGRLineString();
2844 101 : poLine->setNumPoints(static_cast<int>(nPoints));
2845 :
2846 298 : for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
2847 : {
2848 : json_object *poObjCoords =
2849 201 : json_object_array_get_idx(poObjPoints, i);
2850 201 : if (poObjCoords == nullptr)
2851 : {
2852 4 : delete poLine;
2853 4 : CPLDebug("GeoJSON", "LineString: got null object.");
2854 4 : return nullptr;
2855 : }
2856 :
2857 197 : OGRPoint pt;
2858 197 : if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
2859 : {
2860 0 : delete poLine;
2861 0 : CPLDebug("GeoJSON", "LineString: raw point parsing failure.");
2862 0 : return nullptr;
2863 : }
2864 197 : if (pt.getCoordinateDimension() == 2)
2865 : {
2866 171 : poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
2867 : }
2868 : else
2869 : {
2870 26 : poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
2871 : pt.getZ());
2872 : }
2873 : }
2874 : }
2875 :
2876 97 : return poLine;
2877 : }
2878 :
2879 : /************************************************************************/
2880 : /* OGRGeoJSONReadMultiLineString */
2881 : /************************************************************************/
2882 :
2883 32 : OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj)
2884 : {
2885 32 : CPLAssert(nullptr != poObj);
2886 :
2887 32 : json_object *poObjLines = OGRGeoJSONFindMemberByName(poObj, "coordinates");
2888 32 : if (nullptr == poObjLines)
2889 : {
2890 1 : CPLError(CE_Failure, CPLE_AppDefined,
2891 : "Invalid MultiLineString object. "
2892 : "Missing \'coordinates\' member.");
2893 1 : return nullptr;
2894 : }
2895 :
2896 31 : OGRMultiLineString *poMultiLine = nullptr;
2897 :
2898 31 : if (json_type_array == json_object_get_type(poObjLines))
2899 : {
2900 31 : const auto nLines = json_object_array_length(poObjLines);
2901 :
2902 31 : poMultiLine = new OGRMultiLineString();
2903 :
2904 86 : for (auto i = decltype(nLines){0}; i < nLines; ++i)
2905 : {
2906 55 : json_object *poObjLine = json_object_array_get_idx(poObjLines, i);
2907 :
2908 : OGRLineString *poLine;
2909 55 : if (poObjLine != nullptr)
2910 53 : poLine = OGRGeoJSONReadLineString(poObjLine, true);
2911 : else
2912 2 : poLine = new OGRLineString();
2913 :
2914 55 : if (nullptr != poLine)
2915 : {
2916 53 : poMultiLine->addGeometryDirectly(poLine);
2917 : }
2918 : }
2919 : }
2920 :
2921 31 : return poMultiLine;
2922 : }
2923 :
2924 : /************************************************************************/
2925 : /* OGRGeoJSONReadLinearRing */
2926 : /************************************************************************/
2927 :
2928 1300 : OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj)
2929 : {
2930 1300 : CPLAssert(nullptr != poObj);
2931 :
2932 1300 : OGRLinearRing *poRing = nullptr;
2933 :
2934 1300 : if (json_type_array == json_object_get_type(poObj))
2935 : {
2936 1300 : const auto nPoints = json_object_array_length(poObj);
2937 :
2938 1300 : poRing = new OGRLinearRing();
2939 1300 : poRing->setNumPoints(static_cast<int>(nPoints));
2940 :
2941 81158 : for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
2942 : {
2943 79858 : json_object *poObjCoords = json_object_array_get_idx(poObj, i);
2944 79858 : if (poObjCoords == nullptr)
2945 : {
2946 0 : delete poRing;
2947 0 : CPLDebug("GeoJSON", "LinearRing: got null object.");
2948 0 : return nullptr;
2949 : }
2950 :
2951 79858 : OGRPoint pt;
2952 79858 : if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
2953 : {
2954 0 : delete poRing;
2955 0 : CPLDebug("GeoJSON", "LinearRing: raw point parsing failure.");
2956 0 : return nullptr;
2957 : }
2958 :
2959 79858 : if (2 == pt.getCoordinateDimension())
2960 79701 : poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
2961 : else
2962 157 : poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
2963 : pt.getZ());
2964 : }
2965 : }
2966 :
2967 1300 : return poRing;
2968 : }
2969 :
2970 : /************************************************************************/
2971 : /* OGRGeoJSONReadPolygon */
2972 : /************************************************************************/
2973 :
2974 1267 : OGRPolygon *OGRGeoJSONReadPolygon(json_object *poObj, bool bRaw)
2975 : {
2976 1267 : CPLAssert(nullptr != poObj);
2977 :
2978 1267 : json_object *poObjRings = nullptr;
2979 :
2980 1267 : if (!bRaw)
2981 : {
2982 610 : poObjRings = OGRGeoJSONFindMemberByName(poObj, "coordinates");
2983 610 : if (nullptr == poObjRings)
2984 : {
2985 1 : CPLError(CE_Failure, CPLE_AppDefined,
2986 : "Invalid Polygon object. "
2987 : "Missing \'coordinates\' member.");
2988 1 : return nullptr;
2989 : }
2990 : }
2991 : else
2992 : {
2993 657 : poObjRings = poObj;
2994 : }
2995 :
2996 1266 : OGRPolygon *poPolygon = nullptr;
2997 :
2998 1266 : if (json_type_array == json_object_get_type(poObjRings))
2999 : {
3000 1266 : const auto nRings = json_object_array_length(poObjRings);
3001 1266 : if (nRings > 0)
3002 : {
3003 1262 : json_object *poObjPoints = json_object_array_get_idx(poObjRings, 0);
3004 1262 : if (poObjPoints == nullptr)
3005 : {
3006 2 : poPolygon = new OGRPolygon();
3007 : }
3008 : else
3009 : {
3010 1260 : OGRLinearRing *poRing = OGRGeoJSONReadLinearRing(poObjPoints);
3011 1260 : if (nullptr != poRing)
3012 : {
3013 1260 : poPolygon = new OGRPolygon();
3014 1260 : poPolygon->addRingDirectly(poRing);
3015 : }
3016 : }
3017 :
3018 1303 : for (auto i = decltype(nRings){1};
3019 1303 : i < nRings && nullptr != poPolygon; ++i)
3020 : {
3021 41 : poObjPoints = json_object_array_get_idx(poObjRings, i);
3022 41 : if (poObjPoints != nullptr)
3023 : {
3024 : OGRLinearRing *poRing =
3025 40 : OGRGeoJSONReadLinearRing(poObjPoints);
3026 40 : if (nullptr != poRing)
3027 : {
3028 40 : poPolygon->addRingDirectly(poRing);
3029 : }
3030 : }
3031 : }
3032 : }
3033 : else
3034 : {
3035 4 : poPolygon = new OGRPolygon();
3036 : }
3037 : }
3038 :
3039 1266 : return poPolygon;
3040 : }
3041 :
3042 : /************************************************************************/
3043 : /* OGRGeoJSONReadMultiPolygon */
3044 : /************************************************************************/
3045 :
3046 405 : OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj)
3047 : {
3048 405 : CPLAssert(nullptr != poObj);
3049 :
3050 405 : json_object *poObjPolys = OGRGeoJSONFindMemberByName(poObj, "coordinates");
3051 405 : if (nullptr == poObjPolys)
3052 : {
3053 1 : CPLError(CE_Failure, CPLE_AppDefined,
3054 : "Invalid MultiPolygon object. "
3055 : "Missing \'coordinates\' member.");
3056 1 : return nullptr;
3057 : }
3058 :
3059 404 : OGRMultiPolygon *poMultiPoly = nullptr;
3060 :
3061 404 : if (json_type_array == json_object_get_type(poObjPolys))
3062 : {
3063 404 : const auto nPolys = json_object_array_length(poObjPolys);
3064 :
3065 404 : poMultiPoly = new OGRMultiPolygon();
3066 :
3067 1056 : for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
3068 : {
3069 652 : json_object *poObjPoly = json_object_array_get_idx(poObjPolys, i);
3070 652 : if (poObjPoly == nullptr)
3071 : {
3072 3 : poMultiPoly->addGeometryDirectly(new OGRPolygon());
3073 : }
3074 : else
3075 : {
3076 649 : OGRPolygon *poPoly = OGRGeoJSONReadPolygon(poObjPoly, true);
3077 649 : if (nullptr != poPoly)
3078 : {
3079 649 : poMultiPoly->addGeometryDirectly(poPoly);
3080 : }
3081 : }
3082 : }
3083 : }
3084 :
3085 404 : return poMultiPoly;
3086 : }
3087 :
3088 : /************************************************************************/
3089 : /* OGRGeoJSONReadGeometryCollection */
3090 : /************************************************************************/
3091 :
3092 : OGRGeometryCollection *
3093 10 : OGRGeoJSONReadGeometryCollection(json_object *poObj, OGRSpatialReference *poSRS)
3094 : {
3095 10 : CPLAssert(nullptr != poObj);
3096 :
3097 10 : json_object *poObjGeoms = OGRGeoJSONFindMemberByName(poObj, "geometries");
3098 10 : if (nullptr == poObjGeoms)
3099 : {
3100 1 : CPLError(CE_Failure, CPLE_AppDefined,
3101 : "Invalid GeometryCollection object. "
3102 : "Missing \'geometries\' member.");
3103 1 : return nullptr;
3104 : }
3105 :
3106 9 : OGRGeometryCollection *poCollection = nullptr;
3107 :
3108 9 : if (json_type_array == json_object_get_type(poObjGeoms))
3109 : {
3110 9 : poCollection = new OGRGeometryCollection();
3111 9 : poCollection->assignSpatialReference(poSRS);
3112 :
3113 9 : const auto nGeoms = json_object_array_length(poObjGeoms);
3114 25 : for (auto i = decltype(nGeoms){0}; i < nGeoms; ++i)
3115 : {
3116 16 : json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
3117 16 : if (poObjGeom == nullptr)
3118 : {
3119 3 : CPLDebug("GeoJSON", "Skipping null sub-geometry");
3120 3 : continue;
3121 : }
3122 :
3123 13 : OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObjGeom, poSRS);
3124 13 : if (nullptr != poGeometry)
3125 : {
3126 13 : poCollection->addGeometryDirectly(poGeometry);
3127 : }
3128 : }
3129 : }
3130 :
3131 9 : return poCollection;
3132 : }
3133 :
3134 : /************************************************************************/
3135 : /* OGR_G_CreateGeometryFromJson */
3136 : /************************************************************************/
3137 :
3138 : /** Create a OGR geometry from a GeoJSON geometry object */
3139 46 : OGRGeometryH OGR_G_CreateGeometryFromJson(const char *pszJson)
3140 : {
3141 46 : if (nullptr == pszJson)
3142 : {
3143 : // Translation failed.
3144 0 : return nullptr;
3145 : }
3146 :
3147 46 : json_object *poObj = nullptr;
3148 46 : if (!OGRJSonParse(pszJson, &poObj))
3149 0 : return nullptr;
3150 :
3151 46 : OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj);
3152 :
3153 : // Release JSON tree.
3154 46 : json_object_put(poObj);
3155 :
3156 46 : return (OGRGeometryH)poGeometry;
3157 : }
3158 :
3159 : /************************************************************************/
3160 : /* json_ex_get_object_by_path() */
3161 : /************************************************************************/
3162 :
3163 167 : json_object *json_ex_get_object_by_path(json_object *poObj, const char *pszPath)
3164 : {
3165 153 : if (poObj == nullptr || json_object_get_type(poObj) != json_type_object ||
3166 320 : pszPath == nullptr || *pszPath == '\0')
3167 : {
3168 14 : return nullptr;
3169 : }
3170 153 : char **papszTokens = CSLTokenizeString2(pszPath, ".", 0);
3171 376 : for (int i = 0; papszTokens[i] != nullptr; i++)
3172 : {
3173 264 : poObj = CPL_json_object_object_get(poObj, papszTokens[i]);
3174 264 : if (poObj == nullptr)
3175 41 : break;
3176 223 : if (papszTokens[i + 1] != nullptr)
3177 : {
3178 111 : if (json_object_get_type(poObj) != json_type_object)
3179 : {
3180 0 : poObj = nullptr;
3181 0 : break;
3182 : }
3183 : }
3184 : }
3185 153 : CSLDestroy(papszTokens);
3186 153 : return poObj;
3187 : }
3188 :
3189 : /************************************************************************/
3190 : /* OGRJSonParse() */
3191 : /************************************************************************/
3192 :
3193 1310 : bool OGRJSonParse(const char *pszText, json_object **ppoObj, bool bVerboseError)
3194 : {
3195 1310 : if (ppoObj == nullptr)
3196 0 : return false;
3197 1310 : json_tokener *jstok = json_tokener_new();
3198 1310 : const int nLen = pszText == nullptr ? 0 : static_cast<int>(strlen(pszText));
3199 1310 : *ppoObj = json_tokener_parse_ex(jstok, pszText, nLen);
3200 1310 : if (jstok->err != json_tokener_success)
3201 : {
3202 15 : if (bVerboseError)
3203 : {
3204 13 : CPLError(CE_Failure, CPLE_AppDefined,
3205 : "JSON parsing error: %s (at offset %d)",
3206 : json_tokener_error_desc(jstok->err), jstok->char_offset);
3207 : }
3208 :
3209 15 : json_tokener_free(jstok);
3210 15 : *ppoObj = nullptr;
3211 15 : return false;
3212 : }
3213 1295 : json_tokener_free(jstok);
3214 1295 : return true;
3215 : }
3216 :
3217 : /************************************************************************/
3218 : /* CPL_json_object_object_get() */
3219 : /************************************************************************/
3220 :
3221 : // This is the same as json_object_object_get() except it will not raise
3222 : // deprecation warning.
3223 :
3224 34215 : json_object *CPL_json_object_object_get(struct json_object *obj,
3225 : const char *key)
3226 : {
3227 34215 : json_object *poRet = nullptr;
3228 34215 : json_object_object_get_ex(obj, key, &poRet);
3229 34215 : return poRet;
3230 : }
3231 :
3232 2888 : bool OGRGeoJSONGetExtent3D(json_object *poObj, OGREnvelope3D *poEnvelope)
3233 : {
3234 2888 : if (!poEnvelope || !poObj)
3235 : {
3236 0 : return false;
3237 : }
3238 :
3239 : // poObjCoords can be an array of arrays, this lambda function will
3240 : // recursively parse the array
3241 5776 : std::function<bool(json_object *, OGREnvelope3D *)> fParseCoords;
3242 156146 : fParseCoords = [&fParseCoords](json_object *poObjCoordsIn,
3243 153254 : OGREnvelope3D *poEnvelopeIn) -> bool
3244 : {
3245 156146 : if (json_type_array == json_object_get_type(poObjCoordsIn))
3246 : {
3247 156146 : const auto nItems = json_object_array_length(poObjCoordsIn);
3248 :
3249 156146 : double dXVal = std::numeric_limits<double>::quiet_NaN();
3250 156146 : double dYVal = std::numeric_limits<double>::quiet_NaN();
3251 156146 : double dZVal = std::numeric_limits<double>::quiet_NaN();
3252 :
3253 610535 : for (auto i = decltype(nItems){0}; i < nItems; ++i)
3254 : {
3255 :
3256 : // Get the i element
3257 : json_object *poObjCoordsElement =
3258 454406 : json_object_array_get_idx(poObjCoordsIn, i);
3259 :
3260 454406 : const json_type eType{json_object_get_type(poObjCoordsElement)};
3261 :
3262 : // if it is an array, recurse
3263 454406 : if (json_type_array == eType)
3264 : {
3265 153254 : if (!fParseCoords(poObjCoordsElement, poEnvelopeIn))
3266 : {
3267 1 : return false;
3268 : }
3269 : }
3270 301152 : else if (json_type_double == eType || json_type_int == eType)
3271 : {
3272 301136 : switch (i)
3273 : {
3274 150448 : case 0:
3275 : {
3276 150448 : dXVal = json_object_get_double(poObjCoordsElement);
3277 150448 : break;
3278 : }
3279 150447 : case 1:
3280 : {
3281 150447 : dYVal = json_object_get_double(poObjCoordsElement);
3282 150447 : break;
3283 : }
3284 241 : case 2:
3285 : {
3286 241 : dZVal = json_object_get_double(poObjCoordsElement);
3287 241 : break;
3288 : }
3289 0 : default:
3290 0 : return false;
3291 : }
3292 : }
3293 : else
3294 : {
3295 16 : return false;
3296 : }
3297 : }
3298 :
3299 156129 : if (!std::isnan(dXVal) && !std::isnan(dYVal))
3300 : {
3301 150446 : if (std::isnan(dZVal))
3302 : {
3303 : static_cast<OGREnvelope *>(poEnvelopeIn)
3304 150205 : ->Merge(dXVal, dYVal);
3305 : }
3306 : else
3307 : {
3308 241 : poEnvelopeIn->Merge(dXVal, dYVal, dZVal);
3309 : }
3310 : }
3311 :
3312 156129 : return true;
3313 : }
3314 : else
3315 : {
3316 0 : return false;
3317 : }
3318 2888 : };
3319 :
3320 : // This function looks for "coordinates" and for "geometries" to handle
3321 : // geometry collections. It will recurse on itself to handle nested geometry.
3322 5776 : std::function<bool(json_object *, OGREnvelope3D *)> fParseGeometry;
3323 2910 : fParseGeometry = [&fParseGeometry,
3324 : &fParseCoords](json_object *poObjIn,
3325 2914 : OGREnvelope3D *poEnvelopeIn) -> bool
3326 : {
3327 : // Get the "coordinates" array from the JSON object
3328 : json_object *poObjCoords =
3329 2910 : OGRGeoJSONFindMemberByName(poObjIn, "coordinates");
3330 :
3331 : // Return if found and not an array
3332 2910 : if (poObjCoords && json_object_get_type(poObjCoords) != json_type_array)
3333 : {
3334 0 : return false;
3335 : }
3336 2910 : else if (poObjCoords)
3337 : {
3338 2892 : return fParseCoords(poObjCoords, poEnvelopeIn);
3339 : }
3340 :
3341 : // Try "geometries"
3342 18 : if (!poObjCoords)
3343 : {
3344 18 : poObjCoords = OGRGeoJSONFindMemberByName(poObjIn, "geometries");
3345 : }
3346 :
3347 : // Return if not found or not an array
3348 28 : if (!poObjCoords ||
3349 10 : json_object_get_type(poObjCoords) != json_type_array)
3350 : {
3351 8 : return false;
3352 : }
3353 : else
3354 : {
3355 : // Loop thgrough the geometries
3356 10 : const auto nItems = json_object_array_length(poObjCoords);
3357 30 : for (auto i = decltype(nItems){0}; i < nItems; ++i)
3358 : {
3359 : json_object *poObjGeometry =
3360 22 : json_object_array_get_idx(poObjCoords, i);
3361 :
3362 : // Recurse
3363 22 : if (!fParseGeometry(poObjGeometry, poEnvelopeIn))
3364 : {
3365 2 : return false;
3366 : }
3367 : }
3368 8 : return true;
3369 : }
3370 2888 : };
3371 :
3372 2888 : return fParseGeometry(poObj, poEnvelope);
3373 : }
|