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