1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implementation of GeoJSON writer utilities (OGR GeoJSON Driver).
5  * Author:   Mateusz Loskot, mateusz@loskot.net
6  *
7  ******************************************************************************
8  * Copyright (c) 2007, Mateusz Loskot
9  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #define JSON_C_VER_013 (13 << 8)
31 
32 #include "ogrgeojsonwriter.h"
33 #include "ogrgeojsonutils.h"
34 #include "ogr_geojson.h"
35 #include "ogrgeojsonreader.h"
36 #include <json.h>  // JSON-C
37 
38 #if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
39 #include <json_object_private.h>
40 #endif
41 
42 #include <printbuf.h>
43 #include <ogr_api.h>
44 #include <ogr_p.h>
45 
46 #include <algorithm>
47 #include <cstdint>
48 
49 CPL_CVSID("$Id: ogrgeojsonwriter.cpp e15d9c61f434751cb4d978f2ebdbfbbef4173c79 2021-06-17 17:59:49 +0200 Momtchil Momtchev $")
50 
51 static json_object *
52 json_object_new_float_with_significant_figures( float fVal,
53                                                 int nSignificantFigures );
54 
55 /************************************************************************/
56 /*                         SetRFC7946Settings()                         */
57 /************************************************************************/
58 
59 /*! @cond Doxygen_Suppress */
SetRFC7946Settings()60 void OGRGeoJSONWriteOptions::SetRFC7946Settings()
61 {
62     bBBOXRFC7946 = true;
63     if( nCoordPrecision < 0 )
64         nCoordPrecision = 7;
65     bPolygonRightHandRule = true;
66     bCanPatchCoordinatesWithNativeData = false;
67     bHonourReservedRFC7946Members = true;
68 }
69 
SetIDOptions(CSLConstList papszOptions)70 void OGRGeoJSONWriteOptions::SetIDOptions(CSLConstList papszOptions)
71 {
72 
73     osIDField = CSLFetchNameValueDef(papszOptions, "ID_FIELD", "");
74     const char* pszIDFieldType = CSLFetchNameValue(papszOptions, "ID_TYPE");
75     if( pszIDFieldType )
76     {
77         if( EQUAL(pszIDFieldType, "String") )
78         {
79             bForceIDFieldType = true;
80             eForcedIDFieldType = OFTString;
81         }
82         else if( EQUAL(pszIDFieldType, "Integer") )
83         {
84             bForceIDFieldType = true;
85             eForcedIDFieldType = OFTInteger64;
86         }
87     }
88     bGenerateID = CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "ID_GENERATE", false));
89 }
90 
91 /*! @endcond */
92 
93 /************************************************************************/
94 /*                        json_object_new_coord()                       */
95 /************************************************************************/
96 
json_object_new_coord(double dfVal,const OGRGeoJSONWriteOptions & oOptions)97 static json_object* json_object_new_coord( double dfVal,
98                                            const OGRGeoJSONWriteOptions& oOptions )
99 {
100     // If coordinate precision is specified, or significant figures is not
101     // then use the '%f' formatting.
102     if( oOptions.nCoordPrecision >= 0 || oOptions.nSignificantFigures < 0 )
103         return json_object_new_double_with_precision(dfVal,
104                                                      oOptions.nCoordPrecision);
105 
106     return json_object_new_double_with_significant_figures(dfVal,
107                                                 oOptions.nSignificantFigures);
108 }
109 
110 /************************************************************************/
111 /*                     OGRGeoJSONIsPatchablePosition()                  */
112 /************************************************************************/
113 
OGRGeoJSONIsPatchablePosition(json_object * poJSonCoordinates,json_object * poNativeCoordinates)114 static bool OGRGeoJSONIsPatchablePosition( json_object* poJSonCoordinates,
115                                            json_object* poNativeCoordinates )
116 {
117     return
118         json_object_get_type(poJSonCoordinates) == json_type_array &&
119         json_object_get_type(poNativeCoordinates) == json_type_array &&
120         json_object_array_length(poJSonCoordinates) == 3 &&
121         json_object_array_length(poNativeCoordinates) >= 4 &&
122         json_object_get_type(json_object_array_get_idx(poJSonCoordinates,
123                                                        0)) != json_type_array &&
124         json_object_get_type(json_object_array_get_idx(poNativeCoordinates,
125                                                        0)) != json_type_array;
126 }
127 
128 /************************************************************************/
129 /*                    OGRGeoJSONIsCompatiblePosition()                  */
130 /************************************************************************/
131 
OGRGeoJSONIsCompatiblePosition(json_object * poJSonCoordinates,json_object * poNativeCoordinates)132 static bool OGRGeoJSONIsCompatiblePosition( json_object* poJSonCoordinates,
133                                            json_object* poNativeCoordinates )
134 {
135     return
136         json_object_get_type(poJSonCoordinates) == json_type_array &&
137         json_object_get_type(poNativeCoordinates) == json_type_array &&
138         json_object_array_length(poJSonCoordinates) ==
139             json_object_array_length(poNativeCoordinates) &&
140         json_object_get_type(json_object_array_get_idx(poJSonCoordinates,
141                                                        0)) != json_type_array &&
142         json_object_get_type(json_object_array_get_idx(poNativeCoordinates,
143                                                        0)) != json_type_array;
144 }
145 
146 /************************************************************************/
147 /*                       OGRGeoJSONPatchPosition()                      */
148 /************************************************************************/
149 
OGRGeoJSONPatchPosition(json_object * poJSonCoordinates,json_object * poNativeCoordinates)150 static void OGRGeoJSONPatchPosition( json_object* poJSonCoordinates,
151                                      json_object* poNativeCoordinates )
152 {
153     const auto nLength = json_object_array_length(poNativeCoordinates);
154     for( auto i = decltype(nLength){3}; i < nLength; i++ )
155     {
156         json_object_array_add(poJSonCoordinates,
157             json_object_get(
158                 json_object_array_get_idx(poNativeCoordinates, i)));
159     }
160 }
161 
162 /************************************************************************/
163 /*                      OGRGeoJSONIsPatchableArray()                    */
164 /************************************************************************/
165 
OGRGeoJSONIsPatchableArray(json_object * poJSonArray,json_object * poNativeArray,int nDepth)166 static bool OGRGeoJSONIsPatchableArray( json_object* poJSonArray,
167                                         json_object* poNativeArray,
168                                         int nDepth )
169 {
170     if( nDepth == 0 )
171         return OGRGeoJSONIsPatchablePosition(poJSonArray, poNativeArray);
172 
173     if( json_object_get_type(poJSonArray) == json_type_array &&
174         json_object_get_type(poNativeArray) == json_type_array )
175     {
176         const auto nLength = json_object_array_length(poJSonArray);
177         if( nLength == json_object_array_length(poNativeArray) )
178         {
179             if( nLength > 0 )
180             {
181                 json_object* poJSonChild =
182                     json_object_array_get_idx(poJSonArray, 0);
183                 json_object* poNativeChild =
184                     json_object_array_get_idx(poNativeArray, 0);
185                 if( !OGRGeoJSONIsPatchableArray(poJSonChild, poNativeChild,
186                                                 nDepth - 1) )
187                 {
188                     return false;
189                 }
190                 // Light check as a former extensive check was done in
191                 // OGRGeoJSONComputePatchableOrCompatibleArray
192             }
193             return true;
194         }
195     }
196     return false;
197 }
198 
199 /************************************************************************/
200 /*                OGRGeoJSONComputePatchableOrCompatibleArray()         */
201 /************************************************************************/
202 
203 /* Returns true if the objects are comparable, ie Point vs Point, LineString
204    vs LineString, but they might not be patchable or compatible */
OGRGeoJSONComputePatchableOrCompatibleArrayInternal(json_object * poJSonArray,json_object * poNativeArray,int nDepth,bool & bOutPatchable,bool & bOutCompatible)205 static bool OGRGeoJSONComputePatchableOrCompatibleArrayInternal(
206                                                     json_object* poJSonArray,
207                                                     json_object* poNativeArray,
208                                                     int nDepth,
209                                                     bool& bOutPatchable,
210                                                     bool& bOutCompatible)
211 {
212     if( nDepth == 0 )
213     {
214         bOutPatchable &= OGRGeoJSONIsPatchablePosition(poJSonArray, poNativeArray);
215         bOutCompatible &= OGRGeoJSONIsCompatiblePosition(poJSonArray, poNativeArray);
216         return json_object_get_type(poJSonArray) == json_type_array &&
217                json_object_get_type(poNativeArray) == json_type_array &&
218                json_object_get_type(json_object_array_get_idx(poJSonArray,
219                                                             0)) != json_type_array &&
220                json_object_get_type(json_object_array_get_idx(poNativeArray,
221                                                             0)) != json_type_array;
222     }
223 
224     if( json_object_get_type(poJSonArray) == json_type_array &&
225         json_object_get_type(poNativeArray) == json_type_array )
226     {
227         const auto nLength = json_object_array_length(poJSonArray);
228         if (nLength == json_object_array_length(poNativeArray) )
229         {
230             for( auto i = decltype(nLength){0}; i < nLength; i++ )
231             {
232                 json_object* poJSonChild =
233                     json_object_array_get_idx(poJSonArray, i);
234                 json_object* poNativeChild =
235                     json_object_array_get_idx(poNativeArray, i);
236                 if( !OGRGeoJSONComputePatchableOrCompatibleArrayInternal(poJSonChild,
237                                                     poNativeChild,
238                                                     nDepth - 1,
239                                                     bOutPatchable,
240                                                     bOutCompatible) )
241                 {
242                     return false;
243                 }
244                 if (!bOutPatchable && !bOutCompatible)
245                     break;
246             }
247             return true;
248         }
249     }
250 
251     bOutPatchable = false;
252     bOutCompatible = false;
253     return false;
254 }
255 
256 /* Returns true if the objects are comparable, ie Point vs Point, LineString
257    vs LineString, but they might not be patchable or compatible */
OGRGeoJSONComputePatchableOrCompatibleArray(json_object * poJSonArray,json_object * poNativeArray,int nDepth,bool & bOutPatchable,bool & bOutCompatible)258 static bool OGRGeoJSONComputePatchableOrCompatibleArray( json_object* poJSonArray,
259                                                     json_object* poNativeArray,
260                                                     int nDepth,
261                                                     bool& bOutPatchable,
262                                                     bool& bOutCompatible)
263 {
264     bOutPatchable = true;
265     bOutCompatible = true;
266     return OGRGeoJSONComputePatchableOrCompatibleArrayInternal(poJSonArray,
267                                                           poNativeArray,
268                                                           nDepth,
269                                                           bOutPatchable,
270                                                           bOutCompatible);
271 }
272 
273 /************************************************************************/
274 /*                        OGRGeoJSONPatchArray()                        */
275 /************************************************************************/
276 
OGRGeoJSONPatchArray(json_object * poJSonArray,json_object * poNativeArray,int nDepth)277 static void OGRGeoJSONPatchArray( json_object* poJSonArray,
278                                   json_object* poNativeArray,
279                                   int nDepth )
280 {
281     if( nDepth == 0 )
282     {
283         OGRGeoJSONPatchPosition(poJSonArray, poNativeArray);
284         return;
285     }
286     const auto nLength = json_object_array_length(poJSonArray);
287     for( auto i = decltype(nLength){0}; i<nLength; i++ )
288     {
289         json_object* poJSonChild = json_object_array_get_idx(poJSonArray, i);
290         json_object* poNativeChild =
291             json_object_array_get_idx(poNativeArray, i);
292         OGRGeoJSONPatchArray(poJSonChild, poNativeChild,nDepth-1);
293     }
294 }
295 
296 /************************************************************************/
297 /*                        OGRGeoJSONIsPatchableGeometry()                */
298 /************************************************************************/
299 
OGRGeoJSONIsPatchableGeometry(json_object * poJSonGeometry,json_object * poNativeGeometry,bool & bOutPatchableCoords,bool & bOutCompatibleCoords)300 static bool OGRGeoJSONIsPatchableGeometry( json_object* poJSonGeometry,
301                                            json_object* poNativeGeometry,
302                                            bool& bOutPatchableCoords,
303                                            bool& bOutCompatibleCoords )
304 {
305     if( json_object_get_type(poJSonGeometry) != json_type_object ||
306         json_object_get_type(poNativeGeometry) != json_type_object )
307     {
308         return false;
309     }
310 
311     json_object* poType = CPL_json_object_object_get(poJSonGeometry, "type");
312     json_object* poNativeType = CPL_json_object_object_get(poNativeGeometry, "type");
313     if( poType == nullptr || poNativeType == nullptr ||
314         json_object_get_type(poType) != json_type_string ||
315         json_object_get_type(poNativeType) != json_type_string ||
316         strcmp(json_object_get_string(poType),
317                json_object_get_string(poNativeType)) != 0 )
318     {
319         return false;
320     }
321 
322     json_object_iter it;
323     it.key = nullptr;
324     it.val = nullptr;
325     it.entry = nullptr;
326     json_object_object_foreachC(poNativeGeometry, it)
327     {
328         if( strcmp(it.key, "coordinates") == 0 )
329         {
330             json_object* poJSonCoordinates =
331                 CPL_json_object_object_get(poJSonGeometry, "coordinates");
332             json_object* poNativeCoordinates = it.val;
333             // 0 = Point
334             // 1 = LineString or MultiPoint
335             // 2 = MultiLineString or Polygon
336             // 3 = MultiPolygon
337             for( int i = 0; i <= 3; i++ )
338             {
339                 if( OGRGeoJSONComputePatchableOrCompatibleArray(poJSonCoordinates,
340                                                        poNativeCoordinates,
341                                                        i,
342                                                        bOutPatchableCoords,
343                                                        bOutCompatibleCoords) )
344                 {
345                     return bOutPatchableCoords || bOutCompatibleCoords;
346                 }
347             }
348             return false;
349         }
350         if( strcmp(it.key, "geometries") == 0 )
351         {
352             json_object* poJSonGeometries =
353                 CPL_json_object_object_get(poJSonGeometry, "geometries");
354             json_object* poNativeGeometries = it.val;
355             if( json_object_get_type(poJSonGeometries) == json_type_array &&
356                 json_object_get_type(poNativeGeometries) == json_type_array )
357             {
358                 const auto nLength = json_object_array_length(poJSonGeometries);
359                 if( nLength == json_object_array_length(poNativeGeometries) )
360                 {
361                     for( auto i = decltype(nLength){0}; i < nLength; i++ )
362                     {
363                         json_object* poJSonChild =
364                             json_object_array_get_idx(poJSonGeometries, i);
365                         json_object* poNativeChild =
366                             json_object_array_get_idx(poNativeGeometries, i);
367                         if( !OGRGeoJSONIsPatchableGeometry(poJSonChild,
368                                                         poNativeChild,
369                                                         bOutPatchableCoords,
370                                                         bOutCompatibleCoords) )
371                         {
372                             return false;
373                         }
374                     }
375                     return true;
376                 }
377             }
378             return false;
379         }
380     }
381     return false;
382 }
383 
384 /************************************************************************/
385 /*                        OGRGeoJSONPatchGeometry()                     */
386 /************************************************************************/
387 
OGRGeoJSONPatchGeometry(json_object * poJSonGeometry,json_object * poNativeGeometry,bool bPatchableCoordinates,const OGRGeoJSONWriteOptions & oOptions)388 static void OGRGeoJSONPatchGeometry( json_object* poJSonGeometry,
389                                      json_object* poNativeGeometry,
390                                      bool bPatchableCoordinates,
391                                      const OGRGeoJSONWriteOptions& oOptions )
392 {
393     json_object_iter it;
394     it.key = nullptr;
395     it.val = nullptr;
396     it.entry = nullptr;
397     json_object_object_foreachC(poNativeGeometry, it)
398     {
399         if( strcmp(it.key, "type") == 0 ||
400             strcmp(it.key, "bbox") == 0 )
401         {
402             continue;
403         }
404         if( strcmp(it.key, "coordinates") == 0 )
405         {
406             if( !bPatchableCoordinates &&
407                 !oOptions.bCanPatchCoordinatesWithNativeData )
408             {
409                 continue;
410             }
411 
412             json_object* poJSonCoordinates =
413                 CPL_json_object_object_get(poJSonGeometry, "coordinates");
414             json_object* poNativeCoordinates = it.val;
415             for( int i = 0; i <= 3; i++ )
416             {
417                 if( OGRGeoJSONIsPatchableArray(poJSonCoordinates,
418                                                poNativeCoordinates, i) )
419                 {
420                     OGRGeoJSONPatchArray(poJSonCoordinates,
421                                          poNativeCoordinates, i);
422                     break;
423                 }
424             }
425 
426             continue;
427         }
428         if( strcmp(it.key, "geometries") == 0 )
429         {
430             json_object* poJSonGeometries =
431                 CPL_json_object_object_get(poJSonGeometry, "geometries");
432             json_object* poNativeGeometries = it.val;
433             const auto nLength = json_object_array_length(poJSonGeometries);
434             for( auto i=decltype(nLength){0}; i < nLength; i++ )
435             {
436                 json_object* poJSonChild =
437                     json_object_array_get_idx(poJSonGeometries, i);
438                 json_object* poNativeChild =
439                     json_object_array_get_idx(poNativeGeometries, i);
440                 OGRGeoJSONPatchGeometry(poJSonChild, poNativeChild,
441                                         bPatchableCoordinates, oOptions);
442             }
443 
444             continue;
445         }
446 
447         // See https://tools.ietf.org/html/rfc7946#section-7.1
448         if( oOptions.bHonourReservedRFC7946Members &&
449             (strcmp(it.key, "geometry") == 0 ||
450              strcmp(it.key, "properties") == 0 ||
451              strcmp(it.key, "features") == 0) )
452         {
453             continue;
454         }
455 
456         json_object_object_add( poJSonGeometry, it.key,
457                                 json_object_get(it.val) );
458     }
459 }
460 
461 /************************************************************************/
462 /*                           OGRGeoJSONGetBBox                          */
463 /************************************************************************/
464 
OGRGeoJSONGetBBox(OGRGeometry * poGeometry,const OGRGeoJSONWriteOptions & oOptions)465 OGREnvelope3D OGRGeoJSONGetBBox( OGRGeometry* poGeometry,
466                                  const OGRGeoJSONWriteOptions& oOptions )
467 {
468     OGREnvelope3D sEnvelope;
469     poGeometry->getEnvelope(&sEnvelope);
470 
471     if( oOptions.bBBOXRFC7946 )
472     {
473         // Heuristics to determine if the geometry was split along the
474         // date line.
475         const double EPS = 1e-7;
476         const OGRwkbGeometryType eType =
477                         wkbFlatten(poGeometry->getGeometryType());
478         if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) &&
479             poGeometry->toGeometryCollection()->getNumGeometries() >= 2 &&
480             fabs(sEnvelope.MinX - (-180.0)) < EPS &&
481             fabs(sEnvelope.MaxX - 180.0) < EPS )
482         {
483             OGRGeometryCollection* poGC = poGeometry->toGeometryCollection();
484             double dfWestLimit = -180.0;
485             double dfEastLimit = 180.0;
486             bool bWestLimitIsInit = false;
487             bool bEastLimitIsInit = false;
488             for( auto&& poMember: poGC )
489             {
490                 OGREnvelope sEnvelopePart;
491                 if( poMember->IsEmpty() )
492                     continue;
493                 poMember->getEnvelope(&sEnvelopePart);
494                 const bool bTouchesMinus180 =
495                             fabs(sEnvelopePart.MinX - (-180.0)) < EPS;
496                 const bool bTouchesPlus180 =
497                             fabs(sEnvelopePart.MaxX - 180.0) < EPS;
498                 if( bTouchesMinus180 && !bTouchesPlus180 )
499                 {
500                     if( sEnvelopePart.MaxX > dfEastLimit ||
501                         !bEastLimitIsInit )
502                     {
503                         bEastLimitIsInit = true;
504                         dfEastLimit = sEnvelopePart.MaxX;
505                     }
506                 }
507                 else if( bTouchesPlus180 && !bTouchesMinus180 )
508                 {
509                     if( sEnvelopePart.MinX < dfWestLimit ||
510                         !bWestLimitIsInit )
511                     {
512                         bWestLimitIsInit = true;
513                         dfWestLimit = sEnvelopePart.MinX;
514                     }
515                 }
516                 else if( !bTouchesMinus180 && !bTouchesPlus180 )
517                 {
518                     if( sEnvelopePart.MinX > 0 &&
519                         (sEnvelopePart.MinX < dfWestLimit ||
520                             !bWestLimitIsInit))
521                     {
522                         bWestLimitIsInit = true;
523                         dfWestLimit = sEnvelopePart.MinX;
524                     }
525                     else if ( sEnvelopePart.MaxX < 0 &&
526                                 (sEnvelopePart.MaxX > dfEastLimit ||
527                                 !bEastLimitIsInit) )
528                     {
529                         bEastLimitIsInit = true;
530                         dfEastLimit = sEnvelopePart.MaxX;
531                     }
532                 }
533             }
534             sEnvelope.MinX = dfWestLimit;
535             sEnvelope.MaxX = dfEastLimit;
536         }
537     }
538 
539     return sEnvelope;
540 }
541 
542 /************************************************************************/
543 /*                           OGRGeoJSONWriteFeature                     */
544 /************************************************************************/
545 
OGRGeoJSONWriteFeature(OGRFeature * poFeature,const OGRGeoJSONWriteOptions & oOptions)546 json_object* OGRGeoJSONWriteFeature( OGRFeature* poFeature,
547                                      const OGRGeoJSONWriteOptions& oOptions )
548 {
549     CPLAssert( nullptr != poFeature );
550 
551     bool bWriteBBOX = oOptions.bWriteBBOX;
552 
553     json_object* poObj = json_object_new_object();
554     CPLAssert( nullptr != poObj );
555 
556     json_object_object_add( poObj, "type",
557                             json_object_new_string("Feature") );
558 
559 /* -------------------------------------------------------------------- */
560 /*      Write native JSon data.                                         */
561 /* -------------------------------------------------------------------- */
562     bool bIdAlreadyWritten = false;
563     const char* pszNativeMediaType = poFeature->GetNativeMediaType();
564     json_object* poNativeGeom = nullptr;
565     bool bHasProperties = true;
566     bool bWriteIdIfFoundInAttributes = true;
567     if( pszNativeMediaType &&
568         EQUAL(pszNativeMediaType, "application/vnd.geo+json") )
569     {
570         const char* pszNativeData = poFeature->GetNativeData();
571         json_object* poNativeJSon = nullptr;
572         if( pszNativeData && OGRJSonParse(pszNativeData, &poNativeJSon) &&
573             json_object_get_type(poNativeJSon) == json_type_object )
574         {
575             json_object_iter it;
576             it.key = nullptr;
577             it.val = nullptr;
578             it.entry = nullptr;
579             CPLString osNativeData;
580             bHasProperties = false;
581             json_object_object_foreachC(poNativeJSon, it)
582             {
583                 if( strcmp(it.key, "type") == 0 )
584                 {
585                     continue;
586                 }
587                 if( strcmp(it.key, "properties") == 0 )
588                 {
589                     bHasProperties = true;
590                     continue;
591                 }
592                 if( strcmp(it.key, "bbox") == 0 )
593                 {
594                     bWriteBBOX = true;
595                     continue;
596                 }
597                 if( strcmp(it.key, "geometry") == 0 )
598                 {
599                     poNativeGeom = json_object_get(it.val);
600                     continue;
601                 }
602                 if( strcmp(it.key, "id") == 0 )
603                 {
604                     const auto eType = json_object_get_type(it.val);
605                     // See https://tools.ietf.org/html/rfc7946#section-3.2
606                     if( oOptions.bHonourReservedRFC7946Members &&
607                         !oOptions.bForceIDFieldType &&
608                         eType != json_type_string &&
609                         eType != json_type_int &&
610                         eType != json_type_double )
611                     {
612                         continue;
613                     }
614 
615                     bIdAlreadyWritten = true;
616 
617                     if( it.val &&
618                         oOptions.bForceIDFieldType &&
619                         oOptions.eForcedIDFieldType == OFTInteger64 )
620                     {
621                         if( eType != json_type_int )
622                         {
623                             json_object_object_add( poObj, it.key,
624                                 json_object_new_int64(CPLAtoGIntBig(
625                                     json_object_get_string(it.val))));
626                             bWriteIdIfFoundInAttributes = false;
627                             continue;
628                         }
629                     }
630                     else if( it.val &&
631                              oOptions.bForceIDFieldType &&
632                              oOptions.eForcedIDFieldType == OFTString )
633                     {
634                         if( eType != json_type_string )
635                         {
636                             json_object_object_add( poObj, it.key,
637                                 json_object_new_string(
638                                     json_object_get_string(it.val)));
639                             bWriteIdIfFoundInAttributes = false;
640                             continue;
641                         }
642                     }
643 
644                     if( it.val != nullptr )
645                     {
646                         int nIdx = poFeature->GetDefnRef()->GetFieldIndexCaseSensitive("id");
647                         if( eType == json_type_string &&
648                             nIdx >= 0 &&
649                             poFeature->GetFieldDefnRef(nIdx)->GetType() == OFTString &&
650                             strcmp(json_object_get_string(it.val),
651                                    poFeature->GetFieldAsString(nIdx)) == 0 )
652                         {
653                             bWriteIdIfFoundInAttributes = false;
654                         }
655                         else if ( eType == json_type_int &&
656                                 nIdx >= 0 &&
657                                 (poFeature->GetFieldDefnRef(nIdx)->GetType() == OFTInteger ||
658                                 poFeature->GetFieldDefnRef(nIdx)->GetType() == OFTInteger64) &&
659                                 json_object_get_int64(it.val) ==
660                                     poFeature->GetFieldAsInteger64(nIdx) )
661                         {
662                             bWriteIdIfFoundInAttributes = false;
663                         }
664                     }
665                 }
666 
667                 // See https://tools.ietf.org/html/rfc7946#section-7.1
668                 if( oOptions.bHonourReservedRFC7946Members &&
669                     (strcmp(it.key, "coordinates") == 0 ||
670                      strcmp(it.key, "geometries") == 0 ||
671                      strcmp(it.key, "features") == 0) )
672                 {
673                     continue;
674                 }
675 
676                 json_object_object_add( poObj, it.key,
677                                         json_object_get(it.val) );
678             }
679             json_object_put(poNativeJSon);
680         }
681     }
682 
683 /* -------------------------------------------------------------------- */
684 /*      Write FID if available                                          */
685 /* -------------------------------------------------------------------- */
686     if( !oOptions.osIDField.empty() )
687     {
688         int nIdx = poFeature->GetDefnRef()->GetFieldIndexCaseSensitive(oOptions.osIDField);
689         if( nIdx >= 0 )
690         {
691             if( (oOptions.bForceIDFieldType &&
692                  oOptions.eForcedIDFieldType == OFTInteger64) ||
693                 (!oOptions.bForceIDFieldType &&
694                  (poFeature->GetFieldDefnRef(nIdx)->GetType() == OFTInteger ||
695                   poFeature->GetFieldDefnRef(nIdx)->GetType() == OFTInteger64)) )
696             {
697                 json_object_object_add( poObj, "id",
698                     json_object_new_int64(poFeature->GetFieldAsInteger64(nIdx)) );
699             }
700             else
701             {
702                 json_object_object_add( poObj, "id",
703                     json_object_new_string(poFeature->GetFieldAsString(nIdx)) );
704             }
705         }
706     }
707     else if( poFeature->GetFID() != OGRNullFID && !bIdAlreadyWritten )
708     {
709         if( oOptions.bForceIDFieldType &&
710             oOptions.eForcedIDFieldType == OFTString )
711         {
712             json_object_object_add( poObj, "id", json_object_new_string(
713                 CPLSPrintf(CPL_FRMT_GIB,poFeature->GetFID())) );
714         }
715         else
716         {
717             json_object_object_add( poObj, "id",
718                                 json_object_new_int64(poFeature->GetFID()) );
719         }
720     }
721 
722 /* -------------------------------------------------------------------- */
723 /*      Write feature attributes to GeoJSON "properties" object.        */
724 /* -------------------------------------------------------------------- */
725     if( bHasProperties )
726     {
727         json_object* poObjProps
728             = OGRGeoJSONWriteAttributes( poFeature, bWriteIdIfFoundInAttributes, oOptions );
729         json_object_object_add( poObj, "properties", poObjProps );
730     }
731 
732 /* -------------------------------------------------------------------- */
733 /*      Write feature geometry to GeoJSON "geometry" object.            */
734 /*      Null geometries are allowed, according to the GeoJSON Spec.     */
735 /* -------------------------------------------------------------------- */
736     json_object* poObjGeom = nullptr;
737 
738     OGRGeometry* poGeometry = poFeature->GetGeometryRef();
739     if( nullptr != poGeometry )
740     {
741         poObjGeom = OGRGeoJSONWriteGeometry( poGeometry, oOptions );
742 
743         if( bWriteBBOX && !poGeometry->IsEmpty() )
744         {
745             OGREnvelope3D sEnvelope = OGRGeoJSONGetBBox( poGeometry, oOptions );
746 
747             json_object* poObjBBOX = json_object_new_array();
748             json_object_array_add(
749                 poObjBBOX,
750                 json_object_new_coord(sEnvelope.MinX, oOptions));
751             json_object_array_add(
752                 poObjBBOX,
753                 json_object_new_coord(sEnvelope.MinY, oOptions));
754             if( wkbHasZ(poGeometry->getGeometryType()) )
755                 json_object_array_add(
756                     poObjBBOX,
757                     json_object_new_coord(sEnvelope.MinZ, oOptions));
758             json_object_array_add(
759                 poObjBBOX,
760                 json_object_new_coord(sEnvelope.MaxX, oOptions));
761             json_object_array_add(
762                 poObjBBOX,
763                 json_object_new_coord(sEnvelope.MaxY, oOptions));
764             if( wkbHasZ(poGeometry->getGeometryType()) )
765                 json_object_array_add(
766                     poObjBBOX,
767                     json_object_new_coord(sEnvelope.MaxZ, oOptions));
768 
769             json_object_object_add( poObj, "bbox", poObjBBOX );
770         }
771 
772         bool bOutPatchableCoords = false;
773         bool bOutCompatibleCoords = false;
774         if( OGRGeoJSONIsPatchableGeometry( poObjGeom, poNativeGeom,
775                                            bOutPatchableCoords,
776                                            bOutCompatibleCoords ) )
777         {
778             OGRGeoJSONPatchGeometry( poObjGeom, poNativeGeom,
779                                      bOutPatchableCoords, oOptions );
780         }
781     }
782 
783     json_object_object_add( poObj, "geometry", poObjGeom );
784 
785     if( poNativeGeom != nullptr )
786         json_object_put(poNativeGeom);
787 
788     return poObj;
789 }
790 
791 /************************************************************************/
792 /*                        OGRGeoJSONWriteAttributes                     */
793 /************************************************************************/
794 
OGRGeoJSONWriteAttributes(OGRFeature * poFeature,bool bWriteIdIfFoundInAttributes,const OGRGeoJSONWriteOptions & oOptions)795 json_object* OGRGeoJSONWriteAttributes( OGRFeature* poFeature,
796                                         bool bWriteIdIfFoundInAttributes,
797                                         const OGRGeoJSONWriteOptions& oOptions )
798 {
799     CPLAssert( nullptr != poFeature );
800 
801     json_object* poObjProps = json_object_new_object();
802     CPLAssert( nullptr != poObjProps );
803 
804     OGRFeatureDefn* poDefn = poFeature->GetDefnRef();
805 
806     const int nIDField = !oOptions.osIDField.empty() ?
807         poDefn->GetFieldIndexCaseSensitive(oOptions.osIDField) : -1;
808 
809     constexpr int MAX_SIGNIFICANT_DIGITS_FLOAT32 = 8;
810     const int nFloat32SignificantDigits =
811         oOptions.nSignificantFigures >= 0 ?
812                         std::min(oOptions.nSignificantFigures,
813                                  MAX_SIGNIFICANT_DIGITS_FLOAT32):
814                         MAX_SIGNIFICANT_DIGITS_FLOAT32;
815 
816     const int nFieldCount = poDefn->GetFieldCount();
817     for( int nField = 0; nField < nFieldCount; ++nField )
818     {
819         if( !poFeature->IsFieldSet(nField) || nField == nIDField )
820         {
821             continue;
822         }
823 
824         OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn( nField );
825         CPLAssert( nullptr != poFieldDefn );
826         const OGRFieldType eType = poFieldDefn->GetType();
827         const OGRFieldSubType eSubType = poFieldDefn->GetSubType();
828 
829         if( !bWriteIdIfFoundInAttributes &&
830             strcmp(poFieldDefn->GetNameRef(), "id") == 0 )
831         {
832             continue;
833         }
834 
835         json_object* poObjProp = nullptr;
836 
837         if( poFeature->IsFieldNull(nField) )
838         {
839             // poObjProp = NULL;
840         }
841         else if( OFTInteger == eType )
842         {
843             if( eSubType == OFSTBoolean )
844                 poObjProp = json_object_new_boolean(
845                     poFeature->GetFieldAsInteger( nField ) );
846             else
847                 poObjProp = json_object_new_int(
848                     poFeature->GetFieldAsInteger( nField ) );
849         }
850         else if( OFTInteger64 == eType )
851         {
852             if( eSubType == OFSTBoolean )
853                 poObjProp = json_object_new_boolean(
854                     (json_bool)poFeature->GetFieldAsInteger64( nField ) );
855             else
856                 poObjProp = json_object_new_int64(
857                     poFeature->GetFieldAsInteger64( nField ) );
858         }
859         else if( OFTReal == eType )
860         {
861             const double val = poFeature->GetFieldAsDouble(nField);
862             if( !CPLIsFinite(val) )
863             {
864                 if( !oOptions.bAllowNonFiniteValues )
865                 {
866                     static bool bHasWarned = false;
867                     if( !bHasWarned )
868                     {
869                         bHasWarned = true;
870                         CPLError(CE_Warning, CPLE_AppDefined,
871                                  "NaN of Infinity value found. Skipped");
872                     }
873                     continue;
874                 }
875             }
876             if( eSubType == OFSTFloat32 )
877             {
878                 poObjProp = json_object_new_float_with_significant_figures(
879                     static_cast<float>(val), nFloat32SignificantDigits );
880             }
881             else
882             {
883                 poObjProp = json_object_new_double_with_significant_figures(
884                     val,
885                     oOptions.nSignificantFigures );
886             }
887         }
888         else if( OFTString == eType )
889         {
890             const char* pszStr = poFeature->GetFieldAsString(nField);
891             const size_t nLen = strlen(pszStr);
892             poObjProp = nullptr;
893             if( (pszStr[0] == '{' && pszStr[nLen-1] == '}') ||
894                 (pszStr[0] == '[' && pszStr[nLen-1] == ']') )
895             {
896                 OGRJSonParse(pszStr, &poObjProp, false);
897             }
898             if( poObjProp == nullptr )
899                 poObjProp = json_object_new_string( pszStr );
900         }
901         else if( OFTIntegerList == eType )
902         {
903             int nSize = 0;
904             const int* panList =
905                 poFeature->GetFieldAsIntegerList(nField, &nSize);
906             poObjProp = json_object_new_array();
907             for( int i = 0; i < nSize; i++ )
908             {
909                 if( eSubType == OFSTBoolean )
910                     json_object_array_add(
911                         poObjProp,
912                         json_object_new_boolean(panList[i]));
913                 else
914                     json_object_array_add(
915                         poObjProp,
916                         json_object_new_int(panList[i]));
917             }
918         }
919         else if( OFTInteger64List == eType )
920         {
921             int nSize = 0;
922             const GIntBig* panList =
923                 poFeature->GetFieldAsInteger64List(nField, &nSize);
924             poObjProp = json_object_new_array();
925             for( int i = 0; i < nSize; i++ )
926             {
927                 if( eSubType == OFSTBoolean )
928                     json_object_array_add(
929                         poObjProp,
930                         json_object_new_boolean(
931                             static_cast<json_bool>(panList[i])));
932                 else
933                     json_object_array_add(
934                         poObjProp,
935                         json_object_new_int64(panList[i]));
936             }
937         }
938         else if( OFTRealList == eType )
939         {
940             int nSize = 0;
941             const double* padfList =
942                 poFeature->GetFieldAsDoubleList(nField, &nSize);
943             poObjProp = json_object_new_array();
944             for( int i = 0; i < nSize; i++ )
945             {
946                 if( eSubType == OFSTFloat32 )
947                 {
948                     json_object_array_add(
949                         poObjProp,
950                         json_object_new_float_with_significant_figures(
951                             static_cast<float>(padfList[i]),
952                             nFloat32SignificantDigits));
953                 }
954                 else
955                 {
956                     json_object_array_add(
957                         poObjProp,
958                         json_object_new_double_with_significant_figures(
959                             padfList[i],
960                             oOptions.nSignificantFigures));
961                 }
962             }
963         }
964         else if( OFTStringList == eType )
965         {
966             char** papszStringList = poFeature->GetFieldAsStringList(nField);
967             poObjProp = json_object_new_array();
968             for( int i = 0; papszStringList && papszStringList[i]; i++ )
969             {
970                 json_object_array_add(
971                     poObjProp,
972                     json_object_new_string(papszStringList[i]));
973             }
974         }
975         else if( OFTDateTime == eType || OFTDate == eType )
976         {
977             char* pszDT = OGRGetXMLDateTime(poFeature->GetRawFieldRef(nField));
978             if( eType == OFTDate )
979             {
980                 char* pszT = strchr(pszDT, 'T');
981                 if( pszT )
982                     *pszT = 0;
983             }
984             poObjProp = json_object_new_string(pszDT);
985             CPLFree(pszDT);
986         }
987         else
988         {
989             poObjProp = json_object_new_string(
990                 poFeature->GetFieldAsString(nField) );
991         }
992 
993         json_object_object_add( poObjProps,
994                                 poFieldDefn->GetNameRef(),
995                                 poObjProp );
996     }
997 
998     return poObjProps;
999 }
1000 
1001 /************************************************************************/
1002 /*                           OGRGeoJSONWriteGeometry                    */
1003 /************************************************************************/
1004 
OGRGeoJSONWriteGeometry(const OGRGeometry * poGeometry,int nCoordPrecision,int nSignificantFigures)1005 json_object* OGRGeoJSONWriteGeometry( const OGRGeometry* poGeometry,
1006                                       int nCoordPrecision,
1007                                       int nSignificantFigures )
1008 {
1009     OGRGeoJSONWriteOptions oOptions;
1010     oOptions.nCoordPrecision = nCoordPrecision;
1011     oOptions.nSignificantFigures = nSignificantFigures;
1012     return OGRGeoJSONWriteGeometry( poGeometry, oOptions );
1013 }
1014 
OGRGeoJSONWriteGeometry(const OGRGeometry * poGeometry,const OGRGeoJSONWriteOptions & oOptions)1015 json_object* OGRGeoJSONWriteGeometry( const OGRGeometry* poGeometry,
1016                                       const OGRGeoJSONWriteOptions& oOptions )
1017 {
1018     if( poGeometry == nullptr )
1019     {
1020         CPLAssert( false );
1021         return nullptr;
1022     }
1023 
1024     OGRwkbGeometryType eFType = wkbFlatten(poGeometry->getGeometryType());
1025     // For point empty, return a null geometry. For other empty geometry types,
1026     // we will generate an empty coordinate array, which is probably also
1027     // borderline.
1028     if( eFType == wkbPoint && poGeometry->IsEmpty() )
1029     {
1030         return nullptr;
1031     }
1032 
1033     json_object* poObj = json_object_new_object();
1034     CPLAssert( nullptr != poObj );
1035 
1036 /* -------------------------------------------------------------------- */
1037 /*      Build "type" member of GeoJSOn "geometry" object.               */
1038 /* -------------------------------------------------------------------- */
1039 
1040     // XXX - mloskot: workaround hack for pure JSON-C API design.
1041     char* pszName = const_cast<char*>(OGRGeoJSONGetGeometryName( poGeometry ));
1042     json_object_object_add( poObj, "type", json_object_new_string(pszName) );
1043 
1044 /* -------------------------------------------------------------------- */
1045 /*      Build "coordinates" member of GeoJSOn "geometry" object.        */
1046 /* -------------------------------------------------------------------- */
1047     json_object* poObjGeom = nullptr;
1048 
1049     if( eFType == wkbGeometryCollection  )
1050     {
1051         poObjGeom =
1052             OGRGeoJSONWriteGeometryCollection(
1053                 poGeometry->toGeometryCollection(), oOptions );
1054         json_object_object_add( poObj, "geometries", poObjGeom);
1055     }
1056     else
1057     {
1058         if( wkbPoint == eFType )
1059             poObjGeom =
1060                 OGRGeoJSONWritePoint( poGeometry->toPoint(), oOptions );
1061         else if( wkbLineString == eFType )
1062             poObjGeom =
1063                 OGRGeoJSONWriteLineString(poGeometry->toLineString(),
1064                                           oOptions );
1065         else if( wkbPolygon == eFType )
1066             poObjGeom =
1067                 OGRGeoJSONWritePolygon( poGeometry->toPolygon(), oOptions );
1068         else if( wkbMultiPoint == eFType )
1069             poObjGeom =
1070                 OGRGeoJSONWriteMultiPoint( poGeometry->toMultiPoint(),
1071                                            oOptions );
1072         else if( wkbMultiLineString == eFType )
1073             poObjGeom =
1074                 OGRGeoJSONWriteMultiLineString(
1075                     poGeometry->toMultiLineString(), oOptions );
1076         else if( wkbMultiPolygon == eFType )
1077             poObjGeom =
1078                 OGRGeoJSONWriteMultiPolygon(poGeometry->toMultiPolygon(),
1079                                             oOptions );
1080         else
1081         {
1082             CPLError(CE_Failure, CPLE_NotSupported,
1083                      "OGR geometry type unsupported as a GeoJSON geometry detected. "
1084                      "Feature gets NULL geometry assigned.");
1085         }
1086 
1087         if( poObjGeom != nullptr )
1088         {
1089             json_object_object_add( poObj, "coordinates", poObjGeom);
1090         }
1091         else
1092         {
1093             json_object_put(poObj);
1094             poObj = nullptr;
1095         }
1096     }
1097 
1098     return poObj;
1099 }
1100 
1101 /************************************************************************/
1102 /*                           OGRGeoJSONWritePoint                       */
1103 /************************************************************************/
1104 
OGRGeoJSONWritePoint(const OGRPoint * poPoint,const OGRGeoJSONWriteOptions & oOptions)1105 json_object* OGRGeoJSONWritePoint( const OGRPoint* poPoint,
1106                                    const OGRGeoJSONWriteOptions& oOptions )
1107 {
1108     CPLAssert( nullptr != poPoint );
1109 
1110     json_object* poObj = nullptr;
1111 
1112     // Generate "coordinates" object for 2D or 3D dimension.
1113     if( wkbHasZ(poPoint->getGeometryType()) )
1114     {
1115         poObj = OGRGeoJSONWriteCoords( poPoint->getX(),
1116                                        poPoint->getY(),
1117                                        poPoint->getZ(),
1118                                        oOptions );
1119     }
1120     else if( !poPoint->IsEmpty() )
1121     {
1122         poObj = OGRGeoJSONWriteCoords( poPoint->getX(),
1123                                        poPoint->getY(),
1124                                        oOptions );
1125     }
1126 
1127     return poObj;
1128 }
1129 
1130 /************************************************************************/
1131 /*                           OGRGeoJSONWriteLineString                  */
1132 /************************************************************************/
1133 
OGRGeoJSONWriteLineString(const OGRLineString * poLine,const OGRGeoJSONWriteOptions & oOptions)1134 json_object* OGRGeoJSONWriteLineString( const OGRLineString* poLine,
1135                                         const OGRGeoJSONWriteOptions& oOptions )
1136 {
1137     CPLAssert( nullptr != poLine );
1138 
1139     // Generate "coordinates" object for 2D or 3D dimension.
1140     json_object* poObj =
1141         OGRGeoJSONWriteLineCoords( poLine, oOptions );
1142 
1143     return poObj;
1144 }
1145 
1146 /************************************************************************/
1147 /*                           OGRGeoJSONWritePolygon                     */
1148 /************************************************************************/
1149 
OGRGeoJSONWritePolygon(const OGRPolygon * poPolygon,const OGRGeoJSONWriteOptions & oOptions)1150 json_object* OGRGeoJSONWritePolygon( const OGRPolygon* poPolygon,
1151                                      const OGRGeoJSONWriteOptions& oOptions )
1152 {
1153     CPLAssert( nullptr != poPolygon );
1154 
1155     // Generate "coordinates" array object.
1156     json_object* poObj = json_object_new_array();
1157 
1158     // Exterior ring.
1159     const OGRLinearRing* poRing = poPolygon->getExteriorRing();
1160     if( poRing == nullptr )
1161         return poObj;
1162 
1163     json_object* poObjRing =
1164         OGRGeoJSONWriteRingCoords( poRing, true, oOptions );
1165     if( poObjRing == nullptr )
1166     {
1167         json_object_put(poObj);
1168         return nullptr;
1169     }
1170     json_object_array_add( poObj, poObjRing );
1171 
1172     // Interior rings.
1173     const int nCount = poPolygon->getNumInteriorRings();
1174     for( int i = 0; i < nCount; ++i )
1175     {
1176         poRing = poPolygon->getInteriorRing( i );
1177         CPLAssert(poRing);
1178 
1179         poObjRing =
1180             OGRGeoJSONWriteRingCoords( poRing, false, oOptions );
1181         if( poObjRing == nullptr )
1182         {
1183             json_object_put(poObj);
1184             return nullptr;
1185         }
1186 
1187         json_object_array_add( poObj, poObjRing );
1188     }
1189 
1190     return poObj;
1191 }
1192 
1193 /************************************************************************/
1194 /*                           OGRGeoJSONWriteMultiPoint                  */
1195 /************************************************************************/
1196 
OGRGeoJSONWriteMultiPoint(const OGRMultiPoint * poGeometry,const OGRGeoJSONWriteOptions & oOptions)1197 json_object* OGRGeoJSONWriteMultiPoint( const OGRMultiPoint* poGeometry,
1198                                         const OGRGeoJSONWriteOptions& oOptions )
1199 {
1200     CPLAssert( nullptr != poGeometry );
1201 
1202     // Generate "coordinates" object for 2D or 3D dimension.
1203     json_object* poObj
1204         = json_object_new_array();
1205 
1206     for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
1207     {
1208         const OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
1209         CPLAssert( nullptr != poGeom );
1210         const OGRPoint* poPoint = poGeom->toPoint();
1211 
1212         json_object* poObjPoint =
1213             OGRGeoJSONWritePoint(poPoint, oOptions);
1214         if( poObjPoint == nullptr )
1215         {
1216             json_object_put(poObj);
1217             return nullptr;
1218         }
1219 
1220         json_object_array_add( poObj, poObjPoint );
1221     }
1222 
1223     return poObj;
1224 }
1225 
1226 /************************************************************************/
1227 /*                           OGRGeoJSONWriteMultiLineString             */
1228 /************************************************************************/
1229 
OGRGeoJSONWriteMultiLineString(const OGRMultiLineString * poGeometry,const OGRGeoJSONWriteOptions & oOptions)1230 json_object* OGRGeoJSONWriteMultiLineString( const OGRMultiLineString* poGeometry,
1231                                              const OGRGeoJSONWriteOptions& oOptions )
1232 {
1233     CPLAssert( nullptr != poGeometry );
1234 
1235     // Generate "coordinates" object for 2D or 3D dimension.
1236     json_object* poObj = json_object_new_array();
1237 
1238     for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
1239     {
1240         const OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
1241         CPLAssert( nullptr != poGeom );
1242         const OGRLineString* poLine = poGeom->toLineString();
1243 
1244         json_object* poObjLine =
1245             OGRGeoJSONWriteLineString( poLine, oOptions );
1246         if( poObjLine == nullptr )
1247         {
1248             json_object_put(poObj);
1249             return nullptr;
1250         }
1251 
1252         json_object_array_add( poObj, poObjLine );
1253     }
1254 
1255     return poObj;
1256 }
1257 
1258 /************************************************************************/
1259 /*                           OGRGeoJSONWriteMultiPolygon                */
1260 /************************************************************************/
1261 
OGRGeoJSONWriteMultiPolygon(const OGRMultiPolygon * poGeometry,const OGRGeoJSONWriteOptions & oOptions)1262 json_object* OGRGeoJSONWriteMultiPolygon( const OGRMultiPolygon* poGeometry,
1263                                           const OGRGeoJSONWriteOptions& oOptions )
1264 {
1265     CPLAssert( nullptr != poGeometry );
1266 
1267     // Generate "coordinates" object for 2D or 3D dimension.
1268     json_object* poObj = json_object_new_array();
1269 
1270     for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
1271     {
1272         const OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
1273         CPLAssert( nullptr != poGeom );
1274         const OGRPolygon* poPoly = poGeom->toPolygon();
1275 
1276         json_object* poObjPoly =
1277             OGRGeoJSONWritePolygon( poPoly, oOptions );
1278         if( poObjPoly == nullptr )
1279         {
1280             json_object_put(poObj);
1281             return nullptr;
1282         }
1283 
1284         json_object_array_add( poObj, poObjPoly );
1285     }
1286 
1287     return poObj;
1288 }
1289 
1290 /************************************************************************/
1291 /*                           OGRGeoJSONWriteGeometryCollection          */
1292 /************************************************************************/
1293 
OGRGeoJSONWriteGeometryCollection(const OGRGeometryCollection * poGeometry,const OGRGeoJSONWriteOptions & oOptions)1294 json_object* OGRGeoJSONWriteGeometryCollection(
1295     const OGRGeometryCollection* poGeometry,
1296     const OGRGeoJSONWriteOptions& oOptions )
1297 {
1298     CPLAssert( nullptr != poGeometry );
1299 
1300     /* Generate "geometries" object. */
1301     json_object* poObj = json_object_new_array();
1302 
1303     for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
1304     {
1305         const OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
1306         CPLAssert( nullptr != poGeom );
1307 
1308         json_object* poObjGeom =
1309             OGRGeoJSONWriteGeometry( poGeom, oOptions );
1310         if( poObjGeom == nullptr )
1311         {
1312             json_object_put(poObj);
1313             return nullptr;
1314         }
1315 
1316         json_object_array_add( poObj, poObjGeom );
1317     }
1318 
1319     return poObj;
1320 }
1321 
1322 /************************************************************************/
1323 /*                           OGRGeoJSONWriteCoords                      */
1324 /************************************************************************/
1325 
OGRGeoJSONWriteCoords(double const & fX,double const & fY,const OGRGeoJSONWriteOptions & oOptions)1326 json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY,
1327                                     const OGRGeoJSONWriteOptions& oOptions )
1328 {
1329     json_object* poObjCoords = nullptr;
1330     if( CPLIsInf(fX) || CPLIsInf(fY) ||
1331         CPLIsNan(fX) || CPLIsNan(fY) )
1332     {
1333         CPLError(CE_Warning, CPLE_AppDefined,
1334                  "Infinite or NaN coordinate encountered");
1335         return nullptr;
1336     }
1337     poObjCoords = json_object_new_array();
1338     json_object_array_add( poObjCoords,
1339                            json_object_new_coord( fX, oOptions ) );
1340     json_object_array_add( poObjCoords,
1341                            json_object_new_coord( fY, oOptions ) );
1342 
1343     return poObjCoords;
1344 }
1345 
OGRGeoJSONWriteCoords(double const & fX,double const & fY,double const & fZ,const OGRGeoJSONWriteOptions & oOptions)1346 json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY,
1347                                     double const& fZ,
1348                                     const OGRGeoJSONWriteOptions& oOptions )
1349 {
1350     if( CPLIsInf(fX) || CPLIsInf(fY) || CPLIsInf(fZ) ||
1351         CPLIsNan(fX) || CPLIsNan(fY) || CPLIsNan(fZ) )
1352     {
1353         CPLError(CE_Warning, CPLE_AppDefined,
1354                  "Infinite or NaN coordinate encountered");
1355         return nullptr;
1356     }
1357     json_object* poObjCoords = json_object_new_array();
1358     json_object_array_add( poObjCoords,
1359                            json_object_new_coord( fX, oOptions ) );
1360     json_object_array_add( poObjCoords,
1361                            json_object_new_coord( fY, oOptions ) );
1362     json_object_array_add( poObjCoords,
1363                            json_object_new_coord( fZ, oOptions ) );
1364 
1365     return poObjCoords;
1366 }
1367 
1368 /************************************************************************/
1369 /*                           OGRGeoJSONWriteLineCoords                  */
1370 /************************************************************************/
1371 
OGRGeoJSONWriteLineCoords(const OGRLineString * poLine,const OGRGeoJSONWriteOptions & oOptions)1372 json_object* OGRGeoJSONWriteLineCoords( const OGRLineString* poLine,
1373                                         const OGRGeoJSONWriteOptions& oOptions )
1374 {
1375     json_object* poObjPoint = nullptr;
1376     json_object* poObjCoords = json_object_new_array();
1377 
1378     const int nCount = poLine->getNumPoints();
1379     const bool bHasZ = wkbHasZ(poLine->getGeometryType());
1380     for( int i = 0; i < nCount; ++i )
1381     {
1382         if( !bHasZ )
1383             poObjPoint =
1384                 OGRGeoJSONWriteCoords( poLine->getX(i), poLine->getY(i),
1385                                        oOptions );
1386         else
1387             poObjPoint =
1388                 OGRGeoJSONWriteCoords( poLine->getX(i), poLine->getY(i),
1389                                        poLine->getZ(i),
1390                                        oOptions );
1391         if( poObjPoint == nullptr )
1392         {
1393             json_object_put(poObjCoords);
1394             return nullptr;
1395         }
1396         json_object_array_add( poObjCoords, poObjPoint );
1397     }
1398 
1399     return poObjCoords;
1400 }
1401 
1402 /************************************************************************/
1403 /*                        OGRGeoJSONWriteRingCoords                     */
1404 /************************************************************************/
1405 
OGRGeoJSONWriteRingCoords(const OGRLinearRing * poLine,bool bIsExteriorRing,const OGRGeoJSONWriteOptions & oOptions)1406 json_object* OGRGeoJSONWriteRingCoords( const OGRLinearRing* poLine,
1407                                         bool bIsExteriorRing,
1408                                         const OGRGeoJSONWriteOptions& oOptions )
1409 {
1410     json_object* poObjPoint = nullptr;
1411     json_object* poObjCoords = json_object_new_array();
1412 
1413     bool bInvertOrder = oOptions.bPolygonRightHandRule &&
1414                         ((bIsExteriorRing && poLine->isClockwise()) ||
1415                          (!bIsExteriorRing && !poLine->isClockwise()));
1416 
1417     const int nCount = poLine->getNumPoints();
1418     const bool bHasZ = wkbHasZ(poLine->getGeometryType());
1419     for( int i = 0; i < nCount; ++i )
1420     {
1421         const int nIdx = (bInvertOrder) ? nCount - 1 - i: i;
1422         if( !bHasZ )
1423             poObjPoint =
1424                 OGRGeoJSONWriteCoords( poLine->getX(nIdx), poLine->getY(nIdx),
1425                                        oOptions );
1426         else
1427             poObjPoint =
1428                 OGRGeoJSONWriteCoords( poLine->getX(nIdx), poLine->getY(nIdx),
1429                                        poLine->getZ(nIdx),
1430                                        oOptions );
1431         if( poObjPoint == nullptr )
1432         {
1433             json_object_put(poObjCoords);
1434             return nullptr;
1435         }
1436         json_object_array_add( poObjCoords, poObjPoint );
1437     }
1438 
1439     return poObjCoords;
1440 }
1441 
1442 /************************************************************************/
1443 /*                           OGR_G_ExportToJson                         */
1444 /************************************************************************/
1445 
1446 /**
1447  * \brief Convert a geometry into GeoJSON format.
1448  *
1449  * The returned string should be freed with CPLFree() when no longer required.
1450  *
1451  * This method is the same as the C++ method OGRGeometry::exportToJson().
1452  *
1453  * @param hGeometry handle to the geometry.
1454  * @return A GeoJSON fragment or NULL in case of error.
1455  */
1456 
OGR_G_ExportToJson(OGRGeometryH hGeometry)1457 char* OGR_G_ExportToJson( OGRGeometryH hGeometry )
1458 {
1459     return OGR_G_ExportToJsonEx(hGeometry, nullptr);
1460 }
1461 
1462 /************************************************************************/
1463 /*                           OGR_G_ExportToJsonEx                       */
1464 /************************************************************************/
1465 
1466 /**
1467  * \brief Convert a geometry into GeoJSON format.
1468  *
1469  * The returned string should be freed with CPLFree() when no longer required.
1470  *
1471  * The following options are supported :
1472  * <ul>
1473  * <li>COORDINATE_PRECISION=number: maximum number of figures after decimal separator to write in coordinates.</li>
1474  * <li>SIGNIFICANT_FIGURES=number: maximum number of significant figures (GDAL &gt;= 2.1).</li>
1475  * </ul>
1476  *
1477  * If COORDINATE_PRECISION is defined, SIGNIFICANT_FIGURES will be ignored if
1478  * specified.
1479  * When none are defined, the default is COORDINATE_PRECISION=15.
1480  *
1481  * This method is the same as the C++ method OGRGeometry::exportToJson().
1482  *
1483  * @param hGeometry handle to the geometry.
1484  * @param papszOptions a null terminated list of options.
1485  * @return A GeoJSON fragment or NULL in case of error.
1486  *
1487  * @since OGR 1.9.0
1488  */
1489 
OGR_G_ExportToJsonEx(OGRGeometryH hGeometry,char ** papszOptions)1490 char* OGR_G_ExportToJsonEx( OGRGeometryH hGeometry, char** papszOptions )
1491 {
1492     VALIDATE_POINTER1( hGeometry, "OGR_G_ExportToJson", nullptr );
1493 
1494     OGRGeometry* poGeometry = reinterpret_cast<OGRGeometry *>( hGeometry );
1495 
1496     const int nCoordPrecision =
1497         atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1"));
1498 
1499     const int nSignificantFigures =
1500         atoi(CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"));
1501 
1502     OGRGeoJSONWriteOptions oOptions;
1503     oOptions.nCoordPrecision = nCoordPrecision;
1504     oOptions.nSignificantFigures = nSignificantFigures;
1505 
1506     // If the CRS has latitude, longitude (or northing, easting) axis order,
1507     // and the data axis to SRS axis mapping doesn't change that order,
1508     // then swap X and Y values.
1509     bool bHasSwappedXY = false;
1510     const auto poSRS = poGeometry->getSpatialReference();
1511     if( poSRS &&
1512         (poSRS->EPSGTreatsAsLatLong() || poSRS->EPSGTreatsAsNorthingEasting()) &&
1513         poSRS->GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2} )
1514     {
1515         poGeometry->swapXY();
1516         bHasSwappedXY = true;
1517     }
1518 
1519     json_object* poObj =
1520        OGRGeoJSONWriteGeometry( poGeometry, oOptions );
1521 
1522     // Unswap back
1523     if( bHasSwappedXY )
1524         poGeometry->swapXY();
1525 
1526     if( nullptr != poObj )
1527     {
1528         char* pszJson = CPLStrdup( json_object_to_json_string( poObj ) );
1529 
1530         // Release JSON tree.
1531         json_object_put( poObj );
1532 
1533         return pszJson;
1534     }
1535 
1536     // Translation failed.
1537     return nullptr;
1538 }
1539 
1540 /************************************************************************/
1541 /*               OGR_json_double_with_precision_to_string()             */
1542 /************************************************************************/
1543 
OGR_json_double_with_precision_to_string(struct json_object * jso,struct printbuf * pb,int,int)1544 static int OGR_json_double_with_precision_to_string( struct json_object *jso,
1545                                                      struct printbuf *pb,
1546                                                      int /* level */,
1547                                                      int /* flags */)
1548 {
1549     const void* userData =
1550 #if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
1551         jso->_userdata;
1552 #else
1553         json_object_get_userdata(jso);
1554 #endif
1555     // Precision is stored as a uintptr_t content casted to void*
1556     const uintptr_t nPrecision = reinterpret_cast<uintptr_t>(userData);
1557     char szBuffer[75] = {};
1558     const double dfVal =  json_object_get_double(jso);
1559     if( fabs(dfVal) > 1e50 && !CPLIsInf(dfVal) )
1560     {
1561         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.18g", dfVal);
1562     }
1563     else
1564     {
1565         const bool bPrecisionIsNegative =
1566             (nPrecision >> (8 * sizeof(nPrecision)-1)) != 0;
1567         OGRFormatDouble( szBuffer, sizeof(szBuffer), dfVal, '.',
1568              bPrecisionIsNegative ? 15 : static_cast<int>(nPrecision) );
1569     }
1570     return printbuf_memappend(pb, szBuffer, static_cast<int>(strlen(szBuffer)));
1571 }
1572 
1573 /************************************************************************/
1574 /*                   json_object_new_double_with_precision()            */
1575 /************************************************************************/
1576 
json_object_new_double_with_precision(double dfVal,int nCoordPrecision)1577 json_object* json_object_new_double_with_precision(double dfVal,
1578                                                    int nCoordPrecision)
1579 {
1580     json_object* jso = json_object_new_double(dfVal);
1581     json_object_set_serializer(jso, OGR_json_double_with_precision_to_string,
1582                                (void*)(size_t)nCoordPrecision, nullptr );
1583     return jso;
1584 }
1585 
1586 /************************************************************************/
1587 /*             OGR_json_double_with_significant_figures_to_string()     */
1588 /************************************************************************/
1589 
1590 static int
OGR_json_double_with_significant_figures_to_string(struct json_object * jso,struct printbuf * pb,int,int)1591 OGR_json_double_with_significant_figures_to_string( struct json_object *jso,
1592                                                     struct printbuf *pb,
1593                                                     int /* level */,
1594                                                     int /* flags */)
1595 {
1596     char szBuffer[75] = {};
1597     int nSize = 0;
1598     const double dfVal = json_object_get_double(jso);
1599     if( CPLIsNan(dfVal))
1600         nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "NaN");
1601     else if( CPLIsInf(dfVal) )
1602     {
1603         if( dfVal > 0 )
1604             nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "Infinity");
1605         else
1606             nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "-Infinity");
1607     }
1608     else
1609     {
1610         char szFormatting[32] = {};
1611         const void* userData =
1612 #if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
1613             jso->_userdata;
1614 #else
1615             json_object_get_userdata(jso);
1616 #endif
1617         const uintptr_t nSignificantFigures = reinterpret_cast<uintptr_t>(userData);
1618         const bool bSignificantFiguresIsNegative =
1619             (nSignificantFigures >> (8 * sizeof(nSignificantFigures)-1)) != 0;
1620         const int nInitialSignificantFigures =
1621             bSignificantFiguresIsNegative ? 17 : static_cast<int>(nSignificantFigures);
1622         CPLsnprintf(szFormatting, sizeof(szFormatting),
1623                     "%%.%dg", nInitialSignificantFigures);
1624         nSize = CPLsnprintf(szBuffer, sizeof(szBuffer),
1625                             szFormatting, dfVal);
1626         const char* pszDot = strchr(szBuffer, '.');
1627 
1628         // Try to avoid .xxxx999999y or .xxxx000000y rounding issues by
1629         // decreasing a bit precision.
1630         if( nInitialSignificantFigures > 10 &&
1631             pszDot != nullptr &&
1632             (strstr(pszDot, "999999") != nullptr ||
1633              strstr(pszDot, "000000") != nullptr) )
1634         {
1635             bool bOK = false;
1636             for( int i = 1; i <= 3; i++ )
1637             {
1638                 CPLsnprintf(szFormatting, sizeof(szFormatting),
1639                             "%%.%dg", nInitialSignificantFigures- i);
1640                 nSize = CPLsnprintf(szBuffer, sizeof(szBuffer),
1641                                     szFormatting, dfVal);
1642                 pszDot = strchr(szBuffer, '.');
1643                 if( pszDot != nullptr &&
1644                     strstr(pszDot, "999999") == nullptr &&
1645                     strstr(pszDot, "000000") == nullptr )
1646                 {
1647                     bOK = true;
1648                     break;
1649                 }
1650             }
1651             if( !bOK )
1652             {
1653                 CPLsnprintf(szFormatting, sizeof(szFormatting),
1654                             "%%.%dg", nInitialSignificantFigures);
1655                 nSize = CPLsnprintf(szBuffer, sizeof(szBuffer),
1656                                     szFormatting, dfVal);
1657             }
1658         }
1659 
1660         if( nSize+2 < static_cast<int>(sizeof(szBuffer)) &&
1661             strchr(szBuffer, '.') == nullptr &&
1662             strchr(szBuffer, 'e') == nullptr )
1663         {
1664             nSize += CPLsnprintf(szBuffer + nSize, sizeof(szBuffer) - nSize,
1665                                  ".0");
1666         }
1667 
1668     }
1669 
1670     return printbuf_memappend(pb, szBuffer, nSize);
1671 }
1672 
1673 /************************************************************************/
1674 /*              json_object_new_double_with_significant_figures()       */
1675 /************************************************************************/
1676 
1677 json_object *
json_object_new_double_with_significant_figures(double dfVal,int nSignificantFigures)1678 json_object_new_double_with_significant_figures( double dfVal,
1679                                                  int nSignificantFigures )
1680 {
1681     json_object* jso = json_object_new_double(dfVal);
1682     json_object_set_serializer(
1683         jso, OGR_json_double_with_significant_figures_to_string,
1684         reinterpret_cast<void*>(static_cast<size_t>(nSignificantFigures)),
1685         nullptr );
1686     return jso;
1687 }
1688 
1689 /************************************************************************/
1690 /*             OGR_json_float_with_significant_figures_to_string()      */
1691 /************************************************************************/
1692 
1693 static int
OGR_json_float_with_significant_figures_to_string(struct json_object * jso,struct printbuf * pb,int,int)1694 OGR_json_float_with_significant_figures_to_string( struct json_object *jso,
1695                                                    struct printbuf *pb,
1696                                                    int /* level */,
1697                                                    int /* flags */)
1698 {
1699     char szBuffer[75] = {};
1700     int nSize = 0;
1701     const float fVal = static_cast<float>(json_object_get_double(jso));
1702     if( CPLIsNan(fVal))
1703         nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "NaN");
1704     else if( CPLIsInf(fVal) )
1705     {
1706         if( fVal > 0 )
1707             nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "Infinity");
1708         else
1709             nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "-Infinity");
1710     }
1711     else
1712     {
1713         const void* userData =
1714 #if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
1715             jso->_userdata;
1716 #else
1717             json_object_get_userdata(jso);
1718 #endif
1719         const uintptr_t nSignificantFigures = reinterpret_cast<uintptr_t>(userData);
1720         const bool bSignificantFiguresIsNegative =
1721             (nSignificantFigures >> (8 * sizeof(nSignificantFigures)-1)) != 0;
1722         const int nInitialSignificantFigures =
1723             bSignificantFiguresIsNegative ? 8 : static_cast<int>(nSignificantFigures);
1724         nSize = OGRFormatFloat(szBuffer, sizeof(szBuffer), fVal,
1725                                nInitialSignificantFigures, 'g');
1726     }
1727 
1728     return printbuf_memappend(pb, szBuffer, nSize);
1729 }
1730 
1731 /************************************************************************/
1732 /*              json_object_new_float_with_significant_figures()        */
1733 /************************************************************************/
1734 
1735 json_object *
json_object_new_float_with_significant_figures(float fVal,int nSignificantFigures)1736 json_object_new_float_with_significant_figures( float fVal,
1737                                                 int nSignificantFigures )
1738 {
1739     json_object* jso = json_object_new_double(fVal);
1740     json_object_set_serializer(
1741         jso, OGR_json_float_with_significant_figures_to_string,
1742         reinterpret_cast<void*>(static_cast<size_t>(nSignificantFigures)),
1743         nullptr );
1744     return jso;
1745 }
1746