1 : /******************************************************************************
2 : * $Id: ogrgeojsonwriter.cpp 22490 2011-06-03 10:26:47Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implementation of GeoJSON writer utilities (OGR GeoJSON Driver).
6 : * Author: Mateusz Loskot, mateusz@loskot.net
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Mateusz Loskot
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 : #include "ogrgeojsonwriter.h"
30 : #include "ogrgeojsonutils.h"
31 : #include "ogr_geojson.h"
32 : #include <jsonc/json.h> // JSON-C
33 : #include <ogr_api.h>
34 :
35 : /************************************************************************/
36 : /* OGRGeoJSONWriteFeature */
37 : /************************************************************************/
38 :
39 27 : json_object* OGRGeoJSONWriteFeature( OGRFeature* poFeature, int bWriteBBOX, int nCoordPrecision )
40 : {
41 27 : CPLAssert( NULL != poFeature );
42 :
43 27 : json_object* poObj = json_object_new_object();
44 27 : CPLAssert( NULL != poObj );
45 :
46 : json_object_object_add( poObj, "type",
47 27 : json_object_new_string("Feature") );
48 :
49 : /* -------------------------------------------------------------------- */
50 : /* Write FID if available */
51 : /* -------------------------------------------------------------------- */
52 27 : if ( poFeature->GetFID() != OGRNullFID )
53 : {
54 : json_object_object_add( poObj, "id",
55 0 : json_object_new_int((int)poFeature->GetFID()) );
56 : }
57 :
58 : /* -------------------------------------------------------------------- */
59 : /* Write feature attributes to GeoJSON "properties" object. */
60 : /* -------------------------------------------------------------------- */
61 27 : json_object* poObjProps = NULL;
62 :
63 27 : poObjProps = OGRGeoJSONWriteAttributes( poFeature );
64 27 : json_object_object_add( poObj, "properties", poObjProps );
65 :
66 : /* -------------------------------------------------------------------- */
67 : /* Write feature geometry to GeoJSON "geometry" object. */
68 : /* Null geometries are allowed, according to the GeoJSON Spec. */
69 : /* -------------------------------------------------------------------- */
70 27 : json_object* poObjGeom = NULL;
71 :
72 27 : OGRGeometry* poGeometry = poFeature->GetGeometryRef();
73 27 : if ( NULL != poGeometry )
74 : {
75 27 : poObjGeom = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision );
76 :
77 27 : if ( bWriteBBOX && !poGeometry->IsEmpty() )
78 : {
79 2 : OGREnvelope3D sEnvelope;
80 2 : poGeometry->getEnvelope(&sEnvelope);
81 :
82 2 : json_object* poObjBBOX = json_object_new_array();
83 : json_object_array_add(poObjBBOX,
84 2 : json_object_new_double_with_precision(sEnvelope.MinX, nCoordPrecision));
85 : json_object_array_add(poObjBBOX,
86 2 : json_object_new_double_with_precision(sEnvelope.MinY, nCoordPrecision));
87 2 : if (poGeometry->getCoordinateDimension() == 3)
88 : json_object_array_add(poObjBBOX,
89 0 : json_object_new_double_with_precision(sEnvelope.MinZ, nCoordPrecision));
90 : json_object_array_add(poObjBBOX,
91 2 : json_object_new_double_with_precision(sEnvelope.MaxX, nCoordPrecision));
92 : json_object_array_add(poObjBBOX,
93 2 : json_object_new_double_with_precision(sEnvelope.MaxY, nCoordPrecision));
94 2 : if (poGeometry->getCoordinateDimension() == 3)
95 : json_object_array_add(poObjBBOX,
96 0 : json_object_new_double_with_precision(sEnvelope.MaxZ, nCoordPrecision));
97 :
98 2 : json_object_object_add( poObj, "bbox", poObjBBOX );
99 : }
100 : }
101 :
102 27 : json_object_object_add( poObj, "geometry", poObjGeom );
103 :
104 27 : return poObj;
105 : }
106 :
107 : /************************************************************************/
108 : /* OGRGeoJSONWriteGeometry */
109 : /************************************************************************/
110 :
111 27 : json_object* OGRGeoJSONWriteAttributes( OGRFeature* poFeature )
112 : {
113 27 : CPLAssert( NULL != poFeature );
114 :
115 27 : json_object* poObjProps = json_object_new_object();
116 27 : CPLAssert( NULL != poObjProps );
117 :
118 27 : OGRFeatureDefn* poDefn = poFeature->GetDefnRef();
119 53 : for( int nField = 0; nField < poDefn->GetFieldCount(); ++nField )
120 : {
121 : json_object* poObjProp;
122 26 : OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn( nField );
123 26 : CPLAssert( NULL != poFieldDefn );
124 26 : OGRFieldType eType = poFieldDefn->GetType();
125 :
126 26 : if( !poFeature->IsFieldSet(nField) )
127 : {
128 0 : poObjProp = NULL;
129 : }
130 26 : else if( OFTInteger == eType )
131 : {
132 : poObjProp = json_object_new_int(
133 0 : poFeature->GetFieldAsInteger( nField ) );
134 : }
135 26 : else if( OFTReal == eType )
136 : {
137 : poObjProp = json_object_new_double(
138 13 : poFeature->GetFieldAsDouble(nField) );
139 : }
140 13 : else if( OFTString == eType )
141 : {
142 : poObjProp = json_object_new_string(
143 13 : poFeature->GetFieldAsString(nField) );
144 : }
145 0 : else if( OFTIntegerList == eType )
146 : {
147 0 : int nSize = 0;
148 0 : const int* panList = poFeature->GetFieldAsIntegerList(nField, &nSize);
149 0 : poObjProp = json_object_new_array();
150 0 : for(int i=0;i<nSize;i++)
151 : {
152 : json_object_array_add(poObjProp,
153 0 : json_object_new_int(panList[i]));
154 : }
155 : }
156 0 : else if( OFTRealList == eType )
157 : {
158 0 : int nSize = 0;
159 0 : const double* padfList = poFeature->GetFieldAsDoubleList(nField, &nSize);
160 0 : poObjProp = json_object_new_array();
161 0 : for(int i=0;i<nSize;i++)
162 : {
163 : json_object_array_add(poObjProp,
164 0 : json_object_new_double(padfList[i]));
165 : }
166 : }
167 0 : else if( OFTStringList == eType )
168 : {
169 0 : char** papszStringList = poFeature->GetFieldAsStringList(nField);
170 0 : poObjProp = json_object_new_array();
171 0 : for(int i=0; papszStringList && papszStringList[i]; i++)
172 : {
173 : json_object_array_add(poObjProp,
174 0 : json_object_new_string(papszStringList[i]));
175 : }
176 : }
177 : else
178 : {
179 : poObjProp = json_object_new_string(
180 0 : poFeature->GetFieldAsString(nField) );
181 : }
182 :
183 : json_object_object_add( poObjProps,
184 : poFieldDefn->GetNameRef(),
185 26 : poObjProp );
186 : }
187 :
188 27 : return poObjProps;
189 : }
190 :
191 : /************************************************************************/
192 : /* OGRGeoJSONWriteGeometry */
193 : /************************************************************************/
194 :
195 30 : json_object* OGRGeoJSONWriteGeometry( OGRGeometry* poGeometry, int nCoordPrecision )
196 : {
197 30 : CPLAssert( NULL != poGeometry );
198 :
199 30 : json_object* poObj = json_object_new_object();
200 30 : CPLAssert( NULL != poObj );
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* Build "type" member of GeoJSOn "geometry" object. */
204 : /* -------------------------------------------------------------------- */
205 :
206 : // XXX - mloskot: workaround hack for pure JSON-C API design.
207 30 : char* pszName = const_cast<char*>(OGRGeoJSONGetGeometryName( poGeometry ));
208 30 : json_object_object_add( poObj, "type", json_object_new_string(pszName) );
209 :
210 : /* -------------------------------------------------------------------- */
211 : /* Build "coordinates" member of GeoJSOn "geometry" object. */
212 : /* -------------------------------------------------------------------- */
213 30 : json_object* poObjGeom = NULL;
214 :
215 30 : OGRwkbGeometryType eType = poGeometry->getGeometryType();
216 32 : if( wkbGeometryCollection == eType || wkbGeometryCollection25D == eType )
217 : {
218 2 : poObjGeom = OGRGeoJSONWriteGeometryCollection( static_cast<OGRGeometryCollection*>(poGeometry), nCoordPrecision );
219 2 : json_object_object_add( poObj, "geometries", poObjGeom);
220 : }
221 : else
222 : {
223 35 : if( wkbPoint == eType || wkbPoint25D == eType )
224 7 : poObjGeom = OGRGeoJSONWritePoint( static_cast<OGRPoint*>(poGeometry), nCoordPrecision );
225 24 : else if( wkbLineString == eType || wkbLineString25D == eType )
226 3 : poObjGeom = OGRGeoJSONWriteLineString( static_cast<OGRLineString*>(poGeometry), nCoordPrecision );
227 23 : else if( wkbPolygon == eType || wkbPolygon25D == eType )
228 5 : poObjGeom = OGRGeoJSONWritePolygon( static_cast<OGRPolygon*>(poGeometry), nCoordPrecision );
229 17 : else if( wkbMultiPoint == eType || wkbMultiPoint25D == eType )
230 4 : poObjGeom = OGRGeoJSONWriteMultiPoint( static_cast<OGRMultiPoint*>(poGeometry), nCoordPrecision );
231 13 : else if( wkbMultiLineString == eType || wkbMultiLineString25D == eType )
232 4 : poObjGeom = OGRGeoJSONWriteMultiLineString( static_cast<OGRMultiLineString*>(poGeometry), nCoordPrecision );
233 10 : else if( wkbMultiPolygon == eType || wkbMultiPolygon25D == eType )
234 5 : poObjGeom = OGRGeoJSONWriteMultiPolygon( static_cast<OGRMultiPolygon*>(poGeometry), nCoordPrecision );
235 : else
236 : {
237 : CPLDebug( "GeoJSON",
238 : "Unsupported geometry type detected. "
239 0 : "Feature gets NULL geometry assigned." );
240 : }
241 :
242 28 : json_object_object_add( poObj, "coordinates", poObjGeom);
243 : }
244 :
245 30 : return poObj;
246 : }
247 :
248 : /************************************************************************/
249 : /* OGRGeoJSONWritePoint */
250 : /************************************************************************/
251 :
252 16 : json_object* OGRGeoJSONWritePoint( OGRPoint* poPoint, int nCoordPrecision )
253 : {
254 16 : CPLAssert( NULL != poPoint );
255 :
256 16 : json_object* poObj = NULL;
257 :
258 : /* Generate "coordinates" object for 2D or 3D dimension. */
259 16 : if( 3 == poPoint->getCoordinateDimension() )
260 : {
261 : poObj = OGRGeoJSONWriteCoords( poPoint->getX(),
262 : poPoint->getY(),
263 : poPoint->getZ(),
264 0 : nCoordPrecision );
265 : }
266 16 : else if( 2 == poPoint->getCoordinateDimension() )
267 : {
268 : poObj = OGRGeoJSONWriteCoords( poPoint->getX(),
269 : poPoint->getY(),
270 13 : nCoordPrecision );
271 : }
272 : else
273 : {
274 : /* We can get here with POINT EMPTY geometries */
275 : }
276 :
277 16 : return poObj;
278 : }
279 :
280 : /************************************************************************/
281 : /* OGRGeoJSONWriteLineString */
282 : /************************************************************************/
283 :
284 10 : json_object* OGRGeoJSONWriteLineString( OGRLineString* poLine, int nCoordPrecision )
285 : {
286 10 : CPLAssert( NULL != poLine );
287 :
288 : /* Generate "coordinates" object for 2D or 3D dimension. */
289 10 : json_object* poObj = NULL;
290 10 : poObj = OGRGeoJSONWriteLineCoords( poLine, nCoordPrecision );
291 :
292 10 : return poObj;
293 : }
294 :
295 : /************************************************************************/
296 : /* OGRGeoJSONWritePolygon */
297 : /************************************************************************/
298 :
299 15 : json_object* OGRGeoJSONWritePolygon( OGRPolygon* poPolygon, int nCoordPrecision )
300 : {
301 15 : CPLAssert( NULL != poPolygon );
302 :
303 : /* Generate "coordinates" array object. */
304 15 : json_object* poObj = NULL;
305 15 : poObj = json_object_new_array();
306 :
307 : /* Exterior ring. */
308 15 : OGRLinearRing* poRing = poPolygon->getExteriorRing();
309 15 : if (poRing == NULL)
310 3 : return poObj;
311 :
312 12 : json_object* poObjRing = NULL;
313 12 : poObjRing = OGRGeoJSONWriteLineCoords( poRing, nCoordPrecision );
314 12 : json_object_array_add( poObj, poObjRing );
315 :
316 : /* Interior rings. */
317 12 : const int nCount = poPolygon->getNumInteriorRings();
318 14 : for( int i = 0; i < nCount; ++i )
319 : {
320 2 : poRing = poPolygon->getInteriorRing( i );
321 2 : if (poRing == NULL)
322 0 : continue;
323 :
324 2 : poObjRing = OGRGeoJSONWriteLineCoords( poRing, nCoordPrecision );
325 :
326 2 : json_object_array_add( poObj, poObjRing );
327 : }
328 :
329 12 : return poObj;
330 : }
331 :
332 : /************************************************************************/
333 : /* OGRGeoJSONWriteMultiPoint */
334 : /************************************************************************/
335 :
336 4 : json_object* OGRGeoJSONWriteMultiPoint( OGRMultiPoint* poGeometry, int nCoordPrecision )
337 : {
338 4 : CPLAssert( NULL != poGeometry );
339 :
340 : /* Generate "coordinates" object for 2D or 3D dimension. */
341 4 : json_object* poObj = NULL;
342 4 : poObj = json_object_new_array();
343 :
344 13 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
345 : {
346 9 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
347 9 : CPLAssert( NULL != poGeom );
348 9 : OGRPoint* poPoint = static_cast<OGRPoint*>(poGeom);
349 :
350 9 : json_object* poObjPoint = NULL;
351 9 : poObjPoint = OGRGeoJSONWritePoint( poPoint, nCoordPrecision );
352 :
353 9 : json_object_array_add( poObj, poObjPoint );
354 : }
355 :
356 4 : return poObj;
357 : }
358 :
359 : /************************************************************************/
360 : /* OGRGeoJSONWriteMultiLineString */
361 : /************************************************************************/
362 :
363 4 : json_object* OGRGeoJSONWriteMultiLineString( OGRMultiLineString* poGeometry, int nCoordPrecision )
364 : {
365 4 : CPLAssert( NULL != poGeometry );
366 :
367 : /* Generate "coordinates" object for 2D or 3D dimension. */
368 4 : json_object* poObj = NULL;
369 4 : poObj = json_object_new_array();
370 :
371 11 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
372 : {
373 7 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
374 7 : CPLAssert( NULL != poGeom );
375 7 : OGRLineString* poLine = static_cast<OGRLineString*>(poGeom);
376 :
377 7 : json_object* poObjLine = NULL;
378 7 : poObjLine = OGRGeoJSONWriteLineString( poLine, nCoordPrecision );
379 :
380 7 : json_object_array_add( poObj, poObjLine );
381 : }
382 :
383 4 : return poObj;
384 : }
385 :
386 : /************************************************************************/
387 : /* OGRGeoJSONWriteMultiPolygon */
388 : /************************************************************************/
389 :
390 5 : json_object* OGRGeoJSONWriteMultiPolygon( OGRMultiPolygon* poGeometry, int nCoordPrecision )
391 : {
392 5 : CPLAssert( NULL != poGeometry );
393 :
394 : /* Generate "coordinates" object for 2D or 3D dimension. */
395 5 : json_object* poObj = NULL;
396 5 : poObj = json_object_new_array();
397 :
398 15 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
399 : {
400 10 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
401 10 : CPLAssert( NULL != poGeom );
402 10 : OGRPolygon* poPoly = static_cast<OGRPolygon*>(poGeom);
403 :
404 10 : json_object* poObjPoly = NULL;
405 10 : poObjPoly = OGRGeoJSONWritePolygon( poPoly, nCoordPrecision );
406 :
407 10 : json_object_array_add( poObj, poObjPoly );
408 : }
409 :
410 5 : return poObj;
411 : }
412 :
413 : /************************************************************************/
414 : /* OGRGeoJSONWriteGeometryCollection */
415 : /************************************************************************/
416 :
417 2 : json_object* OGRGeoJSONWriteGeometryCollection( OGRGeometryCollection* poGeometry, int nCoordPrecision )
418 : {
419 2 : CPLAssert( NULL != poGeometry );
420 :
421 : /* Generate "geometries" object. */
422 2 : json_object* poObj = NULL;
423 2 : poObj = json_object_new_array();
424 :
425 3 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
426 : {
427 1 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
428 1 : CPLAssert( NULL != poGeom );
429 :
430 1 : json_object* poObjGeom = NULL;
431 1 : poObjGeom = OGRGeoJSONWriteGeometry( poGeom, nCoordPrecision );
432 :
433 1 : json_object_array_add( poObj, poObjGeom );
434 : }
435 :
436 2 : return poObj;
437 : }
438 : /************************************************************************/
439 : /* OGRGeoJSONWriteCoords */
440 : /************************************************************************/
441 :
442 80 : json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY, int nCoordPrecision )
443 : {
444 80 : json_object* poObjCoords = NULL;
445 80 : poObjCoords = json_object_new_array();
446 80 : json_object_array_add( poObjCoords, json_object_new_double_with_precision( fX, nCoordPrecision ) );
447 80 : json_object_array_add( poObjCoords, json_object_new_double_with_precision( fY, nCoordPrecision ) );
448 :
449 80 : return poObjCoords;
450 : }
451 :
452 0 : json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY, double const& fZ, int nCoordPrecision )
453 : {
454 0 : json_object* poObjCoords = NULL;
455 0 : poObjCoords = json_object_new_array();
456 0 : json_object_array_add( poObjCoords, json_object_new_double_with_precision( fX, nCoordPrecision ) );
457 0 : json_object_array_add( poObjCoords, json_object_new_double_with_precision( fY, nCoordPrecision ) );
458 0 : json_object_array_add( poObjCoords, json_object_new_double_with_precision( fZ, nCoordPrecision ) );
459 :
460 0 : return poObjCoords;
461 : }
462 :
463 : /************************************************************************/
464 : /* OGRGeoJSONWriteLineCoords */
465 : /************************************************************************/
466 :
467 24 : json_object* OGRGeoJSONWriteLineCoords( OGRLineString* poLine, int nCoordPrecision )
468 : {
469 24 : json_object* poObjPoint = NULL;
470 24 : json_object* poObjCoords = json_object_new_array();
471 :
472 24 : const int nCount = poLine->getNumPoints();
473 91 : for( int i = 0; i < nCount; ++i )
474 : {
475 67 : if( poLine->getCoordinateDimension() == 2 )
476 67 : poObjPoint = OGRGeoJSONWriteCoords( poLine->getX(i), poLine->getY(i), nCoordPrecision );
477 : else
478 0 : poObjPoint = OGRGeoJSONWriteCoords( poLine->getX(i), poLine->getY(i), poLine->getZ(i), nCoordPrecision );
479 67 : json_object_array_add( poObjCoords, poObjPoint );
480 : }
481 :
482 24 : return poObjCoords;
483 : }
484 :
485 : /************************************************************************/
486 : /* OGR_G_ExportToJson */
487 : /************************************************************************/
488 :
489 : /**
490 : * \brief Convert a geometry into GeoJSON format.
491 : *
492 : * The returned string should be freed with CPLFree() when no longer required.
493 : *
494 : * This method is the same as the C++ method OGRGeometry::exportToJson().
495 : *
496 : * @param hGeometry handle to the geometry.
497 : * @return A GeoJSON fragment or NULL in case of error.
498 : */
499 :
500 0 : char* OGR_G_ExportToJson( OGRGeometryH hGeometry )
501 : {
502 0 : return OGR_G_ExportToJsonEx(hGeometry, NULL);
503 : }
504 :
505 : /************************************************************************/
506 : /* OGR_G_ExportToJsonEx */
507 : /************************************************************************/
508 :
509 : /**
510 : * \brief Convert a geometry into GeoJSON format.
511 : *
512 : * The returned string should be freed with CPLFree() when no longer required.
513 : *
514 : * This method is the same as the C++ method OGRGeometry::exportToJson().
515 : *
516 : * @param hGeometry handle to the geometry.
517 : * @param papszOptions a null terminated list of options. For now, only COORDINATE_PRECISION=int_number
518 : * where int_number is the maximum number of figures after decimal separator to write in coordinates.
519 : * @return A GeoJSON fragment or NULL in case of error.
520 : *
521 : * @since OGR 1.9.0
522 : */
523 :
524 2 : char* OGR_G_ExportToJsonEx( OGRGeometryH hGeometry, char** papszOptions )
525 : {
526 2 : VALIDATE_POINTER1( hGeometry, "OGR_G_ExportToJson", NULL );
527 :
528 2 : OGRGeometry* poGeometry = (OGRGeometry*) (hGeometry);
529 :
530 2 : int nCoordPrecision = atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1"));
531 :
532 2 : json_object* poObj = NULL;
533 2 : poObj = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision );
534 :
535 2 : if( NULL != poObj )
536 : {
537 2 : char* pszJson = CPLStrdup( json_object_to_json_string( poObj ) );
538 :
539 : /* Release JSON tree. */
540 2 : json_object_put( poObj );
541 :
542 2 : return pszJson;
543 : }
544 :
545 : /* Translation failed */
546 0 : return NULL;
547 : }
|