EVOLUTION-MANAGER
Edit File: ogrgmejson.cpp
/****************************************************************************** * $Id$ * * Project: Google Maps Engine API Driver * Purpose: GME GeoJSON helpper function Implementations. * Author: Wolf Beregnheim <wolf+grass@bergenheim.net> * (derived from Geo JSON driver by Mateusz) * ****************************************************************************** * Copyright (c) 2013, Frank Warmerdam <warmerdam@pobox.com> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "ogrgmejson.h" #include <printbuf.h> static int json_gme_double_to_string(json_object *jso, printbuf *pb, int level, int flags); /************************************************************************/ /* OGRGMEFeatureToGeoJSON() */ /************************************************************************/ json_object* OGRGMEFeatureToGeoJSON(OGRFeature* poFeature) { if( NULL == poFeature ) return NULL; json_object* pjoFeature = json_object_new_object(); CPLAssert( NULL != pjoFeature ); json_object_object_add( pjoFeature, "type", json_object_new_string("Feature") ); /* -------------------------------------------------------------------- */ /* Write feature geometry to GeoJSON "geometry" object. */ /* -------------------------------------------------------------------- */ json_object* pjoGeometry = NULL; OGRGeometry* poGeometry = poFeature->GetGeometryRef(); pjoGeometry = OGRGMEGeometryToGeoJSON(poGeometry); if ( NULL == pjoGeometry ) { CPLError( CE_Failure, CPLE_AppDefined, "GME: NULL Geometry detected in feature %ld. Ignoring feature.", poFeature->GetFID() ); json_object_put( pjoFeature ); return NULL; } json_object_object_add( pjoFeature, "geometry", pjoGeometry ); /* -------------------------------------------------------------------- */ /* Write feature attributes to GeoJSON "properties" object. */ /* -------------------------------------------------------------------- */ json_object* pjoProps = NULL; pjoProps = OGRGMEAttributesToGeoJSON( poFeature ); if ( pjoProps ) json_object_object_add( pjoFeature, "properties", pjoProps ); return pjoFeature; } /************************************************************************/ /* OGRGMEGeometryToGeoJSON() */ /************************************************************************/ json_object* OGRGMEGeometryToGeoJSON(OGRGeometry* poGeometry) { if ( NULL == poGeometry ) return NULL; json_object* pjoGeometry = json_object_new_object(); CPLAssert( NULL != pjoGeometry ); /* -------------------------------------------------------------------- */ /* Build "type" member of GeoJSOn "geometry" object */ /* and "coordinates" member of GeoJSOn "geometry" object. */ /* -------------------------------------------------------------------- */ const char* pszType = NULL; OGRwkbGeometryType eType = poGeometry->getGeometryType(); json_object* pjoCoordinates = NULL; if( wkbGeometryCollection == eType || wkbGeometryCollection25D == eType ) { pszType = "GeometryCollection"; json_object *pjoGeometries = OGRGMEGeometryCollectionToGeoJSON(static_cast<OGRGeometryCollection*>(poGeometry)); if ( pjoGeometries ) { json_object *pjoType = json_object_new_string(pszType); json_object_object_add( pjoGeometry, "type", pjoType ); json_object_object_add( pjoGeometry, "geometries", pjoGeometries ); } else { json_object_put(pjoGeometry); pjoGeometry = NULL; } } else { if( wkbPoint == eType || wkbPoint25D == eType ) { pszType = "Point"; pjoCoordinates = OGRGMEPointToGeoJSON( static_cast<OGRPoint*>(poGeometry) ); } if( wkbMultiPoint == eType || wkbMultiPoint25D == eType ) { pszType = "MultiPoint"; pjoCoordinates = OGRGMEMultiPointToGeoJSON( static_cast<OGRMultiPoint*>(poGeometry) ); } else if( wkbLineString == eType || wkbLineString25D == eType ) { pszType = "LineString"; pjoCoordinates = OGRGMELineStringToGeoJSON( static_cast<OGRLineString*>(poGeometry) ); } else if( wkbMultiLineString == eType || wkbMultiLineString25D == eType ) { pszType = "MultiLineString"; pjoCoordinates = OGRGMEMultiLineStringToGeoJSON( static_cast<OGRMultiLineString*>(poGeometry) ); } else if( wkbPolygon == eType || wkbPolygon25D == eType ) { pszType = "Polygon"; pjoCoordinates = OGRGMEPolygonToGeoJSON( static_cast<OGRPolygon*>(poGeometry) ); } else if( wkbMultiPolygon == eType || wkbMultiPolygon25D == eType ) { pszType = "MultiPolygon"; pjoCoordinates = OGRGMEMultiPolygonToGeoJSON( static_cast<OGRMultiPolygon*>(poGeometry) ); } else { CPLDebug( "GME", "Unsupported geometry type detected. Geometry is IGNORED." ); } if ( pjoCoordinates && pszType ) { json_object *pjoType = json_object_new_string(pszType); json_object_object_add( pjoGeometry, "type", pjoType ); json_object_object_add( pjoGeometry, "coordinates", pjoCoordinates); } else { json_object_put(pjoGeometry); pjoGeometry = NULL; } } return pjoGeometry; } /************************************************************************/ /* OGRGMEGeometryCollectionToGeoJSON() */ /************************************************************************/ json_object* OGRGMEGeometryCollectionToGeoJSON(OGRGeometryCollection* poGeometryCollection) { if ( NULL == poGeometryCollection ) return NULL; /* Generate "geometries" object. */ json_object* pjoGeometries = NULL; pjoGeometries = json_object_new_array(); for( int i = 0; i < poGeometryCollection->getNumGeometries(); ++i ) { OGRGeometry* poGeometry = poGeometryCollection->getGeometryRef( i ); json_object* pjoGeometry = NULL; pjoGeometry = OGRGMEGeometryToGeoJSON( poGeometry ); if ( NULL != poGeometry ) json_object_array_add( pjoGeometries, pjoGeometry ); else CPLError( CE_Failure, CPLE_AppDefined, "GME: Ignoring NULL geometry" ); } return pjoGeometries; } /************************************************************************/ /* OGRGMEPointToGeoJSON */ /************************************************************************/ json_object* OGRGMEPointToGeoJSON( OGRPoint* poPoint ) { if( NULL == poPoint ) return NULL; json_object* pjoCoordinates = NULL; /* Generate "coordinates" object for 2D or 3D dimension. */ if( 3 == poPoint->getCoordinateDimension() ) { pjoCoordinates = OGRGMECoordsToGeoJSON( poPoint->getX(), poPoint->getY(), poPoint->getZ() ); } else if( 2 == poPoint->getCoordinateDimension() ) { pjoCoordinates = OGRGMECoordsToGeoJSON( poPoint->getX(), poPoint->getY() ); } else { CPLError( CE_Failure, CPLE_AppDefined, "GME: Found EMPTY point, ignoring" ); } return pjoCoordinates; } /************************************************************************/ /* OGRGMEMultiPointToGeoJSON */ /************************************************************************/ json_object* OGRGMEMultiPointToGeoJSON( OGRMultiPoint* poGeometry ) { if( NULL == poGeometry ) return NULL; /* Generate "coordinates" object for 2D or 3D dimension. */ json_object* pjoMultiPoint = NULL; pjoMultiPoint = json_object_new_array(); for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) { OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); CPLAssert( NULL != poGeom ); OGRPoint* poPoint = static_cast<OGRPoint*>(poGeom); json_object* pjoPoint = NULL; pjoPoint = OGRGMEPointToGeoJSON( poPoint ); if ( pjoPoint ) json_object_array_add( pjoMultiPoint, pjoPoint ); } return pjoMultiPoint; } /************************************************************************/ /* OGRGMELineStringToGeoJSON */ /************************************************************************/ json_object* OGRGMELineStringToGeoJSON( OGRLineString* poLine ) { if( NULL == poLine ) return NULL; /* Generate "coordinates" object for 2D or 3D dimension. */ json_object* pjoLineString = NULL; pjoLineString = OGRGMELineCoordsToGeoJSON( poLine ); return pjoLineString; } /************************************************************************/ /* OGRGMEMultiLineStringToGeoJSON */ /************************************************************************/ json_object* OGRGMEMultiLineStringToGeoJSON( OGRMultiLineString* poGeometry ) { CPLAssert( NULL != poGeometry ); /* Generate "coordinates" object for 2D or 3D dimension. */ json_object* pjoCoordinates = NULL; pjoCoordinates = json_object_new_array(); for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) { OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); CPLAssert( NULL != poGeom ); OGRLineString* poLine = static_cast<OGRLineString*>(poGeom); json_object* pjoLine = NULL; pjoLine = OGRGMELineStringToGeoJSON( poLine ); json_object_array_add( pjoCoordinates, pjoLine ); } return pjoCoordinates; } /************************************************************************/ /* OGRGMEPolygonToGeoJSON */ /************************************************************************/ json_object* OGRGMEPolygonToGeoJSON( OGRPolygon* poPolygon ) { CPLAssert( NULL != poPolygon ); /* Generate "coordinates" array object. */ json_object* pjoCoordinates = NULL; pjoCoordinates = json_object_new_array(); /* Exterior ring. */ OGRLinearRing* poRing = poPolygon->getExteriorRing(); if (poRing == NULL) { json_object_put(pjoCoordinates); return NULL; } json_object* pjoRing = NULL; // If the linear ring is CW re-wind it CCW if (poRing->isClockwise() ) { poRing->reverseWindingOrder(); } pjoRing = OGRGMELineCoordsToGeoJSON( poRing ); json_object_array_add( pjoCoordinates, pjoRing ); /* Interior rings. */ const int nCount = poPolygon->getNumInteriorRings(); for( int i = 0; i < nCount; ++i ) { poRing = poPolygon->getInteriorRing( i ); if (poRing == NULL) continue; // If the linear ring is CW re-wind it CCW if (poRing->isClockwise() ) { poRing->reverseWindingOrder(); } pjoRing = OGRGMELineCoordsToGeoJSON( poRing ); json_object_array_add( pjoCoordinates, pjoRing ); } return pjoCoordinates; } /************************************************************************/ /* OGRGMEMultiPolygonToGeoJSON */ /************************************************************************/ json_object* OGRGMEMultiPolygonToGeoJSON( OGRMultiPolygon* poGeometry ) { CPLAssert( NULL != poGeometry ); /* Generate "coordinates" object for 2D or 3D dimension. */ json_object* pjoCoordinates = NULL; pjoCoordinates = json_object_new_array(); for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) { OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); CPLAssert( NULL != poGeom ); OGRPolygon* poPoly = static_cast<OGRPolygon*>(poGeom); json_object* pjoPoly = NULL; pjoPoly = OGRGMEPolygonToGeoJSON( poPoly ); json_object_array_add( pjoCoordinates, pjoPoly ); } return pjoCoordinates; } /************************************************************************/ /* OGRGMECoordsToGeoJSON */ /************************************************************************/ json_object* OGRGMECoordsToGeoJSON( double const& fX, double const& fY ) { json_object* pjoCoords = NULL; pjoCoords = json_object_new_array(); json_object_array_add( pjoCoords, json_object_new_gme_double( fX ) ); json_object_array_add( pjoCoords, json_object_new_gme_double( fY ) ); return pjoCoords; } json_object* OGRGMECoordsToGeoJSON( double const& fX, double const& fY, double const& fZ ) { json_object* pjoCoords = NULL; pjoCoords = json_object_new_array(); json_object_array_add( pjoCoords, json_object_new_gme_double( fX ) ); json_object_array_add( pjoCoords, json_object_new_gme_double( fY ) ); json_object_array_add( pjoCoords, json_object_new_gme_double( fZ ) ); return pjoCoords; } /************************************************************************/ /* OGRGMELineCoordsToGeoJSON */ /************************************************************************/ json_object* OGRGMELineCoordsToGeoJSON( OGRLineString* poLine ) { json_object* pjoCoords = json_object_new_array(); const int nCount = poLine->getNumPoints(); for( int i = 0; i < nCount; ++i ) { json_object* pjoPoint = NULL; if( poLine->getCoordinateDimension() == 2 ) pjoPoint = OGRGMECoordsToGeoJSON( poLine->getX(i), poLine->getY(i) ); else pjoPoint = OGRGMECoordsToGeoJSON( poLine->getX(i), poLine->getY(i), poLine->getZ(i) ); json_object_array_add( pjoCoords, pjoPoint ); } return pjoCoords; } /************************************************************************/ /* OGRGMEAttributesToGeoJSON */ /************************************************************************/ json_object* OGRGMEAttributesToGeoJSON( OGRFeature* poFeature ) { if ( NULL == poFeature ) return NULL; json_object* pjoProperties = json_object_new_object(); CPLAssert( NULL != pjoProperties ); OGRFeatureDefn* poDefn = poFeature->GetDefnRef(); for( int nField = 0; nField < poDefn->GetFieldCount(); ++nField ) { json_object* pjoProperty = NULL; OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn( nField ); if ( NULL == poFieldDefn ) continue; OGRFieldType eType = poFieldDefn->GetType(); if( !poFeature->IsFieldSet(nField) ) pjoProperty = NULL; // In GME integers are encoded as strings. else if( OFTInteger == eType ) pjoProperty = json_object_new_string( poFeature->GetFieldAsString( nField ) ); else if( OFTReal == eType ) pjoProperty = json_object_new_gme_double( poFeature->GetFieldAsDouble(nField) ); // Supported types are integer, double and string. So treating everything else as strings else pjoProperty = json_object_new_string( poFeature->GetFieldAsString(nField) ); json_object_object_add( pjoProperties, poFieldDefn->GetNameRef(), pjoProperty ); } int nGxId = poFeature->GetFieldIndex("gx_id"); if (nGxId < 0) { json_object* pjoProperty = NULL; long nFID = poFeature->GetFID(); char acGxId[128]; snprintf(acGxId, 128, "GDAL-%ld", nFID); CPLDebug("GME", "gx_id is not set, so adding \"gx_id\": \"%s\" field.", acGxId); pjoProperty = json_object_new_string( acGxId ); json_object_object_add( pjoProperties, "gx_id", pjoProperty); } return pjoProperties; } /************************************************************************/ /* json_object_new_gme_double() */ /************************************************************************/ json_object* json_object_new_gme_double(double dfVal) { json_object* pjoD = json_object_new_double(dfVal); json_object_set_serializer(pjoD, json_gme_double_to_string, NULL, NULL ); return pjoD; } /************************************************************************/ /* json_gme_double_to_string() */ /************************************************************************/ static int json_gme_double_to_string(json_object *pjo, printbuf *pb, CPL_UNUSED int level, CPL_UNUSED int flags) { char buf[128], *p, *q; int size; size = snprintf(buf, 128, "%.8f", json_object_get_double(pjo)); p = strchr(buf, ','); if (p) { *p = '.'; } else { p = strchr(buf, '.'); } if (p) { /* last useful digit, always keep 1 zero */ p++; for (q=p ; *q ; q++) { if (*q!='0') p=q; } /* drop trailing zeroes */ *(++p) = 0; size = p-buf; } printbuf_memappend(pb, buf, size); return size; } /************************************************************************/ /* OGRGMEParseJSON() */ /************************************************************************/ json_object *OGRGMEParseJSON( const char* pszText ) { if( NULL != pszText ) { json_tokener* jstok = NULL; json_object* jsobj = NULL; jstok = json_tokener_new(); jsobj = json_tokener_parse_ex(jstok, pszText, -1); if( jstok->err != json_tokener_success) { CPLError( CE_Failure, CPLE_AppDefined, "JSON parsing error: %s (at offset %d)", json_tokener_error_desc(jstok->err), jstok->char_offset); json_tokener_free(jstok); return NULL; } json_tokener_free(jstok); /* JSON tree is shared for while lifetime of the reader object * and will be released in the destructor. */ return jsobj; } return NULL; } /************************************************************************/ /* OGRGMEGetJSONString() */ /* */ /* Fetch a string field from a json_object (only an immediate */ /* child). */ /************************************************************************/ const char *OGRGMEGetJSONString(json_object *parent, const char *field, const char *default_value) { json_object *child = json_object_object_get(parent, field); if (child == NULL ) return default_value; return json_object_get_string(child); }