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