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