Add lwgeom_from_geojson to liblwgeom, add cunit test, fix memory leak

git-svn-id: http://svn.osgeo.org/postgis/trunk@11119 b70326c6-7e19-0410-871a-916f4a2858ee
This commit is contained in:
Sandro Santilli 2013-02-28 17:04:44 +00:00
parent eb75a33623
commit 60a4fef503
9 changed files with 814 additions and 502 deletions

1
NEWS
View file

@ -68,6 +68,7 @@ PostGIS 2.1.0
- #945, improved join selectivity, N-D selectivity calculations, user
accessible selectivity and stats reader functions for testing
- #2210, ST_MinConvexHull(raster)
- lwgeom_from_geojson in liblwgeom (Sandro Santilli / Vizzuality)
* Enhancements *
- #823, tiger geocoder: Make loader_generate_script download portion

View file

@ -840,7 +840,7 @@ dnl Always enable use of ANALYZE statistics by default
AC_DEFINE_UNQUOTED([POSTGIS_USE_STATS], [1], [Enable use of ANALYZE statistics])
CPPFLAGS="$PGSQL_CPPFLAGS $GEOS_CPPFLAGS $PROJ_CPPFLAGS $JSON_CPPFLAGS $XML2_CPPFLAGS"
CPPFLAGS="$PGSQL_CPPFLAGS $GEOS_CPPFLAGS $PROJ_CPPFLAGS $XML2_CPPFLAGS"
dnl AC_MSG_RESULT([CPPFLAGS: $CPPFLAGS])
SHLIB_LINK="$PGSQL_LDFLAGS $GEOS_LDFLAGS $PROJ_LDFLAGS -lgeos_c -lproj $JSON_LDFLAGS $XML2_LDFLAGS"

View file

@ -11,8 +11,8 @@
# **********************************************************************
CC = @CC@
CFLAGS = @CFLAGS@ @PICFLAGS@ @WARNFLAGS@ @GEOS_CPPFLAGS@ @PROJ_CPPFLAGS@
LDFLAGS = @LDFLAGS@ @GEOS_LDFLAGS@ -lgeos_c @PROJ_LDFLAGS@ -lproj
CFLAGS = @CFLAGS@ @PICFLAGS@ @WARNFLAGS@ @GEOS_CPPFLAGS@ @PROJ_CPPFLAGS@ @JSON_CPPFLAGS@
LDFLAGS = @LDFLAGS@ @GEOS_LDFLAGS@ -lgeos_c @PROJ_LDFLAGS@ -lproj @JSON_LDFLAGS@
NUMERICFLAGS = @NUMERICFLAGS@
top_builddir = @top_builddir@
prefix = @prefix@
@ -57,6 +57,7 @@ SA_OBJS = \
lwpsurface.o \
lwtin.o \
lwout_wkb.o \
lwin_geojson.o \
lwin_wkb.o \
lwout_wkt.o \
lwin_wkt_parse.o \

View file

@ -46,6 +46,7 @@ OBJS= \
cu_out_svg.o \
cu_surface.o \
cu_out_x3d.o \
cu_in_geojson.o \
cu_in_wkb.o \
cu_in_wkt.o \
cu_tester.o

View file

@ -0,0 +1,228 @@
/**********************************************************************
* $Id$
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
*
* Copyright 2013 Sandro Santilli <strk@keybit.net>
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU General Public Licence. See the COPYING file.
*
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CUnit/Basic.h"
#include "liblwgeom_internal.h"
#include "cu_tester.h"
static void do_geojson_test(const char * exp, char * in, char * exp_srs, int precision, int has_bbox)
{
LWGEOM *g;
char * h = NULL;
char * srs = NULL;
size_t size;
has_bbox = has_bbox; /* unused */
g = lwgeom_from_geojson(in, &srs);
h = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &size);
if (strcmp(h, exp)) {
fprintf(stderr, "\nIn: %s\nExp: %s\nObt: %s\n", in, exp, h);
CU_ASSERT_STRING_EQUAL(h, exp);
}
if ( exp_srs ) {
if ( ! srs ) {
fprintf(stderr, "\nIn: %s\nExp: %s\nObt: (null)\n", in, exp_srs);
CU_ASSERT_EQUAL(srs, exp_srs);
}
else if (strcmp(srs, exp_srs)) {
fprintf(stderr, "\nIn: %s\nExp: %s\nObt: %s\n", in, exp_srs, srs);
CU_ASSERT_STRING_EQUAL(srs, exp_srs);
}
} else if ( srs ) {
fprintf(stderr, "\nIn: %s\nExp: (null)\nObt: %s\n", in, srs);
CU_ASSERT_EQUAL(srs, exp_srs);
}
lwgeom_free(g);
if ( h ) lwfree(h);
if ( srs ) lwfree(srs);
}
static void do_geojson_unsupported(char * in, char * exp)
{
LWGEOM *g;
char * h = NULL;
char * srs = NULL;
size_t size;
g = lwgeom_from_geojson(in, &srs);
if ( g ) {
h = lwgeom_to_wkt(g, WKT_ISO, 1, &size);
fprintf(stderr, "\nIn: %s\nExp: %s\nObt: %s\n",
in, exp, h);
CU_ASSERT(!g);
} else {
if (strcmp(cu_error_msg, exp))
fprintf(stderr, "\nIn: %s\nExp: %s\nObt: %s\n",
in, exp, cu_error_msg);
CU_ASSERT_STRING_EQUAL(in, cu_error_msg);
}
cu_error_msg_reset();
if ( srs ) lwfree(srs);
if ( h ) lwfree(h);
lwgeom_free(g);
}
static void in_geojson_test_srid(void)
{
/* Linestring */
do_geojson_test(
"LINESTRING(0 1,2 3,4 5)",
"{\"type\":\"LineString\",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}},\"coordinates\":[[0,1],[2,3],[4,5]]}",
"EPSG:4326", 0, 0);
/* Polygon */
do_geojson_test(
"POLYGON((0 1,2 3,4 5,0 1))",
"{\"type\":\"Polygon\",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}},\"coordinates\":[[[0,1],[2,3],[4,5],[0,1]]]}",
"EPSG:4326", 0, 0);
/* Polygon - with internal ring */
do_geojson_test(
"POLYGON((0 1,2 3,4 5,0 1),(6 7,8 9,10 11,6 7))",
"{\"type\":\"Polygon\",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}},\"coordinates\":[[[0,1],[2,3],[4,5],[0,1]],[[6,7],[8,9],[10,11],[6,7]]]}",
"EPSG:4326", 0, 0);
/* Multiline */
do_geojson_test(
"MULTILINESTRING((0 1,2 3,4 5),(6 7,8 9,10 11))",
"{\"type\":\"MultiLineString\",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}},\"coordinates\":[[[0,1],[2,3],[4,5]],[[6,7],[8,9],[10,11]]]}",
"EPSG:4326", 0, 0);
/* MultiPolygon */
do_geojson_test(
"MULTIPOLYGON(((0 1,2 3,4 5,0 1)),((6 7,8 9,10 11,6 7)))",
"{\"type\":\"MultiPolygon\",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}},\"coordinates\":[[[[0,1],[2,3],[4,5],[0,1]]],[[[6,7],[8,9],[10,11],[6,7]]]]}",
"EPSG:4326", 0, 0);
/* Empty GeometryCollection */
do_geojson_test(
"GEOMETRYCOLLECTION EMPTY",
"{\"type\":\"GeometryCollection\",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}},\"geometries\":[]}",
"EPSG:4326", 0, 0);
}
static void in_geojson_test_bbox(void)
{
/* Linestring */
do_geojson_test(
"LINESTRING(0 1,2 3,4 5)",
"{\"type\":\"LineString\",\"bbox\":[0,1,4,5],\"coordinates\":[[0,1],[2,3],[4,5]]}",
NULL, 0, 1);
/* Polygon */
do_geojson_test(
"POLYGON((0 1,2 3,4 5,0 1))",
"{\"type\":\"Polygon\",\"bbox\":[0,1,4,5],\"coordinates\":[[[0,1],[2,3],[4,5],[0,1]]]}",
NULL, 0, 1);
/* Polygon - with internal ring */
do_geojson_test(
"POLYGON((0 1,2 3,4 5,0 1),(6 7,8 9,10 11,6 7))",
"{\"type\":\"Polygon\",\"bbox\":[0,1,4,5],\"coordinates\":[[[0,1],[2,3],[4,5],[0,1]],[[6,7],[8,9],[10,11],[6,7]]]}",
NULL, 0, 1);
/* Multiline */
do_geojson_test(
"MULTILINESTRING((0 1,2 3,4 5),(6 7,8 9,10 11))",
"{\"type\":\"MultiLineString\",\"bbox\":[0,1,10,11],\"coordinates\":[[[0,1],[2,3],[4,5]],[[6,7],[8,9],[10,11]]]}",
NULL, 0, 1);
/* MultiPolygon */
do_geojson_test(
"MULTIPOLYGON(((0 1,2 3,4 5,0 1)),((6 7,8 9,10 11,6 7)))",
"{\"type\":\"MultiPolygon\",\"bbox\":[0,1,10,11],\"coordinates\":[[[[0,1],[2,3],[4,5],[0,1]]],[[[6,7],[8,9],[10,11],[6,7]]]]}",
NULL, 0, 1);
/* GeometryCollection */
do_geojson_test(
"GEOMETRYCOLLECTION(LINESTRING(0 1,-1 3),LINESTRING(2 3,4 5))",
"{\"type\":\"GeometryCollection\",\"bbox\":[-1,1,4,5],\"geometries\":[{\"type\":\"LineString\",\"coordinates\":[[0,1],[-1,3]]},{\"type\":\"LineString\",\"coordinates\":[[2,3],[4,5]]}]}",
NULL, 0, 1);
/* Empty GeometryCollection */
do_geojson_test(
"GEOMETRYCOLLECTION EMPTY",
"{\"type\":\"GeometryCollection\",\"geometries\":[]}",
NULL, 0, 1);
}
static void in_geojson_test_geoms(void)
{
/* Linestring */
do_geojson_test(
"LINESTRING(0 1,2 3,4 5)",
"{\"type\":\"LineString\",\"coordinates\":[[0,1],[2,3],[4,5]]}",
NULL, 0, 0);
/* Polygon */
do_geojson_test(
"POLYGON((0 1,2 3,4 5,0 1))",
"{\"type\":\"Polygon\",\"coordinates\":[[[0,1],[2,3],[4,5],[0,1]]]}",
NULL, 0, 0);
/* Polygon - with internal ring */
do_geojson_test(
"POLYGON((0 1,2 3,4 5,0 1),(6 7,8 9,10 11,6 7))",
"{\"type\":\"Polygon\",\"coordinates\":[[[0,1],[2,3],[4,5],[0,1]],[[6,7],[8,9],[10,11],[6,7]]]}",
NULL, 0, 0);
/* Multiline */
do_geojson_test(
"MULTILINESTRING((0 1,2 3,4 5),(6 7,8 9,10 11))",
"{\"type\":\"MultiLineString\",\"coordinates\":[[[0,1],[2,3],[4,5]],[[6,7],[8,9],[10,11]]]}",
NULL, 0, 0);
/* MultiPolygon */
do_geojson_test(
"MULTIPOLYGON(((0 1,2 3,4 5,0 1)),((6 7,8 9,10 11,6 7)))",
"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[0,1],[2,3],[4,5],[0,1]]],[[[6,7],[8,9],[10,11],[6,7]]]]}",
NULL, 0, 0);
/* GeometryCollection */
do_geojson_test(
"GEOMETRYCOLLECTION(POINT(0 1),LINESTRING(2 3,4 5))",
"{\"type\":\"GeometryCollection\",\"geometries\":[{\"type\":\"Point\",\"coordinates\":[0,1]},{\"type\":\"LineString\",\"coordinates\":[[2,3],[4,5]]}]}",
NULL, 0, 0);
/* Empty GeometryCollection */
do_geojson_test(
"GEOMETRYCOLLECTION EMPTY",
"{\"type\":\"GeometryCollection\",\"geometries\":[]}",
NULL, 0, 0);
}
/*
** Used by test harness to register the tests in this file.
*/
CU_TestInfo in_geojson_tests[] =
{
PG_TEST(in_geojson_test_srid),
PG_TEST(in_geojson_test_bbox),
PG_TEST(in_geojson_test_geoms),
CU_TEST_INFO_NULL
};
CU_SuiteInfo in_geojson_suite = {"in_geojson", NULL, NULL, in_geojson_tests};

View file

@ -40,6 +40,7 @@ extern CU_SuiteInfo geos_suite;
extern CU_SuiteInfo tree_suite;
extern CU_SuiteInfo triangulate_suite;
extern CU_SuiteInfo homogenize_suite;
extern CU_SuiteInfo in_geojson_suite;
extern CU_SuiteInfo stringbuffer_suite;
extern CU_SuiteInfo surface_suite;
extern CU_SuiteInfo out_gml_suite;
@ -79,6 +80,7 @@ int main(int argc, char *argv[])
stringbuffer_suite,
surface_suite,
homogenize_suite,
in_geojson_suite,
out_gml_suite,
out_kml_suite,
out_geojson_suite,

View file

@ -1349,6 +1349,17 @@ extern char* lwgeom_to_geojson(const LWGEOM *geo, char *srs, int precision, int
extern char* lwgeom_to_svg(const LWGEOM *geom, int precision, int relative);
extern char* lwgeom_to_x3d3(const LWGEOM *geom, char *srs, int precision, int opts, const char *defid);
/**
* Create an LWGEOM object from a GeoJSON representation
*
* @param geojson the GeoJSON input
* @param srs output parameter. Will be set to a newly allocated
* string holding the spatial reference string, or NULL
* if no such parameter is found in input.
* If not null, the pointer must be freed with lwfree.
*/
extern LWGEOM* lwgeom_from_geojson(const char *geojson, char **srs);
/**
* Initialize a spheroid object for use in geodetic functions.
*/

555
liblwgeom/lwin_geojson.c Normal file
View file

@ -0,0 +1,555 @@
/**********************************************************************
*
* PostGIS - Spatial Types for PostgreSQL
*
* Copyright 2013 Sandro Santilli <strk@keybit.net>
* Copyright 2011 Kashif Rasul <kashif.rasul@gmail.com>
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU General Public Licence. See the COPYING file.
*
**********************************************************************/
#include <assert.h>
#include "liblwgeom.h"
#include "lwgeom_log.h"
#include "../postgis_config.h"
#ifdef HAVE_LIBJSON
#include <json/json.h>
#include <json/json_object_private.h>
#include <string.h>
static void geojson_lwerror(char *msg, int error_code)
{
LWDEBUGF(3, "lwgeom_from_geojson ERROR %i", error_code);
lwerror("%s", msg);
}
/* Prototype */
static LWGEOM* parse_geojson(json_object *geojson, int *hasz, int root_srid);
static json_object*
findMemberByName(json_object* poObj, const char* pszName )
{
json_object* poTmp;
json_object_iter it;
poTmp = poObj;
if( NULL == pszName || NULL == poObj)
return NULL;
it.key = NULL;
it.val = NULL;
it.entry = NULL;
if( NULL != json_object_get_object(poTmp) )
{
assert( NULL != json_object_get_object(poTmp)->head );
for( it.entry = json_object_get_object(poTmp)->head;
( it.entry ?
( it.key = (char*)it.entry->k,
it.val = (json_object*)it.entry->v, it.entry) : 0);
it.entry = it.entry->next)
{
if( strcasecmp((char *)it.key, pszName )==0 )
return it.val;
}
}
return NULL;
}
static int
parse_geojson_coord(json_object *poObj, int *hasz, POINTARRAY *pa)
{
POINT4D pt;
int iType = 0;
LWDEBUGF(3, "parse_geojson_coord called for object %s.", json_object_to_json_string( poObj ) );
if( json_type_array == json_object_get_type( poObj ) )
{
json_object* poObjCoord = NULL;
const int nSize = json_object_array_length( poObj );
LWDEBUGF(3, "parse_geojson_coord called for array size %d.", nSize );
// Read X coordinate
poObjCoord = json_object_array_get_idx( poObj, 0 );
iType = json_object_get_type(poObjCoord);
if (iType == json_type_double)
pt.x = json_object_get_double( poObjCoord );
else
pt.x = json_object_get_int( poObjCoord );
LWDEBUGF(3, "parse_geojson_coord pt.x = %f.", pt.x );
// Read Y coordiante
poObjCoord = json_object_array_get_idx( poObj, 1 );
if (iType == json_type_double)
pt.y = json_object_get_double( poObjCoord );
else
pt.y = json_object_get_int( poObjCoord );
LWDEBUGF(3, "parse_geojson_coord pt.y = %f.", pt.y );
if( nSize == 3 ) /* should this be >= 3 ? */
{
// Read Z coordiante
poObjCoord = json_object_array_get_idx( poObj, 2 );
if (iType == 3)
pt.z = json_object_get_double( poObjCoord );
else
pt.z = json_object_get_int( poObjCoord );
LWDEBUGF(3, "parse_geojson_coord pt.z = %f.", pt.z );
*hasz = LW_TRUE;
}
else
{
*hasz = LW_FALSE;
/* Initialize Z coordinate, if required */
if ( FLAGS_GET_Z(pa->flags) ) pt.z = 0.0;
}
/* TODO: should we account for nSize > 3 ? */
/* Initialize M coordinate, if required */
if ( FLAGS_GET_M(pa->flags) ) pt.m = 0.0;
}
return ptarray_append_point(pa, &pt, LW_FALSE);
}
static LWGEOM*
parse_geojson_point(json_object *geojson, int *hasz, int root_srid)
{
LWGEOM *geom;
POINTARRAY *pa;
json_object* coords = NULL;
LWDEBUGF(3, "parse_geojson_point called with root_srid = %d.", root_srid );
coords = findMemberByName( geojson, "coordinates" );
if ( ! coords ) {
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
return NULL;
}
pa = ptarray_construct_empty(1, 0, 1);
parse_geojson_coord(coords, hasz, pa);
geom = (LWGEOM *) lwpoint_construct(root_srid, NULL, pa);
LWDEBUG(2, "parse_geojson_point finished.");
return geom;
}
static LWGEOM*
parse_geojson_linestring(json_object *geojson, int *hasz, int root_srid)
{
LWGEOM *geom;
POINTARRAY *pa;
json_object* points = NULL;
int i = 0;
LWDEBUG(2, "parse_geojson_linestring called.");
points = findMemberByName( geojson, "coordinates" );
if ( ! points ) {
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
return NULL;
}
pa = ptarray_construct_empty(1, 0, 1);
if( json_type_array == json_object_get_type( points ) )
{
const int nPoints = json_object_array_length( points );
for(i = 0; i < nPoints; ++i)
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, i );
parse_geojson_coord(coords, hasz, pa);
}
}
geom = (LWGEOM *) lwline_construct(root_srid, NULL, pa);
LWDEBUG(2, "parse_geojson_linestring finished.");
return geom;
}
static LWGEOM*
parse_geojson_polygon(json_object *geojson, int *hasz, int root_srid)
{
LWGEOM *geom;
POINTARRAY **ppa;
json_object* rings = NULL;
int i = 0, j = 0;
int ring = 0;
rings = findMemberByName( geojson, "coordinates" );
if ( ! rings ) {
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
return NULL;
}
ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
if( json_type_array == json_object_get_type( rings ) )
{
int nPoints;
json_object* points = NULL;
ppa[0] = ptarray_construct_empty(1, 0, 1);
ring = json_object_array_length( rings );
points = json_object_array_get_idx( rings, 0 );
nPoints = json_object_array_length( points );
for (i=0; i < nPoints; i++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, i );
parse_geojson_coord(coords, hasz, ppa[0]);
}
for(i = 1; i < ring; ++i)
{
int nPoints;
ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa, sizeof(POINTARRAY*) * (i + 1));
ppa[i] = ptarray_construct_empty(1, 0, 1);
points = json_object_array_get_idx( rings, i );
nPoints = json_object_array_length( points );
for (j=0; j < nPoints; j++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, j );
parse_geojson_coord(coords, hasz, ppa[i]);
}
}
}
geom = (LWGEOM *) lwpoly_construct(root_srid, NULL, ring, ppa);
return geom;
}
static LWGEOM*
parse_geojson_multipoint(json_object *geojson, int *hasz, int root_srid)
{
LWGEOM *geom;
int i = 0;
json_object* poObjPoints = NULL;
if (!root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOINTTYPE, root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOINTTYPE, -1, 1, 0);
}
poObjPoints = findMemberByName( geojson, "coordinates" );
if ( ! poObjPoints ) {
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
return NULL;
}
if( json_type_array == json_object_get_type( poObjPoints ) )
{
const int nPoints = json_object_array_length( poObjPoints );
for( i = 0; i < nPoints; ++i)
{
POINTARRAY *pa;
json_object* poObjCoords = NULL;
poObjCoords = json_object_array_get_idx( poObjPoints, i );
pa = ptarray_construct_empty(1, 0, 1);
parse_geojson_coord(poObjCoords, hasz, pa);
geom = (LWGEOM*)lwmpoint_add_lwpoint((LWMPOINT*)geom,
(LWPOINT*)lwpoint_construct(root_srid, NULL, pa));
}
}
return geom;
}
static LWGEOM*
parse_geojson_multilinestring(json_object *geojson, int *hasz, int root_srid)
{
LWGEOM *geom = NULL;
int i, j;
json_object* poObjLines = NULL;
if (!root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTILINETYPE, root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTILINETYPE, -1, 1, 0);
}
poObjLines = findMemberByName( geojson, "coordinates" );
if ( ! poObjLines ) {
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
return NULL;
}
if( json_type_array == json_object_get_type( poObjLines ) )
{
const int nLines = json_object_array_length( poObjLines );
for( i = 0; i < nLines; ++i)
{
POINTARRAY *pa = NULL;
json_object* poObjLine = NULL;
poObjLine = json_object_array_get_idx( poObjLines, i );
pa = ptarray_construct_empty(1, 0, 1);
if( json_type_array == json_object_get_type( poObjLine ) )
{
const int nPoints = json_object_array_length( poObjLine );
for(j = 0; j < nPoints; ++j)
{
json_object* coords = NULL;
coords = json_object_array_get_idx( poObjLine, j );
parse_geojson_coord(coords, hasz, pa);
}
geom = (LWGEOM*)lwmline_add_lwline((LWMLINE*)geom,
(LWLINE*)lwline_construct(root_srid, NULL, pa));
}
}
}
return geom;
}
static LWGEOM*
parse_geojson_multipolygon(json_object *geojson, int *hasz, int root_srid)
{
LWGEOM *geom = NULL;
int i, j, k;
json_object* poObjPolys = NULL;
if (!root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, -1, 1, 0);
}
poObjPolys = findMemberByName( geojson, "coordinates" );
if ( ! poObjPolys ) {
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
return NULL;
}
if( json_type_array == json_object_get_type( poObjPolys ) )
{
const int nPolys = json_object_array_length( poObjPolys );
for(i = 0; i < nPolys; ++i)
{
POINTARRAY **ppa;
json_object* poObjPoly = NULL;
poObjPoly = json_object_array_get_idx( poObjPolys, i );
ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
if( json_type_array == json_object_get_type( poObjPoly ) )
{
int nPoints;
json_object* points = NULL;
int ring = json_object_array_length( poObjPoly );
ppa[0] = ptarray_construct_empty(1, 0, 1);
points = json_object_array_get_idx( poObjPoly, 0 );
nPoints = json_object_array_length( points );
for (j=0; j < nPoints; j++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, j );
parse_geojson_coord(coords, hasz, ppa[0]);
}
for(j = 1; j < ring; ++j)
{
int nPoints;
ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa, sizeof(POINTARRAY*) * (j + 1));
ppa[j] = ptarray_construct_empty(1, 0, 1);
points = json_object_array_get_idx( poObjPoly, j );
nPoints = json_object_array_length( points );
for (k=0; k < nPoints; k++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, k );
parse_geojson_coord(coords, hasz, ppa[i]);
}
}
geom = (LWGEOM*)lwmpoly_add_lwpoly((LWMPOLY*)geom,
(LWPOLY*)lwpoly_construct(root_srid, NULL, ring, ppa));
}
}
}
return geom;
}
static LWGEOM*
parse_geojson_geometrycollection(json_object *geojson, int *hasz, int root_srid)
{
LWGEOM *geom = NULL;
int i;
json_object* poObjGeoms = NULL;
if (!root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, -1, 1, 0);
}
poObjGeoms = findMemberByName( geojson, "geometries" );
if ( ! poObjGeoms ) {
geojson_lwerror("Unable to find 'geometries' in GeoJSON string", 4);
return NULL;
}
if( json_type_array == json_object_get_type( poObjGeoms ) )
{
const int nGeoms = json_object_array_length( poObjGeoms );
json_object* poObjGeom = NULL;
for(i = 0; i < nGeoms; ++i )
{
poObjGeom = json_object_array_get_idx( poObjGeoms, i );
geom = (LWGEOM*)lwcollection_add_lwgeom((LWCOLLECTION *)geom,
parse_geojson(poObjGeom, hasz, root_srid));
}
}
return geom;
}
static LWGEOM*
parse_geojson(json_object *geojson, int *hasz, int root_srid)
{
json_object* type = NULL;
const char* name;
if( NULL == geojson ) {
geojson_lwerror("invalid GeoJSON representation", 2);
return NULL;
}
type = findMemberByName( geojson, "type" );
if( NULL == type ) {
geojson_lwerror("unknown GeoJSON type", 3);
return NULL;
}
name = json_object_get_string( type );
if( strcasecmp( name, "Point" )==0 )
return parse_geojson_point(geojson, hasz, root_srid);
if( strcasecmp( name, "LineString" )==0 )
return parse_geojson_linestring(geojson, hasz, root_srid);
if( strcasecmp( name, "Polygon" )==0 )
return parse_geojson_polygon(geojson, hasz, root_srid);
if( strcasecmp( name, "MultiPoint" )==0 )
return parse_geojson_multipoint(geojson, hasz, root_srid);
if( strcasecmp( name, "MultiLineString" )==0 )
return parse_geojson_multilinestring(geojson, hasz, root_srid);
if( strcasecmp( name, "MultiPolygon" )==0 )
return parse_geojson_multipolygon(geojson, hasz, root_srid);
if( strcasecmp( name, "GeometryCollection" )==0 )
return parse_geojson_geometrycollection(geojson, hasz, root_srid);
lwerror("invalid GeoJson representation");
return NULL; /* Never reach */
}
#endif /* HAVE_LIBJSON */
LWGEOM*
lwgeom_from_geojson(const char *geojson, char **srs)
{
#ifndef HAVE_LIBJSON
*srs = NULL;
lwerror("You need JSON-C for lwgeom_from_geojson");
return NULL;
#else /* HAVE_LIBJSON */
/* size_t geojson_size = strlen(geojson); */
LWGEOM *lwgeom;
int hasz=LW_TRUE;
json_tokener* jstok = NULL;
json_object* poObj = NULL;
json_object* poObjSrs = NULL;
*srs = NULL;
/* Begin to Parse json */
jstok = json_tokener_new();
poObj = json_tokener_parse_ex(jstok, geojson, -1);
if( jstok->err != json_tokener_success)
{
char err[256];
snprintf(err, 256, "%s (at offset %d)", json_tokener_errors[jstok->err], jstok->char_offset);
json_tokener_free(jstok);
json_object_put(poObj);
geojson_lwerror(err, 1);
return NULL;
}
json_tokener_free(jstok);
poObjSrs = findMemberByName( poObj, "crs" );
if (poObjSrs != NULL)
{
json_object* poObjSrsType = findMemberByName( poObjSrs, "type" );
if (poObjSrsType != NULL)
{
json_object* poObjSrsProps = findMemberByName( poObjSrs, "properties" );
json_object* poNameURL = findMemberByName( poObjSrsProps, "name" );
const char* pszName = json_object_get_string( poNameURL );
*srs = lwalloc(strlen(pszName) + 1);
strcpy(*srs, pszName);
}
}
lwgeom = parse_geojson(poObj, &hasz, 0);
json_object_put(poObj);
lwgeom_add_bbox(lwgeom);
if (!hasz)
{
LWGEOM *tmp = lwgeom_force_2d(lwgeom);
lwgeom_free(lwgeom);
lwgeom = tmp;
LWDEBUG(2, "geom_from_geojson called.");
}
return lwgeom;
#endif /* HAVE_LIBJSON */
}

View file

@ -15,464 +15,11 @@
#include "../postgis_config.h"
#include "lwgeom_pg.h"
#include "liblwgeom.h"
#include "lwgeom_export.h"
Datum geom_from_geojson(PG_FUNCTION_ARGS);
Datum postgis_libjson_version(PG_FUNCTION_ARGS);
static void geojson_lwerror(char *msg, int error_code)
{
POSTGIS_DEBUGF(3, "ST_GeomFromGeoJSON ERROR %i", error_code);
lwerror("%s", msg);
}
#ifdef HAVE_LIBJSON
#include "lwgeom_export.h"
#include <json/json.h>
#include <json/json_object_private.h>
/* Prototype */
LWGEOM* parse_geojson(json_object *geojson, bool *hasz, int *root_srid);
static json_object*
findMemberByName(json_object* poObj, const char* pszName )
{
json_object* poTmp;
json_object_iter it;
poTmp = poObj;
if( NULL == pszName || NULL == poObj)
return NULL;
it.key = NULL;
it.val = NULL;
it.entry = NULL;
if( NULL != json_object_get_object(poTmp) )
{
assert( NULL != json_object_get_object(poTmp)->head );
for( it.entry = json_object_get_object(poTmp)->head;
( it.entry ?
( it.key = (char*)it.entry->k,
it.val = (json_object*)it.entry->v, it.entry) : 0);
it.entry = it.entry->next)
{
if( strcasecmp((char *)it.key, pszName )==0 )
return it.val;
}
}
return NULL;
}
static int
parse_geojson_coord(json_object *poObj, bool *hasz, POINTARRAY *pa)
{
POINT4D pt;
int iType = 0;
POSTGIS_DEBUGF(3, "parse_geojson_coord called for object %s.", json_object_to_json_string( poObj ) );
if( json_type_array == json_object_get_type( poObj ) )
{
json_object* poObjCoord = NULL;
const int nSize = json_object_array_length( poObj );
POSTGIS_DEBUGF(3, "parse_geojson_coord called for array size %d.", nSize );
// Read X coordinate
poObjCoord = json_object_array_get_idx( poObj, 0 );
iType = json_object_get_type(poObjCoord);
if (iType == json_type_double)
pt.x = json_object_get_double( poObjCoord );
else
pt.x = json_object_get_int( poObjCoord );
POSTGIS_DEBUGF(3, "parse_geojson_coord pt.x = %f.", pt.x );
// Read Y coordiante
poObjCoord = json_object_array_get_idx( poObj, 1 );
if (iType == json_type_double)
pt.y = json_object_get_double( poObjCoord );
else
pt.y = json_object_get_int( poObjCoord );
POSTGIS_DEBUGF(3, "parse_geojson_coord pt.y = %f.", pt.y );
if( nSize == 3 ) /* should this be >= 3 ? */
{
// Read Z coordiante
poObjCoord = json_object_array_get_idx( poObj, 2 );
if (iType == 3)
pt.z = json_object_get_double( poObjCoord );
else
pt.z = json_object_get_int( poObjCoord );
POSTGIS_DEBUGF(3, "parse_geojson_coord pt.z = %f.", pt.z );
*hasz = true;
}
else
{
*hasz = false;
/* Initialize Z coordinate, if required */
if ( FLAGS_GET_Z(pa->flags) ) pt.z = 0.0;
}
/* TODO: should we account for nSize > 3 ? */
/* Initialize M coordinate, if required */
if ( FLAGS_GET_M(pa->flags) ) pt.m = 0.0;
}
return ptarray_append_point(pa, &pt, LW_FALSE);
}
static LWGEOM*
parse_geojson_point(json_object *geojson, bool *hasz, int *root_srid)
{
LWGEOM *geom;
POINTARRAY *pa;
json_object* coords = NULL;
POSTGIS_DEBUGF(3, "parse_geojson_point called with root_srid = %d.", *root_srid );
coords = findMemberByName( geojson, "coordinates" );
if ( ! coords )
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
pa = ptarray_construct_empty(1, 0, 1);
parse_geojson_coord(coords, hasz, pa);
geom = (LWGEOM *) lwpoint_construct(*root_srid, NULL, pa);
POSTGIS_DEBUG(2, "parse_geojson_point finished.");
return geom;
}
static LWGEOM*
parse_geojson_linestring(json_object *geojson, bool *hasz, int *root_srid)
{
LWGEOM *geom;
POINTARRAY *pa;
json_object* points = NULL;
int i = 0;
POSTGIS_DEBUG(2, "parse_geojson_linestring called.");
points = findMemberByName( geojson, "coordinates" );
if ( ! points )
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
pa = ptarray_construct_empty(1, 0, 1);
if( json_type_array == json_object_get_type( points ) )
{
const int nPoints = json_object_array_length( points );
for(i = 0; i < nPoints; ++i)
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, i );
parse_geojson_coord(coords, hasz, pa);
}
}
geom = (LWGEOM *) lwline_construct(*root_srid, NULL, pa);
POSTGIS_DEBUG(2, "parse_geojson_linestring finished.");
return geom;
}
static LWGEOM*
parse_geojson_polygon(json_object *geojson, bool *hasz, int *root_srid)
{
LWGEOM *geom;
POINTARRAY **ppa;
json_object* rings = NULL;
int i = 0, j = 0;
int ring = 0;
rings = findMemberByName( geojson, "coordinates" );
if ( ! rings )
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
if( json_type_array == json_object_get_type( rings ) )
{
int nPoints;
json_object* points = NULL;
ppa[0] = ptarray_construct_empty(1, 0, 1);
ring = json_object_array_length( rings );
points = json_object_array_get_idx( rings, 0 );
nPoints = json_object_array_length( points );
for (i=0; i < nPoints; i++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, i );
parse_geojson_coord(coords, hasz, ppa[0]);
}
for(i = 1; i < ring; ++i)
{
int nPoints;
ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa, sizeof(POINTARRAY*) * (i + 1));
ppa[i] = ptarray_construct_empty(1, 0, 1);
points = json_object_array_get_idx( rings, i );
nPoints = json_object_array_length( points );
for (j=0; j < nPoints; j++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, j );
parse_geojson_coord(coords, hasz, ppa[i]);
}
}
}
geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, ring, ppa);
return geom;
}
static LWGEOM*
parse_geojson_multipoint(json_object *geojson, bool *hasz, int *root_srid)
{
LWGEOM *geom;
int i = 0;
json_object* poObjPoints = NULL;
if (!*root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOINTTYPE, *root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOINTTYPE, -1, 1, 0);
}
poObjPoints = findMemberByName( geojson, "coordinates" );
if ( ! poObjPoints )
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
if( json_type_array == json_object_get_type( poObjPoints ) )
{
const int nPoints = json_object_array_length( poObjPoints );
for( i = 0; i < nPoints; ++i)
{
POINTARRAY *pa;
json_object* poObjCoords = NULL;
poObjCoords = json_object_array_get_idx( poObjPoints, i );
pa = ptarray_construct_empty(1, 0, 1);
parse_geojson_coord(poObjCoords, hasz, pa);
geom = (LWGEOM*)lwmpoint_add_lwpoint((LWMPOINT*)geom,
(LWPOINT*)lwpoint_construct(*root_srid, NULL, pa));
}
}
return geom;
}
static LWGEOM*
parse_geojson_multilinestring(json_object *geojson, bool *hasz, int *root_srid)
{
LWGEOM *geom = NULL;
int i, j;
json_object* poObjLines = NULL;
if (!*root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTILINETYPE, *root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTILINETYPE, -1, 1, 0);
}
poObjLines = findMemberByName( geojson, "coordinates" );
if ( ! poObjLines )
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
if( json_type_array == json_object_get_type( poObjLines ) )
{
const int nLines = json_object_array_length( poObjLines );
for( i = 0; i < nLines; ++i)
{
POINTARRAY *pa = NULL;
json_object* poObjLine = NULL;
poObjLine = json_object_array_get_idx( poObjLines, i );
pa = ptarray_construct_empty(1, 0, 1);
if( json_type_array == json_object_get_type( poObjLine ) )
{
const int nPoints = json_object_array_length( poObjLine );
for(j = 0; j < nPoints; ++j)
{
json_object* coords = NULL;
coords = json_object_array_get_idx( poObjLine, j );
parse_geojson_coord(coords, hasz, pa);
}
geom = (LWGEOM*)lwmline_add_lwline((LWMLINE*)geom,
(LWLINE*)lwline_construct(*root_srid, NULL, pa));
}
}
}
return geom;
}
static LWGEOM*
parse_geojson_multipolygon(json_object *geojson, bool *hasz, int *root_srid)
{
LWGEOM *geom = NULL;
int i, j, k;
json_object* poObjPolys = NULL;
if (!*root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, *root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, -1, 1, 0);
}
poObjPolys = findMemberByName( geojson, "coordinates" );
if ( ! poObjPolys )
geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4);
if( json_type_array == json_object_get_type( poObjPolys ) )
{
const int nPolys = json_object_array_length( poObjPolys );
for(i = 0; i < nPolys; ++i)
{
POINTARRAY **ppa;
json_object* poObjPoly = NULL;
poObjPoly = json_object_array_get_idx( poObjPolys, i );
ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
if( json_type_array == json_object_get_type( poObjPoly ) )
{
int nPoints;
json_object* points = NULL;
int ring = json_object_array_length( poObjPoly );
ppa[0] = ptarray_construct_empty(1, 0, 1);
points = json_object_array_get_idx( poObjPoly, 0 );
nPoints = json_object_array_length( points );
for (j=0; j < nPoints; j++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, j );
parse_geojson_coord(coords, hasz, ppa[0]);
}
for(j = 1; j < ring; ++j)
{
int nPoints;
ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa, sizeof(POINTARRAY*) * (j + 1));
ppa[j] = ptarray_construct_empty(1, 0, 1);
points = json_object_array_get_idx( poObjPoly, j );
nPoints = json_object_array_length( points );
for (k=0; k < nPoints; k++ )
{
json_object* coords = NULL;
coords = json_object_array_get_idx( points, k );
parse_geojson_coord(coords, hasz, ppa[i]);
}
}
geom = (LWGEOM*)lwmpoly_add_lwpoly((LWMPOLY*)geom,
(LWPOLY*)lwpoly_construct(*root_srid, NULL, ring, ppa));
}
}
}
return geom;
}
static LWGEOM*
parse_geojson_geometrycollection(json_object *geojson, bool *hasz, int *root_srid)
{
LWGEOM *geom = NULL;
int i;
json_object* poObjGeoms = NULL;
if (!*root_srid)
{
geom = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, *root_srid, 1, 0);
}
else
{
geom = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, -1, 1, 0);
}
poObjGeoms = findMemberByName( geojson, "geometries" );
if ( ! poObjGeoms )
geojson_lwerror("Unable to find 'geometries' in GeoJSON string", 4);
if( json_type_array == json_object_get_type( poObjGeoms ) )
{
const int nGeoms = json_object_array_length( poObjGeoms );
json_object* poObjGeom = NULL;
for(i = 0; i < nGeoms; ++i )
{
poObjGeom = json_object_array_get_idx( poObjGeoms, i );
geom = (LWGEOM*)lwcollection_add_lwgeom((LWCOLLECTION *)geom,
parse_geojson(poObjGeom, hasz, root_srid));
}
}
return geom;
}
LWGEOM*
parse_geojson(json_object *geojson, bool *hasz, int *root_srid)
{
json_object* type = NULL;
const char* name;
if( NULL == geojson )
geojson_lwerror("invalid GeoJSON representation", 2);
type = findMemberByName( geojson, "type" );
if( NULL == type )
geojson_lwerror("unknown GeoJSON type", 3);
name = json_object_get_string( type );
if( strcasecmp( name, "Point" )==0 )
return parse_geojson_point(geojson, hasz, root_srid);
if( strcasecmp( name, "LineString" )==0 )
return parse_geojson_linestring(geojson, hasz, root_srid);
if( strcasecmp( name, "Polygon" )==0 )
return parse_geojson_polygon(geojson, hasz, root_srid);
if( strcasecmp( name, "MultiPoint" )==0 )
return parse_geojson_multipoint(geojson, hasz, root_srid);
if( strcasecmp( name, "MultiLineString" )==0 )
return parse_geojson_multilinestring(geojson, hasz, root_srid);
if( strcasecmp( name, "MultiPolygon" )==0 )
return parse_geojson_multipolygon(geojson, hasz, root_srid);
if( strcasecmp( name, "GeometryCollection" )==0 )
return parse_geojson_geometrycollection(geojson, hasz, root_srid);
lwerror("invalid GeoJson representation");
return NULL; /* Never reach */
}
#endif /* HAVE_LIBJSON */
PG_FUNCTION_INFO_V1(postgis_libjson_version);
Datum postgis_libjson_version(PG_FUNCTION_ARGS)
{
@ -496,59 +43,25 @@ Datum geom_from_geojson(PG_FUNCTION_ARGS)
GSERIALIZED *geom;
LWGEOM *lwgeom;
text *geojson_input;
int geojson_size;
char *geojson;
int root_srid=0;
bool hasz=true;
json_tokener* jstok = NULL;
json_object* poObj = NULL;
json_object* poObjSrs = NULL;
char *srs = NULL;
/* Get the geojson stream */
if (PG_ARGISNULL(0)) PG_RETURN_NULL();
geojson_input = PG_GETARG_TEXT_P(0);
geojson = text2cstring(geojson_input);
geojson_size = VARSIZE(geojson_input) - VARHDRSZ;
/* Begin to Parse json */
jstok = json_tokener_new();
poObj = json_tokener_parse_ex(jstok, geojson, -1);
if( jstok->err != json_tokener_success)
{
char err[256];
snprintf(err, 256, "%s (at offset %d)", json_tokener_errors[jstok->err], jstok->char_offset);
json_tokener_free(jstok);
geojson_lwerror(err, 1);
}
json_tokener_free(jstok);
lwgeom = lwgeom_from_geojson(geojson, &srs);
if ( ! lwgeom ) {
/* Shouldn't get here */
elog(ERROR, "lwgeom_from_geojson returned NULL");
PG_RETURN_NULL();
}
poObjSrs = findMemberByName( poObj, "crs" );
if (poObjSrs != NULL)
{
json_object* poObjSrsType = findMemberByName( poObjSrs, "type" );
if (poObjSrsType != NULL)
{
json_object* poObjSrsProps = findMemberByName( poObjSrs, "properties" );
json_object* poNameURL = findMemberByName( poObjSrsProps, "name" );
const char* pszName = json_object_get_string( poNameURL );
root_srid = getSRIDbySRS(pszName);
POSTGIS_DEBUGF(3, "getSRIDbySRS returned root_srid = %d.", root_srid );
}
}
lwgeom = parse_geojson(poObj, &hasz, &root_srid);
lwgeom_add_bbox(lwgeom);
if (root_srid && lwgeom->srid == -1) lwgeom->srid = root_srid;
if (!hasz)
{
LWGEOM *tmp = lwgeom_force_2d(lwgeom);
lwgeom_free(lwgeom);
lwgeom = tmp;
POSTGIS_DEBUG(2, "geom_from_geojson called.");
}
if ( srs ) {
lwgeom_set_srid(lwgeom, getSRIDbySRS(srs));
lwfree(srs);
}
geom = geometry_serialize(lwgeom);
lwgeom_free(lwgeom);