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