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