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