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 54 : json_object* OGRGeoJSONWriteFeature( OGRFeature* poFeature, int bWriteBBOX, int nCoordPrecision )
40 : {
41 54 : CPLAssert( NULL != poFeature );
42 :
43 54 : json_object* poObj = json_object_new_object();
44 54 : CPLAssert( NULL != poObj );
45 :
46 : json_object_object_add( poObj, "type",
47 54 : json_object_new_string("Feature") );
48 :
49 : /* -------------------------------------------------------------------- */
50 : /* Write FID if available */
51 : /* -------------------------------------------------------------------- */
52 54 : 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 54 : json_object* poObjProps = NULL;
62 :
63 54 : poObjProps = OGRGeoJSONWriteAttributes( poFeature );
64 54 : 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 54 : json_object* poObjGeom = NULL;
71 :
72 54 : OGRGeometry* poGeometry = poFeature->GetGeometryRef();
73 54 : if ( NULL != poGeometry )
74 : {
75 54 : poObjGeom = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision );
76 :
77 54 : if ( bWriteBBOX && !poGeometry->IsEmpty() )
78 : {
79 4 : OGREnvelope3D sEnvelope;
80 4 : poGeometry->getEnvelope(&sEnvelope);
81 :
82 4 : json_object* poObjBBOX = json_object_new_array();
83 : json_object_array_add(poObjBBOX,
84 4 : json_object_new_double_with_precision(sEnvelope.MinX, nCoordPrecision));
85 : json_object_array_add(poObjBBOX,
86 4 : json_object_new_double_with_precision(sEnvelope.MinY, nCoordPrecision));
87 4 : 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 4 : json_object_new_double_with_precision(sEnvelope.MaxX, nCoordPrecision));
92 : json_object_array_add(poObjBBOX,
93 4 : json_object_new_double_with_precision(sEnvelope.MaxY, nCoordPrecision));
94 4 : if (poGeometry->getCoordinateDimension() == 3)
95 : json_object_array_add(poObjBBOX,
96 0 : json_object_new_double_with_precision(sEnvelope.MaxZ, nCoordPrecision));
97 :
98 4 : json_object_object_add( poObj, "bbox", poObjBBOX );
99 : }
100 : }
101 :
102 54 : json_object_object_add( poObj, "geometry", poObjGeom );
103 :
104 54 : return poObj;
105 : }
106 :
107 : /************************************************************************/
108 : /* OGRGeoJSONWriteGeometry */
109 : /************************************************************************/
110 :
111 54 : json_object* OGRGeoJSONWriteAttributes( OGRFeature* poFeature )
112 : {
113 54 : CPLAssert( NULL != poFeature );
114 :
115 54 : json_object* poObjProps = json_object_new_object();
116 54 : CPLAssert( NULL != poObjProps );
117 :
118 54 : OGRFeatureDefn* poDefn = poFeature->GetDefnRef();
119 106 : for( int nField = 0; nField < poDefn->GetFieldCount(); ++nField )
120 : {
121 : json_object* poObjProp;
122 52 : OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn( nField );
123 52 : CPLAssert( NULL != poFieldDefn );
124 52 : OGRFieldType eType = poFieldDefn->GetType();
125 :
126 52 : if( !poFeature->IsFieldSet(nField) )
127 : {
128 0 : poObjProp = NULL;
129 : }
130 52 : else if( OFTInteger == eType )
131 : {
132 : poObjProp = json_object_new_int(
133 0 : poFeature->GetFieldAsInteger( nField ) );
134 : }
135 52 : else if( OFTReal == eType )
136 : {
137 : poObjProp = json_object_new_double(
138 26 : poFeature->GetFieldAsDouble(nField) );
139 : }
140 26 : else if( OFTString == eType )
141 : {
142 : poObjProp = json_object_new_string(
143 26 : 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 52 : poObjProp );
186 : }
187 :
188 54 : return poObjProps;
189 : }
190 :
191 : /************************************************************************/
192 : /* OGRGeoJSONWriteGeometry */
193 : /************************************************************************/
194 :
195 60 : json_object* OGRGeoJSONWriteGeometry( OGRGeometry* poGeometry, int nCoordPrecision )
196 : {
197 60 : CPLAssert( NULL != poGeometry );
198 :
199 60 : json_object* poObj = json_object_new_object();
200 60 : 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 60 : char* pszName = const_cast<char*>(OGRGeoJSONGetGeometryName( poGeometry ));
208 60 : json_object_object_add( poObj, "type", json_object_new_string(pszName) );
209 :
210 : /* -------------------------------------------------------------------- */
211 : /* Build "coordinates" member of GeoJSOn "geometry" object. */
212 : /* -------------------------------------------------------------------- */
213 60 : json_object* poObjGeom = NULL;
214 :
215 60 : OGRwkbGeometryType eType = poGeometry->getGeometryType();
216 64 : if( wkbGeometryCollection == eType || wkbGeometryCollection25D == eType )
217 : {
218 4 : poObjGeom = OGRGeoJSONWriteGeometryCollection( static_cast<OGRGeometryCollection*>(poGeometry), nCoordPrecision );
219 4 : json_object_object_add( poObj, "geometries", poObjGeom);
220 : }
221 : else
222 : {
223 70 : if( wkbPoint == eType || wkbPoint25D == eType )
224 14 : poObjGeom = OGRGeoJSONWritePoint( static_cast<OGRPoint*>(poGeometry), nCoordPrecision );
225 48 : else if( wkbLineString == eType || wkbLineString25D == eType )
226 6 : poObjGeom = OGRGeoJSONWriteLineString( static_cast<OGRLineString*>(poGeometry), nCoordPrecision );
227 46 : else if( wkbPolygon == eType || wkbPolygon25D == eType )
228 10 : poObjGeom = OGRGeoJSONWritePolygon( static_cast<OGRPolygon*>(poGeometry), nCoordPrecision );
229 34 : else if( wkbMultiPoint == eType || wkbMultiPoint25D == eType )
230 8 : poObjGeom = OGRGeoJSONWriteMultiPoint( static_cast<OGRMultiPoint*>(poGeometry), nCoordPrecision );
231 26 : else if( wkbMultiLineString == eType || wkbMultiLineString25D == eType )
232 8 : poObjGeom = OGRGeoJSONWriteMultiLineString( static_cast<OGRMultiLineString*>(poGeometry), nCoordPrecision );
233 20 : else if( wkbMultiPolygon == eType || wkbMultiPolygon25D == eType )
234 10 : 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 56 : json_object_object_add( poObj, "coordinates", poObjGeom);
243 : }
244 :
245 60 : return poObj;
246 : }
247 :
248 : /************************************************************************/
249 : /* OGRGeoJSONWritePoint */
250 : /************************************************************************/
251 :
252 32 : json_object* OGRGeoJSONWritePoint( OGRPoint* poPoint, int nCoordPrecision )
253 : {
254 32 : CPLAssert( NULL != poPoint );
255 :
256 32 : json_object* poObj = NULL;
257 :
258 : /* Generate "coordinates" object for 2D or 3D dimension. */
259 32 : if( 3 == poPoint->getCoordinateDimension() )
260 : {
261 : poObj = OGRGeoJSONWriteCoords( poPoint->getX(),
262 : poPoint->getY(),
263 : poPoint->getZ(),
264 0 : nCoordPrecision );
265 : }
266 32 : else if( 2 == poPoint->getCoordinateDimension() )
267 : {
268 : poObj = OGRGeoJSONWriteCoords( poPoint->getX(),
269 : poPoint->getY(),
270 26 : nCoordPrecision );
271 : }
272 : else
273 : {
274 : /* We can get here with POINT EMPTY geometries */
275 : }
276 :
277 32 : return poObj;
278 : }
279 :
280 : /************************************************************************/
281 : /* OGRGeoJSONWriteLineString */
282 : /************************************************************************/
283 :
284 20 : json_object* OGRGeoJSONWriteLineString( OGRLineString* poLine, int nCoordPrecision )
285 : {
286 20 : CPLAssert( NULL != poLine );
287 :
288 : /* Generate "coordinates" object for 2D or 3D dimension. */
289 20 : json_object* poObj = NULL;
290 20 : poObj = OGRGeoJSONWriteLineCoords( poLine, nCoordPrecision );
291 :
292 20 : return poObj;
293 : }
294 :
295 : /************************************************************************/
296 : /* OGRGeoJSONWritePolygon */
297 : /************************************************************************/
298 :
299 30 : json_object* OGRGeoJSONWritePolygon( OGRPolygon* poPolygon, int nCoordPrecision )
300 : {
301 30 : CPLAssert( NULL != poPolygon );
302 :
303 : /* Generate "coordinates" array object. */
304 30 : json_object* poObj = NULL;
305 30 : poObj = json_object_new_array();
306 :
307 : /* Exterior ring. */
308 30 : OGRLinearRing* poRing = poPolygon->getExteriorRing();
309 30 : if (poRing == NULL)
310 6 : return poObj;
311 :
312 24 : json_object* poObjRing = NULL;
313 24 : poObjRing = OGRGeoJSONWriteLineCoords( poRing, nCoordPrecision );
314 24 : json_object_array_add( poObj, poObjRing );
315 :
316 : /* Interior rings. */
317 24 : const int nCount = poPolygon->getNumInteriorRings();
318 28 : for( int i = 0; i < nCount; ++i )
319 : {
320 4 : poRing = poPolygon->getInteriorRing( i );
321 4 : if (poRing == NULL)
322 0 : continue;
323 :
324 4 : poObjRing = OGRGeoJSONWriteLineCoords( poRing, nCoordPrecision );
325 :
326 4 : json_object_array_add( poObj, poObjRing );
327 : }
328 :
329 24 : return poObj;
330 : }
331 :
332 : /************************************************************************/
333 : /* OGRGeoJSONWriteMultiPoint */
334 : /************************************************************************/
335 :
336 8 : json_object* OGRGeoJSONWriteMultiPoint( OGRMultiPoint* poGeometry, int nCoordPrecision )
337 : {
338 8 : CPLAssert( NULL != poGeometry );
339 :
340 : /* Generate "coordinates" object for 2D or 3D dimension. */
341 8 : json_object* poObj = NULL;
342 8 : poObj = json_object_new_array();
343 :
344 26 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
345 : {
346 18 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
347 18 : CPLAssert( NULL != poGeom );
348 18 : OGRPoint* poPoint = static_cast<OGRPoint*>(poGeom);
349 :
350 18 : json_object* poObjPoint = NULL;
351 18 : poObjPoint = OGRGeoJSONWritePoint( poPoint, nCoordPrecision );
352 :
353 18 : json_object_array_add( poObj, poObjPoint );
354 : }
355 :
356 8 : return poObj;
357 : }
358 :
359 : /************************************************************************/
360 : /* OGRGeoJSONWriteMultiLineString */
361 : /************************************************************************/
362 :
363 8 : json_object* OGRGeoJSONWriteMultiLineString( OGRMultiLineString* poGeometry, int nCoordPrecision )
364 : {
365 8 : CPLAssert( NULL != poGeometry );
366 :
367 : /* Generate "coordinates" object for 2D or 3D dimension. */
368 8 : json_object* poObj = NULL;
369 8 : poObj = json_object_new_array();
370 :
371 22 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
372 : {
373 14 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
374 14 : CPLAssert( NULL != poGeom );
375 14 : OGRLineString* poLine = static_cast<OGRLineString*>(poGeom);
376 :
377 14 : json_object* poObjLine = NULL;
378 14 : poObjLine = OGRGeoJSONWriteLineString( poLine, nCoordPrecision );
379 :
380 14 : json_object_array_add( poObj, poObjLine );
381 : }
382 :
383 8 : return poObj;
384 : }
385 :
386 : /************************************************************************/
387 : /* OGRGeoJSONWriteMultiPolygon */
388 : /************************************************************************/
389 :
390 10 : json_object* OGRGeoJSONWriteMultiPolygon( OGRMultiPolygon* poGeometry, int nCoordPrecision )
391 : {
392 10 : CPLAssert( NULL != poGeometry );
393 :
394 : /* Generate "coordinates" object for 2D or 3D dimension. */
395 10 : json_object* poObj = NULL;
396 10 : poObj = json_object_new_array();
397 :
398 30 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
399 : {
400 20 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
401 20 : CPLAssert( NULL != poGeom );
402 20 : OGRPolygon* poPoly = static_cast<OGRPolygon*>(poGeom);
403 :
404 20 : json_object* poObjPoly = NULL;
405 20 : poObjPoly = OGRGeoJSONWritePolygon( poPoly, nCoordPrecision );
406 :
407 20 : json_object_array_add( poObj, poObjPoly );
408 : }
409 :
410 10 : return poObj;
411 : }
412 :
413 : /************************************************************************/
414 : /* OGRGeoJSONWriteGeometryCollection */
415 : /************************************************************************/
416 :
417 4 : json_object* OGRGeoJSONWriteGeometryCollection( OGRGeometryCollection* poGeometry, int nCoordPrecision )
418 : {
419 4 : CPLAssert( NULL != poGeometry );
420 :
421 : /* Generate "geometries" object. */
422 4 : json_object* poObj = NULL;
423 4 : poObj = json_object_new_array();
424 :
425 6 : for( int i = 0; i < poGeometry->getNumGeometries(); ++i )
426 : {
427 2 : OGRGeometry* poGeom = poGeometry->getGeometryRef( i );
428 2 : CPLAssert( NULL != poGeom );
429 :
430 2 : json_object* poObjGeom = NULL;
431 2 : poObjGeom = OGRGeoJSONWriteGeometry( poGeom, nCoordPrecision );
432 :
433 2 : json_object_array_add( poObj, poObjGeom );
434 : }
435 :
436 4 : return poObj;
437 : }
438 : /************************************************************************/
439 : /* OGRGeoJSONWriteCoords */
440 : /************************************************************************/
441 :
442 160 : json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY, int nCoordPrecision )
443 : {
444 160 : json_object* poObjCoords = NULL;
445 160 : poObjCoords = json_object_new_array();
446 160 : json_object_array_add( poObjCoords, json_object_new_double_with_precision( fX, nCoordPrecision ) );
447 160 : json_object_array_add( poObjCoords, json_object_new_double_with_precision( fY, nCoordPrecision ) );
448 :
449 160 : 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 48 : json_object* OGRGeoJSONWriteLineCoords( OGRLineString* poLine, int nCoordPrecision )
468 : {
469 48 : json_object* poObjPoint = NULL;
470 48 : json_object* poObjCoords = json_object_new_array();
471 :
472 48 : const int nCount = poLine->getNumPoints();
473 182 : for( int i = 0; i < nCount; ++i )
474 : {
475 134 : if( poLine->getCoordinateDimension() == 2 )
476 134 : 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 134 : json_object_array_add( poObjCoords, poObjPoint );
480 : }
481 :
482 48 : 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 4 : char* OGR_G_ExportToJsonEx( OGRGeometryH hGeometry, char** papszOptions )
525 : {
526 4 : VALIDATE_POINTER1( hGeometry, "OGR_G_ExportToJson", NULL );
527 :
528 4 : OGRGeometry* poGeometry = (OGRGeometry*) (hGeometry);
529 :
530 4 : int nCoordPrecision = atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1"));
531 :
532 4 : json_object* poObj = NULL;
533 4 : poObj = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision );
534 :
535 4 : if( NULL != poObj )
536 : {
537 4 : char* pszJson = CPLStrdup( json_object_to_json_string( poObj ) );
538 :
539 : /* Release JSON tree. */
540 4 : json_object_put( poObj );
541 :
542 4 : return pszJson;
543 : }
544 :
545 : /* Translation failed */
546 0 : return NULL;
547 : }
|