LCOV - code coverage report
Current view: top level - ogr - ogrgeojsonwriter.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 689 725 95.0 %
Date: 2024-11-21 22:18:42 Functions: 32 32 100.0 %

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

Generated by: LCOV version 1.14