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