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