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 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include "cpl_port.h"
33 : #include "ogrgeojsonreader.h"
34 :
35 : #include <limits.h>
36 : #include <stddef.h>
37 :
38 : #include "cpl_conv.h"
39 : #include "cpl_error.h"
40 : #include "cpl_time.h"
41 : #include "json.h"
42 : // #include "json_object.h"
43 : // #include "json_tokener.h"
44 : #include "ogr_api.h"
45 : #include "ogr_core.h"
46 : #include "ogr_feature.h"
47 : #include "ogr_geometry.h"
48 : #include "ogr_spatialref.h"
49 : #include "ogr_geojson.h"
50 : #include "ogrgeojsonreader.h"
51 : #include "ogrgeojsonutils.h"
52 :
53 : // #include "symbol_renames.h"
54 :
55 : /************************************************************************/
56 : /* OGRESRIJSONReader() */
57 : /************************************************************************/
58 :
59 19 : OGRESRIJSONReader::OGRESRIJSONReader() : poGJObject_(nullptr), poLayer_(nullptr)
60 : {
61 19 : }
62 :
63 : /************************************************************************/
64 : /* ~OGRESRIJSONReader() */
65 : /************************************************************************/
66 :
67 38 : OGRESRIJSONReader::~OGRESRIJSONReader()
68 : {
69 19 : if (nullptr != poGJObject_)
70 : {
71 19 : json_object_put(poGJObject_);
72 : }
73 :
74 19 : poGJObject_ = nullptr;
75 19 : poLayer_ = nullptr;
76 19 : }
77 :
78 : /************************************************************************/
79 : /* Parse() */
80 : /************************************************************************/
81 :
82 19 : OGRErr OGRESRIJSONReader::Parse(const char *pszText)
83 : {
84 19 : json_object *jsobj = nullptr;
85 19 : if (nullptr != pszText && !OGRJSonParse(pszText, &jsobj, true))
86 : {
87 0 : return OGRERR_CORRUPT_DATA;
88 : }
89 :
90 : // JSON tree is shared for while lifetime of the reader object
91 : // and will be released in the destructor.
92 19 : poGJObject_ = jsobj;
93 19 : return OGRERR_NONE;
94 : }
95 :
96 : /************************************************************************/
97 : /* ReadLayers() */
98 : /************************************************************************/
99 :
100 19 : void OGRESRIJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS,
101 : GeoJSONSourceType eSourceType)
102 : {
103 19 : CPLAssert(nullptr == poLayer_);
104 :
105 19 : if (nullptr == poGJObject_)
106 : {
107 0 : CPLDebug("ESRIJSON",
108 : "Missing parsed ESRIJSON data. Forgot to call Parse()?");
109 0 : return;
110 : }
111 :
112 19 : OGRSpatialReference *poSRS = OGRESRIJSONReadSpatialReference(poGJObject_);
113 :
114 19 : const char *pszName = "ESRIJSON";
115 19 : if (eSourceType == eGeoJSONSourceFile)
116 : {
117 17 : pszName = poDS->GetDescription();
118 17 : if (STARTS_WITH_CI(pszName, "ESRIJSON:"))
119 1 : pszName += strlen("ESRIJSON:");
120 17 : pszName = CPLGetBasename(pszName);
121 : }
122 :
123 19 : auto eGeomType = OGRESRIJSONGetGeometryType(poGJObject_);
124 19 : if (eGeomType == wkbNone && poSRS != nullptr)
125 : {
126 0 : eGeomType = wkbUnknown;
127 : }
128 :
129 19 : poLayer_ = new OGRGeoJSONLayer(pszName, poSRS, eGeomType, poDS, nullptr);
130 19 : if (poSRS != nullptr)
131 8 : poSRS->Release();
132 :
133 19 : if (!GenerateLayerDefn())
134 : {
135 0 : CPLError(CE_Failure, CPLE_AppDefined,
136 : "Layer schema generation failed.");
137 :
138 0 : delete poLayer_;
139 0 : return;
140 : }
141 :
142 19 : OGRGeoJSONLayer *poThisLayer = ReadFeatureCollection(poGJObject_);
143 19 : if (poThisLayer == nullptr)
144 : {
145 0 : delete poLayer_;
146 0 : return;
147 : }
148 :
149 19 : CPLErrorReset();
150 :
151 19 : poLayer_->DetectGeometryType();
152 19 : poDS->AddLayer(poLayer_);
153 : }
154 :
155 : /************************************************************************/
156 : /* GenerateFeatureDefn() */
157 : /************************************************************************/
158 :
159 19 : bool OGRESRIJSONReader::GenerateLayerDefn()
160 : {
161 19 : CPLAssert(nullptr != poGJObject_);
162 :
163 19 : bool bSuccess = true;
164 :
165 19 : OGRFeatureDefn *poDefn = poLayer_->GetLayerDefn();
166 19 : CPLAssert(nullptr != poDefn);
167 19 : CPLAssert(0 == poDefn->GetFieldCount());
168 19 : auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
169 :
170 : /* -------------------------------------------------------------------- */
171 : /* Scan all features and generate layer definition. */
172 : /* -------------------------------------------------------------------- */
173 19 : json_object *poFields = OGRGeoJSONFindMemberByName(poGJObject_, "fields");
174 38 : if (nullptr != poFields &&
175 19 : json_type_array == json_object_get_type(poFields))
176 : {
177 19 : const auto nFeatures = json_object_array_length(poFields);
178 54 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
179 : {
180 35 : json_object *poField = json_object_array_get_idx(poFields, i);
181 35 : if (!ParseField(poField))
182 : {
183 0 : CPLDebug("GeoJSON", "Create feature schema failure.");
184 0 : bSuccess = false;
185 : }
186 : }
187 : }
188 : else
189 : {
190 0 : poFields = OGRGeoJSONFindMemberByName(poGJObject_, "fieldAliases");
191 0 : if (nullptr != poFields &&
192 0 : json_object_get_type(poFields) == json_type_object)
193 : {
194 : json_object_iter it;
195 0 : it.key = nullptr;
196 0 : it.val = nullptr;
197 0 : it.entry = nullptr;
198 0 : json_object_object_foreachC(poFields, it)
199 : {
200 0 : OGRFieldDefn fldDefn(it.key, OFTString);
201 0 : poDefn->AddFieldDefn(&fldDefn);
202 : }
203 : }
204 : else
205 : {
206 0 : CPLError(CE_Failure, CPLE_AppDefined,
207 : "Invalid FeatureCollection object. "
208 : "Missing \'fields\' member.");
209 0 : bSuccess = false;
210 : }
211 : }
212 :
213 38 : return bSuccess;
214 : }
215 :
216 : /************************************************************************/
217 : /* ParseField() */
218 : /************************************************************************/
219 :
220 35 : bool OGRESRIJSONReader::ParseField(json_object *poObj)
221 : {
222 35 : OGRFeatureDefn *poDefn = poLayer_->GetLayerDefn();
223 35 : CPLAssert(nullptr != poDefn);
224 :
225 35 : bool bSuccess = false;
226 :
227 : /* -------------------------------------------------------------------- */
228 : /* Read collection of properties. */
229 : /* -------------------------------------------------------------------- */
230 35 : json_object *poObjName = OGRGeoJSONFindMemberByName(poObj, "name");
231 35 : json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
232 35 : if (nullptr != poObjName && nullptr != poObjType)
233 : {
234 35 : OGRFieldType eFieldType = OFTString;
235 35 : OGRFieldSubType eFieldSubType = OFSTNone;
236 35 : const char *pszObjName = json_object_get_string(poObjName);
237 35 : const char *pszObjType = json_object_get_string(poObjType);
238 35 : if (EQUAL(pszObjType, "esriFieldTypeOID"))
239 : {
240 11 : eFieldType = OFTInteger;
241 11 : poLayer_->SetFIDColumn(pszObjName);
242 : }
243 24 : else if (EQUAL(pszObjType, "esriFieldTypeSingle"))
244 : {
245 1 : eFieldType = OFTReal;
246 1 : eFieldSubType = OFSTFloat32;
247 : }
248 23 : else if (EQUAL(pszObjType, "esriFieldTypeDouble"))
249 : {
250 7 : eFieldType = OFTReal;
251 : }
252 16 : else if (EQUAL(pszObjType, "esriFieldTypeSmallInteger"))
253 : {
254 1 : eFieldType = OFTInteger;
255 1 : eFieldSubType = OFSTInt16;
256 : }
257 15 : else if (EQUAL(pszObjType, "esriFieldTypeInteger"))
258 : {
259 7 : eFieldType = OFTInteger;
260 : }
261 8 : else if (EQUAL(pszObjType, "esriFieldTypeDate"))
262 : {
263 1 : eFieldType = OFTDateTime;
264 : }
265 : else
266 : {
267 7 : CPLDebug("ESRIJSON",
268 : "Unhandled fields[\"%s\"].type = %s. "
269 : "Processing it as a String",
270 : pszObjName, pszObjType);
271 : }
272 35 : OGRFieldDefn fldDefn(pszObjName, eFieldType);
273 35 : fldDefn.SetSubType(eFieldSubType);
274 :
275 : json_object *const poObjLength =
276 35 : OGRGeoJSONFindMemberByName(poObj, "length");
277 51 : if (poObjLength != nullptr &&
278 16 : json_object_get_type(poObjLength) == json_type_int)
279 : {
280 16 : const int nWidth = json_object_get_int(poObjLength);
281 : // A dummy width of 2147483647 seems to indicate no known field with
282 : // which in the OGR world is better modelled as 0 field width.
283 : // (#6529)
284 16 : if (nWidth != INT_MAX)
285 16 : fldDefn.SetWidth(nWidth);
286 : }
287 :
288 35 : poDefn->AddFieldDefn(&fldDefn);
289 :
290 35 : bSuccess = true;
291 : }
292 35 : return bSuccess;
293 : }
294 :
295 : /************************************************************************/
296 : /* AddFeature */
297 : /************************************************************************/
298 :
299 18 : bool OGRESRIJSONReader::AddFeature(OGRFeature *poFeature)
300 : {
301 18 : if (nullptr == poFeature)
302 0 : return false;
303 :
304 18 : poLayer_->AddFeature(poFeature);
305 18 : delete poFeature;
306 :
307 18 : return true;
308 : }
309 :
310 : /************************************************************************/
311 : /* OGRESRIJSONReadGeometry() */
312 : /************************************************************************/
313 :
314 17 : OGRGeometry *OGRESRIJSONReadGeometry(json_object *poObj)
315 : {
316 17 : OGRGeometry *poGeometry = nullptr;
317 :
318 17 : if (OGRGeoJSONFindMemberByName(poObj, "x"))
319 5 : poGeometry = OGRESRIJSONReadPoint(poObj);
320 12 : else if (OGRGeoJSONFindMemberByName(poObj, "paths"))
321 3 : poGeometry = OGRESRIJSONReadLineString(poObj);
322 9 : else if (OGRGeoJSONFindMemberByName(poObj, "rings"))
323 4 : poGeometry = OGRESRIJSONReadPolygon(poObj);
324 5 : else if (OGRGeoJSONFindMemberByName(poObj, "points"))
325 5 : poGeometry = OGRESRIJSONReadMultiPoint(poObj);
326 :
327 17 : return poGeometry;
328 : }
329 :
330 : /************************************************************************/
331 : /* OGR_G_CreateGeometryFromEsriJson() */
332 : /************************************************************************/
333 :
334 : /** Create a OGR geometry from a ESRIJson geometry object */
335 2 : OGRGeometryH OGR_G_CreateGeometryFromEsriJson(const char *pszJson)
336 : {
337 2 : if (nullptr == pszJson)
338 : {
339 : // Translation failed.
340 0 : return nullptr;
341 : }
342 :
343 2 : json_object *poObj = nullptr;
344 2 : if (!OGRJSonParse(pszJson, &poObj))
345 1 : return nullptr;
346 :
347 1 : OGRGeometry *poGeometry = OGRESRIJSONReadGeometry(poObj);
348 :
349 : // Release JSON tree.
350 1 : json_object_put(poObj);
351 :
352 1 : return OGRGeometry::ToHandle(poGeometry);
353 : }
354 :
355 : /************************************************************************/
356 : /* EsriDateToOGRDate() */
357 : /************************************************************************/
358 :
359 1 : static void EsriDateToOGRDate(int64_t nVal, OGRField *psField)
360 : {
361 1 : const auto nSeconds = nVal / 1000;
362 1 : const auto nMillisec = static_cast<int>(nVal % 1000);
363 :
364 : struct tm brokendowntime;
365 1 : CPLUnixTimeToYMDHMS(nSeconds, &brokendowntime);
366 :
367 1 : psField->Date.Year = static_cast<GInt16>(brokendowntime.tm_year + 1900);
368 1 : psField->Date.Month = static_cast<GByte>(brokendowntime.tm_mon + 1);
369 1 : psField->Date.Day = static_cast<GByte>(brokendowntime.tm_mday);
370 1 : psField->Date.Hour = static_cast<GByte>(brokendowntime.tm_hour);
371 1 : psField->Date.Minute = static_cast<GByte>(brokendowntime.tm_min);
372 1 : psField->Date.Second =
373 1 : static_cast<float>(brokendowntime.tm_sec + nMillisec / 1000.0);
374 1 : psField->Date.TZFlag = 100;
375 1 : psField->Date.Reserved = 0;
376 1 : }
377 :
378 : /************************************************************************/
379 : /* ReadFeature() */
380 : /************************************************************************/
381 :
382 18 : OGRFeature *OGRESRIJSONReader::ReadFeature(json_object *poObj)
383 : {
384 18 : CPLAssert(nullptr != poObj);
385 18 : CPLAssert(nullptr != poLayer_);
386 :
387 18 : OGRFeature *poFeature = new OGRFeature(poLayer_->GetLayerDefn());
388 :
389 : /* -------------------------------------------------------------------- */
390 : /* Translate ESRIJSON "attributes" object to feature attributes. */
391 : /* -------------------------------------------------------------------- */
392 18 : CPLAssert(nullptr != poFeature);
393 :
394 18 : json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "attributes");
395 35 : if (nullptr != poObjProps &&
396 17 : json_object_get_type(poObjProps) == json_type_object)
397 : {
398 17 : OGRFieldDefn *poFieldDefn = nullptr;
399 : json_object_iter it;
400 17 : it.key = nullptr;
401 17 : it.val = nullptr;
402 17 : it.entry = nullptr;
403 52 : json_object_object_foreachC(poObjProps, it)
404 : {
405 35 : const int nField = poFeature->GetFieldIndex(it.key);
406 35 : if (nField >= 0)
407 : {
408 35 : poFieldDefn = poFeature->GetFieldDefnRef(nField);
409 35 : if (poFieldDefn && it.val != nullptr)
410 : {
411 35 : if (EQUAL(it.key, poLayer_->GetFIDColumn()))
412 11 : poFeature->SetFID(json_object_get_int(it.val));
413 35 : switch (poLayer_->GetLayerDefn()
414 35 : ->GetFieldDefn(nField)
415 35 : ->GetType())
416 : {
417 19 : case OFTInteger:
418 : {
419 19 : poFeature->SetField(nField,
420 19 : json_object_get_int(it.val));
421 19 : break;
422 : }
423 8 : case OFTReal:
424 : {
425 8 : poFeature->SetField(nField,
426 8 : json_object_get_double(it.val));
427 8 : break;
428 : }
429 1 : case OFTDateTime:
430 : {
431 1 : const auto nVal = json_object_get_int64(it.val);
432 1 : EsriDateToOGRDate(
433 : nVal, poFeature->GetRawFieldRef(nField));
434 1 : break;
435 : }
436 7 : default:
437 : {
438 7 : poFeature->SetField(nField,
439 : json_object_get_string(it.val));
440 7 : break;
441 : }
442 : }
443 : }
444 : }
445 : }
446 : }
447 :
448 18 : const OGRwkbGeometryType eType = poLayer_->GetGeomType();
449 18 : if (eType == wkbNone)
450 0 : return poFeature;
451 :
452 : /* -------------------------------------------------------------------- */
453 : /* Translate geometry sub-object of ESRIJSON Feature. */
454 : /* -------------------------------------------------------------------- */
455 18 : json_object *poObjGeom = nullptr;
456 18 : json_object *poTmp = poObj;
457 : json_object_iter it;
458 18 : it.key = nullptr;
459 18 : it.val = nullptr;
460 18 : it.entry = nullptr;
461 49 : json_object_object_foreachC(poTmp, it)
462 : {
463 33 : if (EQUAL(it.key, "geometry"))
464 : {
465 18 : if (it.val != nullptr)
466 16 : poObjGeom = it.val;
467 : // We're done. They had 'geometry':null.
468 : else
469 2 : return poFeature;
470 : }
471 : }
472 :
473 16 : if (nullptr != poObjGeom)
474 : {
475 16 : OGRGeometry *poGeometry = OGRESRIJSONReadGeometry(poObjGeom);
476 16 : if (nullptr != poGeometry)
477 : {
478 16 : poFeature->SetGeometryDirectly(poGeometry);
479 : }
480 : }
481 :
482 16 : return poFeature;
483 : }
484 :
485 : /************************************************************************/
486 : /* ReadFeatureCollection() */
487 : /************************************************************************/
488 :
489 19 : OGRGeoJSONLayer *OGRESRIJSONReader::ReadFeatureCollection(json_object *poObj)
490 : {
491 19 : CPLAssert(nullptr != poLayer_);
492 :
493 19 : json_object *poObjFeatures = OGRGeoJSONFindMemberByName(poObj, "features");
494 19 : if (nullptr == poObjFeatures)
495 : {
496 0 : CPLError(CE_Failure, CPLE_AppDefined,
497 : "Invalid FeatureCollection object. "
498 : "Missing \'features\' member.");
499 0 : return nullptr;
500 : }
501 :
502 19 : if (json_type_array == json_object_get_type(poObjFeatures))
503 : {
504 19 : const auto nFeatures = json_object_array_length(poObjFeatures);
505 37 : for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
506 : {
507 : json_object *poObjFeature =
508 18 : json_object_array_get_idx(poObjFeatures, i);
509 36 : if (poObjFeature != nullptr &&
510 18 : json_object_get_type(poObjFeature) == json_type_object)
511 : {
512 : OGRFeature *poFeature =
513 18 : OGRESRIJSONReader::ReadFeature(poObjFeature);
514 18 : AddFeature(poFeature);
515 : }
516 : }
517 : }
518 :
519 : // We're returning class member to follow the same pattern of
520 : // Read* functions call convention.
521 19 : CPLAssert(nullptr != poLayer_);
522 19 : return poLayer_;
523 : }
524 :
525 : /************************************************************************/
526 : /* OGRESRIJSONGetType() */
527 : /************************************************************************/
528 :
529 19 : OGRwkbGeometryType OGRESRIJSONGetGeometryType(json_object *poObj)
530 : {
531 19 : if (nullptr == poObj)
532 0 : return wkbUnknown;
533 :
534 19 : json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "geometryType");
535 19 : if (nullptr == poObjType)
536 : {
537 0 : return wkbNone;
538 : }
539 :
540 19 : const char *name = json_object_get_string(poObjType);
541 19 : if (EQUAL(name, "esriGeometryPoint"))
542 7 : return wkbPoint;
543 12 : else if (EQUAL(name, "esriGeometryPolyline"))
544 3 : return wkbLineString;
545 9 : else if (EQUAL(name, "esriGeometryPolygon"))
546 4 : return wkbPolygon;
547 5 : else if (EQUAL(name, "esriGeometryMultiPoint"))
548 5 : return wkbMultiPoint;
549 : else
550 0 : return wkbUnknown;
551 : }
552 :
553 : /************************************************************************/
554 : /* OGRESRIJSONGetCoordinateToDouble() */
555 : /************************************************************************/
556 :
557 119 : static double OGRESRIJSONGetCoordinateToDouble(json_object *poObjCoord,
558 : const char *pszCoordName,
559 : bool &bValid)
560 : {
561 119 : const int iType = json_object_get_type(poObjCoord);
562 119 : if (json_type_double != iType && json_type_int != iType)
563 : {
564 0 : CPLError(CE_Failure, CPLE_AppDefined,
565 : "Invalid '%s' coordinate. "
566 : "Type is not double or integer for \'%s\'.",
567 : pszCoordName, json_object_to_json_string(poObjCoord));
568 0 : bValid = false;
569 0 : return 0.0;
570 : }
571 :
572 119 : return json_object_get_double(poObjCoord);
573 : }
574 :
575 : /************************************************************************/
576 : /* OGRESRIJSONGetCoordinate() */
577 : /************************************************************************/
578 :
579 10 : static double OGRESRIJSONGetCoordinate(json_object *poObj,
580 : const char *pszCoordName, bool &bValid)
581 : {
582 10 : json_object *poObjCoord = OGRGeoJSONFindMemberByName(poObj, pszCoordName);
583 10 : if (nullptr == poObjCoord)
584 : {
585 0 : CPLError(CE_Failure, CPLE_AppDefined,
586 : "Invalid Point object. "
587 : "Missing '%s' member.",
588 : pszCoordName);
589 0 : bValid = false;
590 0 : return 0.0;
591 : }
592 :
593 10 : return OGRESRIJSONGetCoordinateToDouble(poObjCoord, pszCoordName, bValid);
594 : }
595 :
596 : /************************************************************************/
597 : /* OGRESRIJSONReadPoint() */
598 : /************************************************************************/
599 :
600 5 : OGRPoint *OGRESRIJSONReadPoint(json_object *poObj)
601 : {
602 5 : CPLAssert(nullptr != poObj);
603 :
604 5 : bool bValid = true;
605 5 : const double dfX = OGRESRIJSONGetCoordinate(poObj, "x", bValid);
606 5 : const double dfY = OGRESRIJSONGetCoordinate(poObj, "y", bValid);
607 5 : if (!bValid)
608 0 : return nullptr;
609 :
610 5 : json_object *poObjZ = OGRGeoJSONFindMemberByName(poObj, "z");
611 5 : if (nullptr == poObjZ)
612 2 : return new OGRPoint(dfX, dfY);
613 :
614 3 : const double dfZ = OGRESRIJSONGetCoordinateToDouble(poObjZ, "z", bValid);
615 3 : if (!bValid)
616 0 : return nullptr;
617 3 : return new OGRPoint(dfX, dfY, dfZ);
618 : }
619 :
620 : /************************************************************************/
621 : /* OGRESRIJSONReaderParseZM() */
622 : /************************************************************************/
623 :
624 12 : static void OGRESRIJSONReaderParseZM(json_object *poObj, bool *bHasZ,
625 : bool *bHasM)
626 : {
627 12 : CPLAssert(nullptr != poObj);
628 : // The ESRI geojson spec states that geometries other than point can
629 : // have the attributes hasZ and hasM. A geometry that has a z value
630 : // implies the 3rd number in the tuple is z. if hasM is true, but hasZ
631 : // is not, it is the M value.
632 12 : bool bZ = false;
633 12 : json_object *poObjHasZ = OGRGeoJSONFindMemberByName(poObj, "hasZ");
634 12 : if (poObjHasZ != nullptr)
635 : {
636 2 : if (json_object_get_type(poObjHasZ) == json_type_boolean)
637 : {
638 2 : bZ = CPL_TO_BOOL(json_object_get_boolean(poObjHasZ));
639 : }
640 : }
641 :
642 12 : bool bM = false;
643 12 : json_object *poObjHasM = OGRGeoJSONFindMemberByName(poObj, "hasM");
644 12 : if (poObjHasM != nullptr)
645 : {
646 2 : if (json_object_get_type(poObjHasM) == json_type_boolean)
647 : {
648 2 : bM = CPL_TO_BOOL(json_object_get_boolean(poObjHasM));
649 : }
650 : }
651 12 : if (bHasZ != nullptr)
652 12 : *bHasZ = bZ;
653 12 : if (bHasM != nullptr)
654 12 : *bHasM = bM;
655 12 : }
656 :
657 : /************************************************************************/
658 : /* OGRESRIJSONReaderParseXYZMArray() */
659 : /************************************************************************/
660 :
661 43 : static bool OGRESRIJSONReaderParseXYZMArray(json_object *poObjCoords,
662 : bool /*bHasZ*/, bool bHasM,
663 : double *pdfX, double *pdfY,
664 : double *pdfZ, double *pdfM,
665 : int *pnNumCoords)
666 : {
667 43 : if (poObjCoords == nullptr)
668 : {
669 0 : CPLDebug("ESRIJSON",
670 : "OGRESRIJSONReaderParseXYZMArray: got null object.");
671 0 : return false;
672 : }
673 :
674 43 : if (json_type_array != json_object_get_type(poObjCoords))
675 : {
676 0 : CPLDebug("ESRIJSON",
677 : "OGRESRIJSONReaderParseXYZMArray: got non-array object.");
678 0 : return false;
679 : }
680 :
681 43 : const auto coordDimension = json_object_array_length(poObjCoords);
682 :
683 : // Allow 4 coordinates if M is present, but it is eventually ignored.
684 43 : if (coordDimension < 2 || coordDimension > 4)
685 : {
686 0 : CPLDebug("ESRIJSON",
687 : "OGRESRIJSONReaderParseXYZMArray: got an unexpected "
688 : "array object.");
689 0 : return false;
690 : }
691 :
692 : // Read X coordinate.
693 43 : json_object *poObjCoord = json_object_array_get_idx(poObjCoords, 0);
694 43 : if (poObjCoord == nullptr)
695 : {
696 0 : CPLDebug("ESRIJSON",
697 : "OGRESRIJSONReaderParseXYZMArray: got null object.");
698 0 : return false;
699 : }
700 :
701 43 : bool bValid = true;
702 : const double dfX =
703 43 : OGRESRIJSONGetCoordinateToDouble(poObjCoord, "x", bValid);
704 :
705 : // Read Y coordinate.
706 43 : poObjCoord = json_object_array_get_idx(poObjCoords, 1);
707 43 : if (poObjCoord == nullptr)
708 : {
709 0 : CPLDebug("ESRIJSON",
710 : "OGRESRIJSONReaderParseXYZMArray: got null object.");
711 0 : return false;
712 : }
713 :
714 : const double dfY =
715 43 : OGRESRIJSONGetCoordinateToDouble(poObjCoord, "y", bValid);
716 43 : if (!bValid)
717 0 : return false;
718 :
719 : // Read Z or M or Z and M coordinates.
720 43 : if (coordDimension > 2)
721 : {
722 18 : poObjCoord = json_object_array_get_idx(poObjCoords, 2);
723 18 : if (poObjCoord == nullptr)
724 : {
725 0 : CPLDebug("ESRIJSON",
726 : "OGRESRIJSONReaderParseXYZMArray: got null object.");
727 0 : return false;
728 : }
729 :
730 34 : const double dfZorM = OGRESRIJSONGetCoordinateToDouble(
731 16 : poObjCoord, (coordDimension > 3 || !bHasM) ? "z" : "m", bValid);
732 18 : if (!bValid)
733 0 : return false;
734 18 : if (pdfZ != nullptr)
735 : {
736 18 : if (coordDimension > 3 || !bHasM)
737 16 : *pdfZ = dfZorM;
738 : else
739 2 : *pdfZ = 0.0;
740 : }
741 18 : if (pdfM != nullptr && coordDimension == 3)
742 : {
743 16 : if (bHasM)
744 2 : *pdfM = dfZorM;
745 : else
746 14 : *pdfM = 0.0;
747 : }
748 18 : if (coordDimension == 4)
749 : {
750 2 : poObjCoord = json_object_array_get_idx(poObjCoords, 3);
751 2 : if (poObjCoord == nullptr)
752 : {
753 0 : CPLDebug("ESRIJSON",
754 : "OGRESRIJSONReaderParseXYZMArray: got null object.");
755 0 : return false;
756 : }
757 :
758 : const double dfM =
759 2 : OGRESRIJSONGetCoordinateToDouble(poObjCoord, "m", bValid);
760 2 : if (!bValid)
761 0 : return false;
762 2 : if (pdfM != nullptr)
763 2 : *pdfM = dfM;
764 : }
765 : }
766 : else
767 : {
768 25 : if (pdfZ != nullptr)
769 25 : *pdfZ = 0.0;
770 25 : if (pdfM != nullptr)
771 25 : *pdfM = 0.0;
772 : }
773 :
774 43 : if (pnNumCoords != nullptr)
775 43 : *pnNumCoords = static_cast<int>(coordDimension);
776 43 : if (pdfX != nullptr)
777 43 : *pdfX = dfX;
778 43 : if (pdfY != nullptr)
779 43 : *pdfY = dfY;
780 :
781 43 : return true;
782 : }
783 :
784 : /************************************************************************/
785 : /* OGRESRIJSONReadLineString() */
786 : /************************************************************************/
787 :
788 3 : OGRGeometry *OGRESRIJSONReadLineString(json_object *poObj)
789 : {
790 3 : CPLAssert(nullptr != poObj);
791 :
792 3 : bool bHasZ = false;
793 3 : bool bHasM = false;
794 :
795 3 : OGRESRIJSONReaderParseZM(poObj, &bHasZ, &bHasM);
796 :
797 3 : json_object *poObjPaths = OGRGeoJSONFindMemberByName(poObj, "paths");
798 3 : if (nullptr == poObjPaths)
799 : {
800 0 : CPLError(CE_Failure, CPLE_AppDefined,
801 : "Invalid LineString object. "
802 : "Missing \'paths\' member.");
803 0 : return nullptr;
804 : }
805 :
806 3 : if (json_type_array != json_object_get_type(poObjPaths))
807 : {
808 0 : CPLError(CE_Failure, CPLE_AppDefined,
809 : "Invalid LineString object. "
810 : "Invalid \'paths\' member.");
811 0 : return nullptr;
812 : }
813 :
814 3 : OGRMultiLineString *poMLS = nullptr;
815 3 : OGRGeometry *poRet = nullptr;
816 3 : const auto nPaths = json_object_array_length(poObjPaths);
817 7 : for (auto iPath = decltype(nPaths){0}; iPath < nPaths; iPath++)
818 : {
819 4 : json_object *poObjPath = json_object_array_get_idx(poObjPaths, iPath);
820 8 : if (poObjPath == nullptr ||
821 4 : json_type_array != json_object_get_type(poObjPath))
822 : {
823 0 : delete poRet;
824 0 : CPLDebug("ESRIJSON", "LineString: got non-array object.");
825 0 : return nullptr;
826 : }
827 :
828 4 : OGRLineString *poLine = new OGRLineString();
829 4 : if (nPaths > 1)
830 : {
831 2 : if (iPath == 0)
832 : {
833 1 : poMLS = new OGRMultiLineString();
834 1 : poRet = poMLS;
835 : }
836 2 : poMLS->addGeometryDirectly(poLine);
837 : }
838 : else
839 : {
840 2 : poRet = poLine;
841 : }
842 4 : const auto nPoints = json_object_array_length(poObjPath);
843 12 : for (auto i = decltype(nPoints){0}; i < nPoints; i++)
844 : {
845 8 : int nNumCoords = 2;
846 8 : json_object *poObjCoords = json_object_array_get_idx(poObjPath, i);
847 8 : double dfX = 0.0;
848 8 : double dfY = 0.0;
849 8 : double dfZ = 0.0;
850 8 : double dfM = 0.0;
851 8 : if (!OGRESRIJSONReaderParseXYZMArray(poObjCoords, bHasZ, bHasM,
852 : &dfX, &dfY, &dfZ, &dfM,
853 : &nNumCoords))
854 : {
855 0 : delete poRet;
856 0 : return nullptr;
857 : }
858 :
859 8 : if (nNumCoords == 3 && !bHasM)
860 : {
861 2 : poLine->addPoint(dfX, dfY, dfZ);
862 : }
863 6 : else if (nNumCoords == 3)
864 : {
865 0 : poLine->addPointM(dfX, dfY, dfM);
866 : }
867 6 : else if (nNumCoords == 4)
868 : {
869 0 : poLine->addPoint(dfX, dfY, dfZ, dfM);
870 : }
871 : else
872 : {
873 6 : poLine->addPoint(dfX, dfY);
874 : }
875 : }
876 : }
877 :
878 3 : if (poRet == nullptr)
879 0 : poRet = new OGRLineString();
880 :
881 3 : return poRet;
882 : }
883 :
884 : /************************************************************************/
885 : /* OGRESRIJSONReadPolygon() */
886 : /************************************************************************/
887 :
888 4 : OGRGeometry *OGRESRIJSONReadPolygon(json_object *poObj)
889 : {
890 4 : CPLAssert(nullptr != poObj);
891 :
892 4 : bool bHasZ = false;
893 4 : bool bHasM = false;
894 :
895 4 : OGRESRIJSONReaderParseZM(poObj, &bHasZ, &bHasM);
896 :
897 4 : json_object *poObjRings = OGRGeoJSONFindMemberByName(poObj, "rings");
898 4 : if (nullptr == poObjRings)
899 : {
900 0 : CPLError(CE_Failure, CPLE_AppDefined,
901 : "Invalid Polygon object. "
902 : "Missing \'rings\' member.");
903 0 : return nullptr;
904 : }
905 :
906 4 : if (json_type_array != json_object_get_type(poObjRings))
907 : {
908 0 : CPLError(CE_Failure, CPLE_AppDefined,
909 : "Invalid Polygon object. "
910 : "Invalid \'rings\' member.");
911 0 : return nullptr;
912 : }
913 :
914 4 : const auto nRings = json_object_array_length(poObjRings);
915 4 : OGRGeometry **papoGeoms = new OGRGeometry *[nRings];
916 9 : for (auto iRing = decltype(nRings){0}; iRing < nRings; iRing++)
917 : {
918 5 : json_object *poObjRing = json_object_array_get_idx(poObjRings, iRing);
919 10 : if (poObjRing == nullptr ||
920 5 : json_type_array != json_object_get_type(poObjRing))
921 : {
922 0 : for (auto j = decltype(iRing){0}; j < iRing; j++)
923 0 : delete papoGeoms[j];
924 0 : delete[] papoGeoms;
925 0 : CPLDebug("ESRIJSON", "Polygon: got non-array object.");
926 0 : return nullptr;
927 : }
928 :
929 5 : OGRPolygon *poPoly = new OGRPolygon();
930 5 : auto poLine = std::make_unique<OGRLinearRing>();
931 5 : papoGeoms[iRing] = poPoly;
932 :
933 5 : const auto nPoints = json_object_array_length(poObjRing);
934 30 : for (auto i = decltype(nPoints){0}; i < nPoints; i++)
935 : {
936 25 : int nNumCoords = 2;
937 25 : json_object *poObjCoords = json_object_array_get_idx(poObjRing, i);
938 25 : double dfX = 0.0;
939 25 : double dfY = 0.0;
940 25 : double dfZ = 0.0;
941 25 : double dfM = 0.0;
942 25 : if (!OGRESRIJSONReaderParseXYZMArray(poObjCoords, bHasZ, bHasM,
943 : &dfX, &dfY, &dfZ, &dfM,
944 : &nNumCoords))
945 : {
946 0 : for (auto j = decltype(iRing){0}; j <= iRing; j++)
947 0 : delete papoGeoms[j];
948 0 : delete[] papoGeoms;
949 0 : return nullptr;
950 : }
951 :
952 25 : if (nNumCoords == 3 && !bHasM)
953 : {
954 10 : poLine->addPoint(dfX, dfY, dfZ);
955 : }
956 15 : else if (nNumCoords == 3)
957 : {
958 0 : poLine->addPointM(dfX, dfY, dfM);
959 : }
960 15 : else if (nNumCoords == 4)
961 : {
962 0 : poLine->addPoint(dfX, dfY, dfZ, dfM);
963 : }
964 : else
965 : {
966 15 : poLine->addPoint(dfX, dfY);
967 : }
968 : }
969 5 : poPoly->addRingDirectly(poLine.release());
970 : }
971 :
972 4 : OGRGeometry *poRet = OGRGeometryFactory::organizePolygons(
973 : papoGeoms, static_cast<int>(nRings), nullptr, nullptr);
974 4 : delete[] papoGeoms;
975 :
976 4 : return poRet;
977 : }
978 :
979 : /************************************************************************/
980 : /* OGRESRIJSONReadMultiPoint() */
981 : /************************************************************************/
982 :
983 5 : OGRMultiPoint *OGRESRIJSONReadMultiPoint(json_object *poObj)
984 : {
985 5 : CPLAssert(nullptr != poObj);
986 :
987 5 : bool bHasZ = false;
988 5 : bool bHasM = false;
989 :
990 5 : OGRESRIJSONReaderParseZM(poObj, &bHasZ, &bHasM);
991 :
992 5 : json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "points");
993 5 : if (nullptr == poObjPoints)
994 : {
995 0 : CPLError(CE_Failure, CPLE_AppDefined,
996 : "Invalid MultiPoint object. "
997 : "Missing \'points\' member.");
998 0 : return nullptr;
999 : }
1000 :
1001 5 : if (json_type_array != json_object_get_type(poObjPoints))
1002 : {
1003 0 : CPLError(CE_Failure, CPLE_AppDefined,
1004 : "Invalid MultiPoint object. "
1005 : "Invalid \'points\' member.");
1006 0 : return nullptr;
1007 : }
1008 :
1009 5 : OGRMultiPoint *poMulti = new OGRMultiPoint();
1010 :
1011 5 : const auto nPoints = json_object_array_length(poObjPoints);
1012 15 : for (auto i = decltype(nPoints){0}; i < nPoints; i++)
1013 : {
1014 10 : int nNumCoords = 2;
1015 10 : json_object *poObjCoords = json_object_array_get_idx(poObjPoints, i);
1016 10 : double dfX = 0.0;
1017 10 : double dfY = 0.0;
1018 10 : double dfZ = 0.0;
1019 10 : double dfM = 0.0;
1020 10 : if (!OGRESRIJSONReaderParseXYZMArray(poObjCoords, bHasZ, bHasM, &dfX,
1021 : &dfY, &dfZ, &dfM, &nNumCoords))
1022 : {
1023 0 : delete poMulti;
1024 0 : return nullptr;
1025 : }
1026 :
1027 10 : if (nNumCoords == 3 && !bHasM)
1028 : {
1029 2 : poMulti->addGeometryDirectly(new OGRPoint(dfX, dfY, dfZ));
1030 : }
1031 8 : else if (nNumCoords == 3)
1032 : {
1033 2 : OGRPoint *poPoint = new OGRPoint(dfX, dfY);
1034 2 : poPoint->setM(dfM);
1035 2 : poMulti->addGeometryDirectly(poPoint);
1036 : }
1037 6 : else if (nNumCoords == 4)
1038 : {
1039 2 : poMulti->addGeometryDirectly(new OGRPoint(dfX, dfY, dfZ, dfM));
1040 : }
1041 : else
1042 : {
1043 4 : poMulti->addGeometryDirectly(new OGRPoint(dfX, dfY));
1044 : }
1045 : }
1046 :
1047 5 : return poMulti;
1048 : }
1049 :
1050 : /************************************************************************/
1051 : /* OGRESRIJSONReadSpatialReference() */
1052 : /************************************************************************/
1053 :
1054 19 : OGRSpatialReference *OGRESRIJSONReadSpatialReference(json_object *poObj)
1055 : {
1056 : /* -------------------------------------------------------------------- */
1057 : /* Read spatial reference definition. */
1058 : /* -------------------------------------------------------------------- */
1059 19 : OGRSpatialReference *poSRS = nullptr;
1060 :
1061 : json_object *poObjSrs =
1062 19 : OGRGeoJSONFindMemberByName(poObj, "spatialReference");
1063 19 : if (nullptr != poObjSrs)
1064 : {
1065 : json_object *poObjWkid =
1066 8 : OGRGeoJSONFindMemberByName(poObjSrs, "latestWkid");
1067 8 : if (poObjWkid == nullptr)
1068 8 : poObjWkid = OGRGeoJSONFindMemberByName(poObjSrs, "wkid");
1069 8 : if (poObjWkid == nullptr)
1070 : {
1071 1 : json_object *poObjWkt = OGRGeoJSONFindMemberByName(poObjSrs, "wkt");
1072 1 : if (poObjWkt == nullptr)
1073 0 : return nullptr;
1074 :
1075 1 : const char *pszWKT = json_object_get_string(poObjWkt);
1076 1 : poSRS = new OGRSpatialReference();
1077 1 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1078 1 : if (OGRERR_NONE != poSRS->importFromWkt(pszWKT))
1079 : {
1080 0 : delete poSRS;
1081 0 : poSRS = nullptr;
1082 : }
1083 : else
1084 : {
1085 1 : auto poSRSMatch = poSRS->FindBestMatch(70);
1086 1 : if (poSRSMatch)
1087 : {
1088 1 : poSRS->Release();
1089 1 : poSRS = poSRSMatch;
1090 1 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1091 : }
1092 : }
1093 :
1094 1 : return poSRS;
1095 : }
1096 :
1097 7 : const int nEPSG = json_object_get_int(poObjWkid);
1098 :
1099 7 : poSRS = new OGRSpatialReference();
1100 7 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1101 7 : if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
1102 : {
1103 0 : delete poSRS;
1104 0 : poSRS = nullptr;
1105 : }
1106 : }
1107 :
1108 18 : return poSRS;
1109 : }
|