/src/gdal/ogr/ogrsf_frmts/geojson/ogresrijsonreader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: Implementation of OGRESRIJSONReader class (OGR ESRIJSON Driver) |
5 | | * to read ESRI Feature Service REST data |
6 | | * Author: Even Rouault, even dot rouault at spatialys.com |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * Copyright (c) 2007, Mateusz Loskot |
11 | | * Copyright (c) 2013, Kyle Shannon <kyle at pobox dot com> |
12 | | * |
13 | | * SPDX-License-Identifier: MIT |
14 | | ****************************************************************************/ |
15 | | |
16 | | #include "cpl_port.h" |
17 | | #include "ogrlibjsonutils.h" |
18 | | |
19 | | #include <limits.h> |
20 | | #include <stddef.h> |
21 | | |
22 | | #include "cpl_conv.h" |
23 | | #include "cpl_error.h" |
24 | | #include "cpl_time.h" |
25 | | #include "json.h" |
26 | | // #include "json_object.h" |
27 | | // #include "json_tokener.h" |
28 | | #include "ogr_api.h" |
29 | | #include "ogr_core.h" |
30 | | #include "ogr_feature.h" |
31 | | #include "ogr_geometry.h" |
32 | | #include "ogr_spatialref.h" |
33 | | #include "ogr_geojson.h" |
34 | | #include "ogrgeojsonreader.h" |
35 | | #include "ogrgeojsonutils.h" |
36 | | #include "ogresrijsongeometry.h" |
37 | | |
38 | | // #include "symbol_renames.h" |
39 | | |
40 | | /************************************************************************/ |
41 | | /* OGRESRIJSONReader() */ |
42 | | /************************************************************************/ |
43 | | |
44 | 0 | OGRESRIJSONReader::OGRESRIJSONReader() : poGJObject_(nullptr), poLayer_(nullptr) |
45 | 0 | { |
46 | 0 | } |
47 | | |
48 | | /************************************************************************/ |
49 | | /* ~OGRESRIJSONReader() */ |
50 | | /************************************************************************/ |
51 | | |
52 | | OGRESRIJSONReader::~OGRESRIJSONReader() |
53 | 0 | { |
54 | 0 | if (nullptr != poGJObject_) |
55 | 0 | { |
56 | 0 | json_object_put(poGJObject_); |
57 | 0 | } |
58 | |
|
59 | 0 | poGJObject_ = nullptr; |
60 | 0 | poLayer_ = nullptr; |
61 | 0 | } |
62 | | |
63 | | /************************************************************************/ |
64 | | /* Parse() */ |
65 | | /************************************************************************/ |
66 | | |
67 | | OGRErr OGRESRIJSONReader::Parse(const char *pszText) |
68 | 0 | { |
69 | 0 | json_object *jsobj = nullptr; |
70 | 0 | if (nullptr != pszText && !OGRJSonParse(pszText, &jsobj, true)) |
71 | 0 | { |
72 | 0 | return OGRERR_CORRUPT_DATA; |
73 | 0 | } |
74 | | |
75 | | // JSON tree is shared for while lifetime of the reader object |
76 | | // and will be released in the destructor. |
77 | 0 | poGJObject_ = jsobj; |
78 | 0 | return OGRERR_NONE; |
79 | 0 | } |
80 | | |
81 | | /************************************************************************/ |
82 | | /* ReadLayers() */ |
83 | | /************************************************************************/ |
84 | | |
85 | | void OGRESRIJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS, |
86 | | GeoJSONSourceType eSourceType) |
87 | 0 | { |
88 | 0 | CPLAssert(nullptr == poLayer_); |
89 | | |
90 | 0 | poDS->SetSupportsMGeometries(true); |
91 | |
|
92 | 0 | if (nullptr == poGJObject_) |
93 | 0 | { |
94 | 0 | CPLDebug("ESRIJSON", |
95 | 0 | "Missing parsed ESRIJSON data. Forgot to call Parse()?"); |
96 | 0 | return; |
97 | 0 | } |
98 | | |
99 | 0 | OGRSpatialReference *poSRS = OGRESRIJSONReadSpatialReference(poGJObject_); |
100 | |
|
101 | 0 | std::string osName = "ESRIJSON"; |
102 | 0 | if (eSourceType == eGeoJSONSourceFile) |
103 | 0 | { |
104 | 0 | osName = poDS->GetDescription(); |
105 | 0 | if (STARTS_WITH_CI(osName.c_str(), "ESRIJSON:")) |
106 | 0 | osName = osName.substr(strlen("ESRIJSON:")); |
107 | 0 | osName = CPLGetBasenameSafe(osName.c_str()); |
108 | 0 | } |
109 | |
|
110 | 0 | auto eGeomType = OGRESRIJSONGetGeometryType(poGJObject_); |
111 | 0 | if (eGeomType == wkbNone) |
112 | 0 | { |
113 | 0 | if (poSRS) |
114 | 0 | { |
115 | 0 | eGeomType = wkbUnknown; |
116 | 0 | } |
117 | 0 | else |
118 | 0 | { |
119 | 0 | json_object *poObjFeatures = |
120 | 0 | OGRGeoJSONFindMemberByName(poGJObject_, "features"); |
121 | 0 | if (poObjFeatures && |
122 | 0 | json_type_array == json_object_get_type(poObjFeatures)) |
123 | 0 | { |
124 | 0 | const auto nFeatures = json_object_array_length(poObjFeatures); |
125 | 0 | for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i) |
126 | 0 | { |
127 | 0 | json_object *poObjFeature = |
128 | 0 | json_object_array_get_idx(poObjFeatures, i); |
129 | 0 | if (poObjFeature != nullptr && |
130 | 0 | json_object_get_type(poObjFeature) == json_type_object) |
131 | 0 | { |
132 | 0 | if (auto poObjGeometry = OGRGeoJSONFindMemberByName( |
133 | 0 | poObjFeature, "geometry")) |
134 | 0 | { |
135 | 0 | eGeomType = wkbUnknown; |
136 | 0 | poSRS = |
137 | 0 | OGRESRIJSONReadSpatialReference(poObjGeometry); |
138 | 0 | break; |
139 | 0 | } |
140 | 0 | } |
141 | 0 | } |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | |
|
146 | 0 | poLayer_ = |
147 | 0 | new OGRGeoJSONLayer(osName.c_str(), poSRS, eGeomType, poDS, nullptr); |
148 | 0 | poLayer_->SetSupportsMGeometries(true); |
149 | 0 | if (poSRS != nullptr) |
150 | 0 | poSRS->Release(); |
151 | |
|
152 | 0 | if (!GenerateLayerDefn()) |
153 | 0 | { |
154 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
155 | 0 | "Layer schema generation failed."); |
156 | |
|
157 | 0 | delete poLayer_; |
158 | 0 | return; |
159 | 0 | } |
160 | | |
161 | 0 | OGRGeoJSONLayer *poThisLayer = ReadFeatureCollection(poGJObject_); |
162 | 0 | if (poThisLayer == nullptr) |
163 | 0 | { |
164 | 0 | delete poLayer_; |
165 | 0 | return; |
166 | 0 | } |
167 | | |
168 | 0 | CPLErrorReset(); |
169 | |
|
170 | 0 | poLayer_->DetectGeometryType(); |
171 | 0 | poDS->AddLayer(poLayer_); |
172 | 0 | } |
173 | | |
174 | | /************************************************************************/ |
175 | | /* GenerateFeatureDefn() */ |
176 | | /************************************************************************/ |
177 | | |
178 | | bool OGRESRIJSONReader::GenerateLayerDefn() |
179 | 0 | { |
180 | 0 | CPLAssert(nullptr != poGJObject_); |
181 | | |
182 | 0 | bool bSuccess = true; |
183 | |
|
184 | 0 | OGRFeatureDefn *poDefn = poLayer_->GetLayerDefn(); |
185 | 0 | CPLAssert(nullptr != poDefn); |
186 | 0 | CPLAssert(0 == poDefn->GetFieldCount()); |
187 | 0 | auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer()); |
188 | | |
189 | | /* -------------------------------------------------------------------- */ |
190 | | /* Scan all features and generate layer definition. */ |
191 | | /* -------------------------------------------------------------------- */ |
192 | 0 | json_object *poFields = OGRGeoJSONFindMemberByName(poGJObject_, "fields"); |
193 | 0 | if (nullptr != poFields && |
194 | 0 | json_type_array == json_object_get_type(poFields)) |
195 | 0 | { |
196 | 0 | const auto nFeatures = json_object_array_length(poFields); |
197 | 0 | for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i) |
198 | 0 | { |
199 | 0 | json_object *poField = json_object_array_get_idx(poFields, i); |
200 | 0 | if (!ParseField(poField)) |
201 | 0 | { |
202 | 0 | CPLDebug("GeoJSON", "Create feature schema failure."); |
203 | 0 | bSuccess = false; |
204 | 0 | } |
205 | 0 | } |
206 | 0 | } |
207 | 0 | else if ((poFields = OGRGeoJSONFindMemberByName( |
208 | 0 | poGJObject_, "fieldAliases")) != nullptr && |
209 | 0 | json_object_get_type(poFields) == json_type_object) |
210 | 0 | { |
211 | 0 | json_object_iter it; |
212 | 0 | it.key = nullptr; |
213 | 0 | it.val = nullptr; |
214 | 0 | it.entry = nullptr; |
215 | 0 | json_object_object_foreachC(poFields, it) |
216 | 0 | { |
217 | 0 | OGRFieldDefn fldDefn(it.key, OFTString); |
218 | 0 | poDefn->AddFieldDefn(&fldDefn); |
219 | 0 | } |
220 | 0 | } |
221 | 0 | else |
222 | 0 | { |
223 | | // Guess the fields' schema from the content of the features' "attributes" |
224 | | // element |
225 | 0 | json_object *poObjFeatures = |
226 | 0 | OGRGeoJSONFindMemberByName(poGJObject_, "features"); |
227 | 0 | if (poObjFeatures && |
228 | 0 | json_type_array == json_object_get_type(poObjFeatures)) |
229 | 0 | { |
230 | 0 | gdal::DirectedAcyclicGraph<int, std::string> dag; |
231 | 0 | std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn{}; |
232 | 0 | std::map<std::string, int> oMapFieldNameToIdx{}; |
233 | 0 | std::vector<int> anCurFieldIndices; |
234 | 0 | std::set<int> aoSetUndeterminedTypeFields; |
235 | |
|
236 | 0 | const auto nFeatures = json_object_array_length(poObjFeatures); |
237 | 0 | for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i) |
238 | 0 | { |
239 | 0 | json_object *poObjFeature = |
240 | 0 | json_object_array_get_idx(poObjFeatures, i); |
241 | 0 | if (poObjFeature != nullptr && |
242 | 0 | json_object_get_type(poObjFeature) == json_type_object) |
243 | 0 | { |
244 | 0 | int nPrevFieldIdx = -1; |
245 | |
|
246 | 0 | json_object *poObjProps = |
247 | 0 | OGRGeoJSONFindMemberByName(poObjFeature, "attributes"); |
248 | 0 | if (nullptr != poObjProps && |
249 | 0 | json_object_get_type(poObjProps) == json_type_object) |
250 | 0 | { |
251 | 0 | json_object_iter it; |
252 | 0 | it.key = nullptr; |
253 | 0 | it.val = nullptr; |
254 | 0 | it.entry = nullptr; |
255 | 0 | json_object_object_foreachC(poObjProps, it) |
256 | 0 | { |
257 | 0 | anCurFieldIndices.clear(); |
258 | 0 | OGRGeoJSONReaderAddOrUpdateField( |
259 | 0 | anCurFieldIndices, oMapFieldNameToIdx, |
260 | 0 | apoFieldDefn, it.key, it.val, |
261 | 0 | /*bFlattenNestedAttributes = */ true, |
262 | 0 | /* chNestedAttributeSeparator = */ '.', |
263 | 0 | /* bArrayAsString =*/false, |
264 | 0 | /* bDateAsString = */ false, |
265 | 0 | aoSetUndeterminedTypeFields); |
266 | 0 | for (int idx : anCurFieldIndices) |
267 | 0 | { |
268 | 0 | dag.addNode(idx, |
269 | 0 | apoFieldDefn[idx]->GetNameRef()); |
270 | 0 | if (nPrevFieldIdx != -1) |
271 | 0 | { |
272 | 0 | dag.addEdge(nPrevFieldIdx, idx); |
273 | 0 | } |
274 | 0 | nPrevFieldIdx = idx; |
275 | 0 | } |
276 | 0 | } |
277 | 0 | } |
278 | 0 | } |
279 | 0 | } |
280 | |
|
281 | 0 | const auto sortedFields = dag.getTopologicalOrdering(); |
282 | 0 | CPLAssert(sortedFields.size() == apoFieldDefn.size()); |
283 | 0 | for (int idx : sortedFields) |
284 | 0 | { |
285 | | // cppcheck-suppress containerOutOfBounds |
286 | 0 | poDefn->AddFieldDefn(apoFieldDefn[idx].get()); |
287 | 0 | } |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | 0 | return bSuccess; |
292 | 0 | } |
293 | | |
294 | | /************************************************************************/ |
295 | | /* ParseField() */ |
296 | | /************************************************************************/ |
297 | | |
298 | | bool OGRESRIJSONReader::ParseField(json_object *poObj) |
299 | 0 | { |
300 | 0 | OGRFeatureDefn *poDefn = poLayer_->GetLayerDefn(); |
301 | 0 | CPLAssert(nullptr != poDefn); |
302 | | |
303 | 0 | bool bSuccess = false; |
304 | | |
305 | | /* -------------------------------------------------------------------- */ |
306 | | /* Read collection of properties. */ |
307 | | /* -------------------------------------------------------------------- */ |
308 | 0 | json_object *poObjName = OGRGeoJSONFindMemberByName(poObj, "name"); |
309 | 0 | json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type"); |
310 | 0 | if (nullptr != poObjName && nullptr != poObjType) |
311 | 0 | { |
312 | 0 | OGRFieldType eFieldType = OFTString; |
313 | 0 | OGRFieldSubType eFieldSubType = OFSTNone; |
314 | 0 | const char *pszObjName = json_object_get_string(poObjName); |
315 | 0 | const char *pszObjType = json_object_get_string(poObjType); |
316 | 0 | if (EQUAL(pszObjType, "esriFieldTypeString")) |
317 | 0 | { |
318 | | // do nothing |
319 | 0 | } |
320 | 0 | else if (EQUAL(pszObjType, "esriFieldTypeOID")) |
321 | 0 | { |
322 | 0 | eFieldType = OFTInteger; |
323 | 0 | poLayer_->SetFIDColumn(pszObjName); |
324 | 0 | } |
325 | 0 | else if (EQUAL(pszObjType, "esriFieldTypeSingle")) |
326 | 0 | { |
327 | 0 | eFieldType = OFTReal; |
328 | 0 | eFieldSubType = OFSTFloat32; |
329 | 0 | } |
330 | 0 | else if (EQUAL(pszObjType, "esriFieldTypeDouble")) |
331 | 0 | { |
332 | 0 | eFieldType = OFTReal; |
333 | 0 | } |
334 | 0 | else if (EQUAL(pszObjType, "esriFieldTypeSmallInteger")) |
335 | 0 | { |
336 | 0 | eFieldType = OFTInteger; |
337 | 0 | eFieldSubType = OFSTInt16; |
338 | 0 | } |
339 | 0 | else if (EQUAL(pszObjType, "esriFieldTypeInteger")) |
340 | 0 | { |
341 | 0 | eFieldType = OFTInteger; |
342 | 0 | } |
343 | 0 | else if (EQUAL(pszObjType, "esriFieldTypeDate")) |
344 | 0 | { |
345 | 0 | eFieldType = OFTDateTime; |
346 | 0 | } |
347 | 0 | else |
348 | 0 | { |
349 | 0 | CPLDebug("ESRIJSON", |
350 | 0 | "Unhandled fields[\"%s\"].type = %s. " |
351 | 0 | "Processing it as a String", |
352 | 0 | pszObjName, pszObjType); |
353 | 0 | } |
354 | 0 | OGRFieldDefn fldDefn(pszObjName, eFieldType); |
355 | 0 | fldDefn.SetSubType(eFieldSubType); |
356 | |
|
357 | 0 | if (eFieldType != OFTDateTime) |
358 | 0 | { |
359 | 0 | json_object *const poObjLength = |
360 | 0 | OGRGeoJSONFindMemberByName(poObj, "length"); |
361 | 0 | if (poObjLength != nullptr && |
362 | 0 | json_object_get_type(poObjLength) == json_type_int) |
363 | 0 | { |
364 | 0 | const int nWidth = json_object_get_int(poObjLength); |
365 | | // A dummy width of 2147483647 seems to indicate no known field with |
366 | | // which in the OGR world is better modelled as 0 field width. |
367 | | // (#6529) |
368 | 0 | if (nWidth != INT_MAX) |
369 | 0 | fldDefn.SetWidth(nWidth); |
370 | 0 | } |
371 | 0 | } |
372 | |
|
373 | 0 | json_object *poObjAlias = OGRGeoJSONFindMemberByName(poObj, "alias"); |
374 | 0 | if (poObjAlias && json_object_get_type(poObjAlias) == json_type_string) |
375 | 0 | { |
376 | 0 | const char *pszAlias = json_object_get_string(poObjAlias); |
377 | 0 | if (strcmp(pszObjName, pszAlias) != 0) |
378 | 0 | fldDefn.SetAlternativeName(pszAlias); |
379 | 0 | } |
380 | |
|
381 | 0 | poDefn->AddFieldDefn(&fldDefn); |
382 | |
|
383 | 0 | bSuccess = true; |
384 | 0 | } |
385 | 0 | return bSuccess; |
386 | 0 | } |
387 | | |
388 | | /************************************************************************/ |
389 | | /* AddFeature */ |
390 | | /************************************************************************/ |
391 | | |
392 | | bool OGRESRIJSONReader::AddFeature(OGRFeature *poFeature) |
393 | 0 | { |
394 | 0 | if (nullptr == poFeature) |
395 | 0 | return false; |
396 | | |
397 | 0 | poLayer_->AddFeature(poFeature); |
398 | 0 | delete poFeature; |
399 | |
|
400 | 0 | return true; |
401 | 0 | } |
402 | | |
403 | | /************************************************************************/ |
404 | | /* EsriDateToOGRDate() */ |
405 | | /************************************************************************/ |
406 | | |
407 | | static void EsriDateToOGRDate(int64_t nVal, OGRField *psField) |
408 | 0 | { |
409 | 0 | const auto nSeconds = nVal / 1000; |
410 | 0 | const auto nMillisec = static_cast<int>(nVal % 1000); |
411 | |
|
412 | 0 | struct tm brokendowntime; |
413 | 0 | CPLUnixTimeToYMDHMS(nSeconds, &brokendowntime); |
414 | |
|
415 | 0 | psField->Date.Year = static_cast<GInt16>(brokendowntime.tm_year + 1900); |
416 | 0 | psField->Date.Month = static_cast<GByte>(brokendowntime.tm_mon + 1); |
417 | 0 | psField->Date.Day = static_cast<GByte>(brokendowntime.tm_mday); |
418 | 0 | psField->Date.Hour = static_cast<GByte>(brokendowntime.tm_hour); |
419 | 0 | psField->Date.Minute = static_cast<GByte>(brokendowntime.tm_min); |
420 | 0 | psField->Date.Second = |
421 | 0 | static_cast<float>(brokendowntime.tm_sec + nMillisec / 1000.0); |
422 | 0 | psField->Date.TZFlag = 100; |
423 | 0 | psField->Date.Reserved = 0; |
424 | 0 | } |
425 | | |
426 | | /************************************************************************/ |
427 | | /* ReadFeature() */ |
428 | | /************************************************************************/ |
429 | | |
430 | | OGRFeature *OGRESRIJSONReader::ReadFeature(json_object *poObj) |
431 | 0 | { |
432 | 0 | CPLAssert(nullptr != poObj); |
433 | 0 | CPLAssert(nullptr != poLayer_); |
434 | | |
435 | 0 | OGRFeature *poFeature = new OGRFeature(poLayer_->GetLayerDefn()); |
436 | | |
437 | | /* -------------------------------------------------------------------- */ |
438 | | /* Translate ESRIJSON "attributes" object to feature attributes. */ |
439 | | /* -------------------------------------------------------------------- */ |
440 | 0 | CPLAssert(nullptr != poFeature); |
441 | | |
442 | 0 | json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "attributes"); |
443 | 0 | if (nullptr != poObjProps && |
444 | 0 | json_object_get_type(poObjProps) == json_type_object) |
445 | 0 | { |
446 | 0 | OGRFieldDefn *poFieldDefn = nullptr; |
447 | 0 | json_object_iter it; |
448 | 0 | it.key = nullptr; |
449 | 0 | it.val = nullptr; |
450 | 0 | it.entry = nullptr; |
451 | 0 | json_object_object_foreachC(poObjProps, it) |
452 | 0 | { |
453 | 0 | const int nField = poFeature->GetFieldIndex(it.key); |
454 | 0 | if (nField >= 0) |
455 | 0 | { |
456 | 0 | poFieldDefn = poFeature->GetFieldDefnRef(nField); |
457 | 0 | if (poFieldDefn && it.val != nullptr) |
458 | 0 | { |
459 | 0 | if (EQUAL(it.key, poLayer_->GetFIDColumn())) |
460 | 0 | poFeature->SetFID(json_object_get_int(it.val)); |
461 | 0 | switch (poLayer_->GetLayerDefn() |
462 | 0 | ->GetFieldDefn(nField) |
463 | 0 | ->GetType()) |
464 | 0 | { |
465 | 0 | case OFTInteger: |
466 | 0 | { |
467 | 0 | poFeature->SetField(nField, |
468 | 0 | json_object_get_int(it.val)); |
469 | 0 | break; |
470 | 0 | } |
471 | 0 | case OFTReal: |
472 | 0 | { |
473 | 0 | poFeature->SetField(nField, |
474 | 0 | json_object_get_double(it.val)); |
475 | 0 | break; |
476 | 0 | } |
477 | 0 | case OFTDateTime: |
478 | 0 | { |
479 | 0 | const auto nVal = json_object_get_int64(it.val); |
480 | 0 | EsriDateToOGRDate( |
481 | 0 | nVal, poFeature->GetRawFieldRef(nField)); |
482 | 0 | break; |
483 | 0 | } |
484 | 0 | default: |
485 | 0 | { |
486 | 0 | poFeature->SetField(nField, |
487 | 0 | json_object_get_string(it.val)); |
488 | 0 | break; |
489 | 0 | } |
490 | 0 | } |
491 | 0 | } |
492 | 0 | } |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | 0 | const OGRwkbGeometryType eType = poLayer_->GetGeomType(); |
497 | 0 | if (eType == wkbNone) |
498 | 0 | return poFeature; |
499 | | |
500 | | /* -------------------------------------------------------------------- */ |
501 | | /* Translate geometry sub-object of ESRIJSON Feature. */ |
502 | | /* -------------------------------------------------------------------- */ |
503 | 0 | json_object *poObjGeom = nullptr; |
504 | 0 | json_object *poTmp = poObj; |
505 | 0 | json_object_iter it; |
506 | 0 | it.key = nullptr; |
507 | 0 | it.val = nullptr; |
508 | 0 | it.entry = nullptr; |
509 | 0 | json_object_object_foreachC(poTmp, it) |
510 | 0 | { |
511 | 0 | if (EQUAL(it.key, "geometry")) |
512 | 0 | { |
513 | 0 | if (it.val != nullptr) |
514 | 0 | poObjGeom = it.val; |
515 | | // We're done. They had 'geometry':null. |
516 | 0 | else |
517 | 0 | return poFeature; |
518 | 0 | } |
519 | 0 | } |
520 | | |
521 | 0 | if (nullptr != poObjGeom) |
522 | 0 | { |
523 | 0 | OGRGeometry *poGeometry = OGRESRIJSONReadGeometry(poObjGeom); |
524 | 0 | if (nullptr != poGeometry) |
525 | 0 | { |
526 | 0 | poFeature->SetGeometryDirectly(poGeometry); |
527 | 0 | } |
528 | 0 | } |
529 | |
|
530 | 0 | return poFeature; |
531 | 0 | } |
532 | | |
533 | | /************************************************************************/ |
534 | | /* ReadFeatureCollection() */ |
535 | | /************************************************************************/ |
536 | | |
537 | | OGRGeoJSONLayer *OGRESRIJSONReader::ReadFeatureCollection(json_object *poObj) |
538 | 0 | { |
539 | 0 | CPLAssert(nullptr != poLayer_); |
540 | | |
541 | 0 | json_object *poObjFeatures = OGRGeoJSONFindMemberByName(poObj, "features"); |
542 | 0 | if (nullptr == poObjFeatures) |
543 | 0 | { |
544 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
545 | 0 | "Invalid FeatureCollection object. " |
546 | 0 | "Missing \'features\' member."); |
547 | 0 | return nullptr; |
548 | 0 | } |
549 | | |
550 | 0 | if (json_type_array == json_object_get_type(poObjFeatures)) |
551 | 0 | { |
552 | 0 | const auto nFeatures = json_object_array_length(poObjFeatures); |
553 | 0 | for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i) |
554 | 0 | { |
555 | 0 | json_object *poObjFeature = |
556 | 0 | json_object_array_get_idx(poObjFeatures, i); |
557 | 0 | if (poObjFeature != nullptr && |
558 | 0 | json_object_get_type(poObjFeature) == json_type_object) |
559 | 0 | { |
560 | 0 | OGRFeature *poFeature = |
561 | 0 | OGRESRIJSONReader::ReadFeature(poObjFeature); |
562 | 0 | AddFeature(poFeature); |
563 | 0 | } |
564 | 0 | } |
565 | 0 | } |
566 | | |
567 | | // We're returning class member to follow the same pattern of |
568 | | // Read* functions call convention. |
569 | 0 | CPLAssert(nullptr != poLayer_); |
570 | 0 | return poLayer_; |
571 | 0 | } |