From 51eb7b396992574fe29ecbe4e9ba3bd617364ce4 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Mon, 22 Feb 2010 19:53:22 +0000 Subject: [PATCH] First cut of new WKT output functions, need more unit tests. git-svn-id: http://svn.osgeo.org/postgis/trunk@5302 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/Makefile.in | 1 + liblwgeom/cunit/Makefile.in | 1 + liblwgeom/cunit/cu_tester.c | 7 + liblwgeom/cunit/cu_tester.h | 3 + liblwgeom/cunit/cu_wkt.c | 118 ++++++++ liblwgeom/cunit/cu_wkt.h | 31 +++ liblwgeom/libgeom.h | 9 + liblwgeom/liblwgeom.h | 6 + liblwgeom/lwout_wkt.c | 542 ++++++++++++++++++++++++++++++++++++ liblwgeom/stringbuffer.c | 44 ++- liblwgeom/stringbuffer.h | 1 + 11 files changed, 751 insertions(+), 12 deletions(-) create mode 100644 liblwgeom/cunit/cu_wkt.c create mode 100644 liblwgeom/cunit/cu_wkt.h create mode 100644 liblwgeom/lwout_wkt.c diff --git a/liblwgeom/Makefile.in b/liblwgeom/Makefile.in index bd4414118..904891063 100644 --- a/liblwgeom/Makefile.in +++ b/liblwgeom/Makefile.in @@ -37,6 +37,7 @@ SA_OBJS = \ lwcurvepoly.o \ lwmcurve.o \ lwmsurface.o \ + lwout_wkt.o \ lwutil.o \ lwhomogenize.o \ lwalgorithm.o \ diff --git a/liblwgeom/cunit/Makefile.in b/liblwgeom/cunit/Makefile.in index b9f6291ce..751e29ef4 100644 --- a/liblwgeom/cunit/Makefile.in +++ b/liblwgeom/cunit/Makefile.in @@ -18,6 +18,7 @@ CUNIT_CPPFLAGS=@CUNIT_CPPFLAGS@ -I.. OBJS= \ cu_algorithm.o \ + cu_wkt.o \ cu_geodetic.o \ cu_measures.o \ cu_libgeom.o \ diff --git a/liblwgeom/cunit/cu_tester.c b/liblwgeom/cunit/cu_tester.c index ae210d244..f15727880 100644 --- a/liblwgeom/cunit/cu_tester.c +++ b/liblwgeom/cunit/cu_tester.c @@ -85,6 +85,13 @@ int main() return CU_get_error(); } + /* Add the wkt suite to the registry */ + if (NULL == register_wkt_suite()) + { + CU_cleanup_registry(); + return CU_get_error(); + } + /* Add the libgeom suite to the registry */ if (NULL == register_libgeom_suite()) { diff --git a/liblwgeom/cunit/cu_tester.h b/liblwgeom/cunit/cu_tester.h index d8fcc7bd8..7b42a9520 100644 --- a/liblwgeom/cunit/cu_tester.h +++ b/liblwgeom/cunit/cu_tester.h @@ -6,6 +6,7 @@ CU_pSuite register_measures_suite(void); CU_pSuite register_geodetic_suite(void); CU_pSuite register_libgeom_suite(void); CU_pSuite register_cg_suite(void); +CU_pSuite register_wkt_suite(void); CU_pSuite register_homogenize_suite(void); CU_pSuite register_out_gml_suite(void); CU_pSuite register_out_kml_suite(void); @@ -16,6 +17,7 @@ int init_measures_suite(void); int init_geodetic_suite(void); int init_libgeom_suite(void); int init_cg_suite(void); +int init_wkt_suite(void); int init_homogenize_suite(void); int init_out_gml_suite(void); int init_out_kml_suite(void); @@ -26,6 +28,7 @@ int clean_measures_suite(void); int clean_geodetic_suite(void); int clean_libgeom_suite(void); int clean_cg_suite(void); +int clean_wkt_suite(void); int clean_homogenize_suite(void); int clean_out_gml_suite(void); int clean_out_kml_suite(void); diff --git a/liblwgeom/cunit/cu_wkt.c b/liblwgeom/cunit/cu_wkt.c new file mode 100644 index 000000000..f2a4bc482 --- /dev/null +++ b/liblwgeom/cunit/cu_wkt.c @@ -0,0 +1,118 @@ +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2010 Olivier Courtin + * + * 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 "cu_wkt.h" + +/* +** Called from test harness to register the tests in this file. +*/ +CU_pSuite register_wkt_suite(void) +{ + CU_pSuite pSuite; + pSuite = CU_add_suite("WKT Suite", init_wkt_suite, clean_wkt_suite); + if (NULL == pSuite) + { + CU_cleanup_registry(); + return NULL; + } + + if ( + (NULL == CU_add_test(pSuite, "test_wkt_point()", test_wkt_point)) || + (NULL == CU_add_test(pSuite, "test_wkt_linestring()", test_wkt_linestring)) || + (NULL == CU_add_test(pSuite, "test_wkt_polygon()", test_wkt_polygon)) || + (NULL == CU_add_test(pSuite, "test_wkt_multipoint()", test_wkt_multipoint)) || + (NULL == CU_add_test(pSuite, "test_wkt_multilinestring()", test_wkt_multilinestring)) || + (NULL == CU_add_test(pSuite, "test_wkt_multipolygon()", test_wkt_multipolygon)) || + (NULL == CU_add_test(pSuite, "test_wkt_collection()", test_wkt_collection)) + ) + { + CU_cleanup_registry(); + return NULL; + } + return pSuite; +} + +/* +** The suite initialization function. +** Create any re-used objects. +*/ +int init_wkt_suite(void) +{ + return 0; +} + +/* +** The suite cleanup function. +** Frees any global objects. +*/ +int clean_wkt_suite(void) +{ + return 0; +} + +void test_wkt_point(void) +{ + LWGEOM *g; + char *s; + + g = lwgeom_from_ewkt("POINT(0 0 0 0)", PARSER_CHECK_NONE); + s = lwgeom_to_wkt(g, 14, WKT_ISO); + CU_ASSERT_STRING_EQUAL(s, "POINTZM(0 0 0 0)"); + lwfree(s); + + s = lwgeom_to_wkt(g, 14, WKT_EXTENDED); + CU_ASSERT_STRING_EQUAL(s, "POINT(0 0 0 0)"); + lwfree(s); + + s = lwgeom_to_wkt(g, 14, WKT_SFSQL); + CU_ASSERT_STRING_EQUAL(s, "POINT(0 0)"); + lwfree(s); + lwgeom_free(g); + + g = lwgeom_from_ewkt("POINTM(0 0 0)", PARSER_CHECK_NONE); + s = lwgeom_to_wkt(g, 14, WKT_ISO); + CU_ASSERT_STRING_EQUAL(s, "POINTM(0 0 0)"); + lwfree(s); + + s = lwgeom_to_wkt(g, 14, WKT_EXTENDED); + CU_ASSERT_STRING_EQUAL(s, "POINTM(0 0 0)"); + lwfree(s); + + s = lwgeom_to_wkt(g, 14, WKT_SFSQL); + CU_ASSERT_STRING_EQUAL(s, "POINT(0 0)"); + lwfree(s); + lwgeom_free(g); + +} + +void test_wkt_linestring(void) +{ + +} + +void test_wkt_polygon(void) +{ + +} +void test_wkt_multipoint(void) +{ + +} + +void test_wkt_multilinestring(void) +{ + +} + +void test_wkt_multipolygon(void) {} +void test_wkt_collection(void) {} + diff --git a/liblwgeom/cunit/cu_wkt.h b/liblwgeom/cunit/cu_wkt.h new file mode 100644 index 000000000..cfaaaa56b --- /dev/null +++ b/liblwgeom/cunit/cu_wkt.h @@ -0,0 +1,31 @@ +/********************************************************************** + * $Id:$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2010 Olivier Courtin + * + * 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 +#include +#include +#include "CUnit/Basic.h" + +#include "libgeom.h" +#include "cu_tester.h" + +/**********************************************************************/ + + +/* Test functions */ +void test_wkt_point(void); +void test_wkt_linestring(void); +void test_wkt_polygon(void); +void test_wkt_multipoint(void); +void test_wkt_multilinestring(void); +void test_wkt_multipolygon(void); +void test_wkt_collection(void); \ No newline at end of file diff --git a/liblwgeom/libgeom.h b/liblwgeom/libgeom.h index 4842254a0..f78f02935 100644 --- a/liblwgeom/libgeom.h +++ b/liblwgeom/libgeom.h @@ -35,6 +35,15 @@ #define LW_TRUE 1 #define LW_FALSE 0 +/** +* WKT Output Variant Types +*/ +#define WKT_ISO 0x01 +#define WKT_SFSQL 0x02 +#define WKT_EXTENDED 0x04 +#define WKT_NOTYPE 0x08 +#define WKT_NOPARENS 0x10 + /** * Maximum allowed SRID value. * Currently we are using 20 bits (1048575) of storage for SRID. diff --git a/liblwgeom/liblwgeom.h b/liblwgeom/liblwgeom.h index 69bc0370e..4c457a2ef 100644 --- a/liblwgeom/liblwgeom.h +++ b/liblwgeom/liblwgeom.h @@ -1423,6 +1423,12 @@ extern LWGEOM* lwpoly_remove_repeated_points(LWPOLY *in); extern uchar parse_hex(char *str); extern void deparse_hex(uchar str, char *result); +/* +** New parsing and unparsing functions. +*/ +extern char *lwgeom_to_wkt(const LWGEOM *geom, int precision, uchar variant); + + /* Parser check flags */ #define PARSER_CHECK_MINPOINTS 1 #define PARSER_CHECK_ODD 2 diff --git a/liblwgeom/lwout_wkt.c b/liblwgeom/lwout_wkt.c new file mode 100644 index 000000000..158a21621 --- /dev/null +++ b/liblwgeom/lwout_wkt.c @@ -0,0 +1,542 @@ +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * Copyright 2009 Paul Ramsey + * + * 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 "libgeom.h" + +static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uchar variant); + + +/** +* ISO format uses both Z and M qualifiers. +* Extended format only uses an M qualifier for 3DM variants, where it is not +* clear what the third dimension represents. +* SFSQL format never has more than two dimensions, so no qualifiers. +*/ +static void dimension_qualifiers_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, uchar variant) +{ + + /* Extended WKT: POINTM(0 0 0) */ + if( (variant & WKT_EXTENDED) && TYPE_HASM(geom->type) && (!TYPE_HASZ(geom->type)) ) + { + stringbuffer_append(sb, "M"); /* "M" */ + return; + } + + /* ISO WKT: POINT ZM (0 0 0 0) */ + if( (variant & WKT_ISO) && (TYPE_NDIMS(geom->type) > 2) ) + { + if( TYPE_HASZ(geom->type) ) + stringbuffer_append(sb, "Z"); + if( TYPE_HASM(geom->type) ) + stringbuffer_append(sb, "M"); + } +} + +/** +* Point array is a list of coordinates. Depending on output mode, +* we may suppress some dimensions. ISO and Extended formats include +* all dimensions. Standard OGC output only includes X/Y coordinates. +*/ +static void ptarray_to_wkt_sb(const POINTARRAY *ptarray, stringbuffer_t *sb, int precision, uchar variant) +{ + /* OGC only includes X/Y */ + int dimensions = 2; + int i, j; + + /* ISO and extended formats include all dimensions */ + if( variant & ( WKT_ISO | WKT_EXTENDED ) ) + dimensions = TYPE_NDIMS(ptarray->dims); + + /* Opening paren? */ + if( ! (variant & WKT_NOPARENS) ) + stringbuffer_append(sb, "("); + + /* Digits and commas */ + for(i = 0; i < ptarray->npoints; i++) + { + uchar *p = getPoint_internal(ptarray, i); + double d; + + /* Commas before ever coord but the first */ + if( i > 0 ) + stringbuffer_append(sb, ","); + + for(j = 0; j < dimensions; j++) + { + memcpy(&d, p + j * sizeof(double), sizeof(double)); + /* Spaces before every ordinate but the first */ + if( j > 0 ) + stringbuffer_append(sb, " "); + stringbuffer_vasbappend(sb, "%.*g", precision, d); + } + } + + /* Closing paren? */ + if( ! (variant & WKT_NOPARENS) ) + stringbuffer_append(sb, ")"); +} + +/** +* A four-dimensional point will have different outputs depending on variant. +* ISO: POINT ZM (0 0 0 0) +* Extended: POINT(0 0 0 0) +* OGC: POINT(0 0) +* A three-dimensional m-point will have different outputs too. +* ISO: POINT M (0 0 0) +* Extended: POINTM(0 0 0) +* OGC: POINT(0 0) +*/ +static void lwpoint_to_wkt_sb(const LWPOINT *pt, stringbuffer_t *sb, int precision, uchar variant) +{ + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "POINT"); /* "POINT" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)pt, sb, variant); + } + + if( pt->point->npoints < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + + ptarray_to_wkt_sb(pt->point, sb, precision, variant); +} + +/** +* LINESTRING(0 0 0, 1 1 1) +*/ +static void lwline_to_wkt_sb(const LWLINE *line, stringbuffer_t *sb, int precision, uchar variant) +{ + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "LINESTRING"); /* "LINESTRING" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)line, sb, variant); + } + if( line->points->npoints < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + + ptarray_to_wkt_sb(line->points, sb, precision, variant); +} + +/** +* POLYGON(0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1) +*/ +static void lwpoly_to_wkt_sb(const LWPOLY *poly, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "POLYGON"); /* "POLYGON" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)poly, sb, variant); + } + if( poly->nrings < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + + stringbuffer_append(sb, "("); + for( i = 0; i < poly->nrings; i++ ) + { + if( i > 0 ) + stringbuffer_append(sb, ","); + ptarray_to_wkt_sb(poly->rings[i], sb, precision, variant); + } + stringbuffer_append(sb, ")"); +} + +/** +* CIRCULARSTRING +*/ +static void lwcircstring_to_wkt_sb(const LWCIRCSTRING *circ, stringbuffer_t *sb, int precision, uchar variant) +{ + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "CIRCULARSTRING"); /* "CIRCULARSTRING" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)circ, sb, variant); + } + if( circ->points->npoints < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + ptarray_to_wkt_sb(circ->points, sb, precision, variant); +} + + +/** +* Multi-points do not wrap their sub-members in parens, unlike other multi-geometries. +* MULTPOINT(0 0, 1 1) instead of MULTIPOINT((0 0),(1 1)) +*/ +static void lwmpoint_to_wkt_sb(const LWMPOINT *mpoint, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "MULTIPOINT"); /* "MULTIPOINT" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoint, sb, variant); + } + if( mpoint->ngeoms < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + stringbuffer_append(sb, "("); + for( i = 0; i < mpoint->ngeoms; i++ ) + { + if( i > 0 ) + stringbuffer_append(sb, ","); + /* We don't want type strings or parens on our subgeoms */ + lwpoint_to_wkt_sb(mpoint->geoms[i], sb, precision, variant | WKT_NOPARENS | WKT_NOTYPE ); + } + stringbuffer_append(sb, ")"); +} + +/** +* MULTILINESTRING +*/ +static void lwmline_to_wkt_sb(const LWMLINE *mline, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "MULTILINESTRING"); /* "MULTILINESTRING" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)mline, sb, variant); + } + if( mline->ngeoms < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + + stringbuffer_append(sb, "("); + for( i = 0; i < mline->ngeoms; i++ ) + { + if( i > 0 ) + stringbuffer_append(sb, ","); + /* We don't want type strings on our subgeoms */ + lwline_to_wkt_sb(mline->geoms[i], sb, precision, variant | WKT_NOTYPE ); + } + stringbuffer_append(sb, ")"); +} + +/** +* MULTIPOLYGON +*/ +static void lwmpoly_to_wkt_sb(const LWMPOLY *mpoly, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "MULTIPOLYGON"); /* "MULTIPOLYGON" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoly, sb, variant); + } + if( mpoly->ngeoms < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + + stringbuffer_append(sb, "("); + for( i = 0; i < mpoly->ngeoms; i++ ) + { + if( i > 0 ) + stringbuffer_append(sb, ","); + /* We don't want type strings on our subgeoms */ + lwpoly_to_wkt_sb(mpoly->geoms[i], sb, precision, variant | WKT_NOTYPE ); + } + stringbuffer_append(sb, ")"); +} + +/** +* Compound curves provide type information for their curved sub-geometries +* but not their linestring sub-geometries. +* COMPOUNDCURVE((0 0, 1 1), CURVESTRING(1 1, 2 2, 3 3)) +*/ +static void lwcompound_to_wkt_sb(const LWCOMPOUND *comp, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "COMPOUNDCURVE"); /* "COMPOUNDCURVE" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)comp, sb, variant); + } + if( comp->ngeoms < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + + stringbuffer_append(sb, "("); + for( i = 0; i < comp->ngeoms; i++ ) + { + int type = TYPE_GETTYPE(comp->geoms[i]->type); + if( i > 0 ) + stringbuffer_append(sb, ","); + /* Linestring subgeoms don't get type identifiers */ + if( type == LINETYPE ) + { + lwline_to_wkt_sb((LWLINE*)comp->geoms[i], sb, precision, variant | WKT_NOTYPE ); + } + /* But circstring subgeoms *do* get type identifiers */ + else if( type == CIRCSTRINGTYPE ) + { + lwcircstring_to_wkt_sb((LWCIRCSTRING*)comp->geoms[i], sb, precision, variant ); + } + else + { + lwerror("lwcompound_to_wkt_size: Unknown type recieved %d", type); + } + } + stringbuffer_append(sb, ")"); +} + +/** +* Curve polygons provide type information for their curved rings +* but not their linestring rings. +* CURVEPOLYGON((0 0, 1 1, 0 1, 0 0), CURVESTRING(0 0, 1 1, 0 1, 0.5 1, 0 0)) +*/ +static void lwcurvepoly_to_wkt_sb(const LWCURVEPOLY *cpoly, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "CURVEPOLYGON"); /* "CURVEPOLYGON" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)cpoly, sb, variant); + } + if( cpoly->nrings < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + stringbuffer_append(sb, "("); + for( i = 0; i < cpoly->nrings; i++ ) + { + int type = TYPE_GETTYPE(cpoly->rings[i]->type); + if( i > 0 ) + stringbuffer_append(sb, ","); + switch(type) + { + case LINETYPE: + /* Linestring subgeoms don't get type identifiers */ + lwline_to_wkt_sb((LWLINE*)cpoly->rings[i], sb, precision, variant | WKT_NOTYPE ); + break; + case CIRCSTRINGTYPE: + /* But circstring subgeoms *do* get type identifiers */ + lwcircstring_to_wkt_sb((LWCIRCSTRING*)cpoly->rings[i], sb, precision, variant ); + break; + case COMPOUNDTYPE: + /* And compoundcurve subgeoms *do* get type identifiers */ + lwcompound_to_wkt_sb((LWCOMPOUND*)cpoly->rings[i], sb, precision, variant ); + break; + default: + lwerror("lwcurvepoly_to_wkt_size: Unknown type recieved %d", type); + } + } + stringbuffer_append(sb, ")"); +} + + +/** +* Multi-curves provide type information for their curved sub-geometries +* but not their linear sub-geometries. +* MULTICURVE((0 0, 1 1), CURVESTRING(0 0, 1 1, 2 2)) +*/ +static void lwmcurve_to_wkt_sb(const LWMCURVE *mcurv, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "MULTICURVE"); /* "MULTICURVE" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)mcurv, sb, variant); + } + if( mcurv->ngeoms < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + stringbuffer_append(sb, "("); + for( i = 0; i < mcurv->ngeoms; i++ ) + { + int type = TYPE_GETTYPE(mcurv->geoms[i]->type); + if( i > 0 ) + stringbuffer_append(sb, ","); + switch(type) + { + case LINETYPE: + /* Linestring subgeoms don't get type identifiers */ + lwline_to_wkt_sb((LWLINE*)mcurv->geoms[i], sb, precision, variant | WKT_NOTYPE ); + break; + case CIRCSTRINGTYPE: + /* But circstring subgeoms *do* get type identifiers */ + lwcircstring_to_wkt_sb((LWCIRCSTRING*)mcurv->geoms[i], sb, precision, variant ); + break; + case COMPOUNDTYPE: + /* And compoundcurve subgeoms *do* get type identifiers */ + lwcompound_to_wkt_sb((LWCOMPOUND*)mcurv->geoms[i], sb, precision, variant ); + break; + default: + lwerror("lwmcurve_to_wkt_size: Unknown type recieved %d", type); + } + } + stringbuffer_append(sb, ")"); +} + + +/** +* Multi-surfaces provide type information for their curved sub-geometries +* but not their linear sub-geometries. +* MULTISURFACE(((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0))) +*/ +static void lwmsurface_to_wkt_sb(const LWMSURFACE *msurf, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "MULTISURFACE"); /* "MULTISURFACE" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)msurf, sb, variant); + } + if( msurf->ngeoms < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + stringbuffer_append(sb, "("); + for( i = 0; i < msurf->ngeoms; i++ ) + { + int type = TYPE_GETTYPE(msurf->geoms[i]->type); + if( i > 0 ) + stringbuffer_append(sb, ","); + switch(type) + { + case POLYGONTYPE: + /* Linestring subgeoms don't get type identifiers */ + lwpoly_to_wkt_sb((LWPOLY*)msurf->geoms[i], sb, precision, variant | WKT_NOTYPE ); + break; + case CURVEPOLYTYPE: + /* But circstring subgeoms *do* get type identifiers */ + lwcurvepoly_to_wkt_sb((LWCURVEPOLY*)msurf->geoms[i], sb, precision, variant ); + break; + default: + lwerror("lwmsurface_to_wkt_size: Unknown type recieved %d", type); + } + } + stringbuffer_append(sb, ")"); +} + +/** +* Geometry collections provide type information for all their curved sub-geometries +* but not their linear sub-geometries. +* GEOMETRYCOLLECTION(POLYGON((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0))) +*/ +static void lwcollection_to_wkt_sb(const LWCOLLECTION *collection, stringbuffer_t *sb, int precision, uchar variant) +{ + int i = 0; + + if( ! (variant & WKT_NOTYPE) ) + { + stringbuffer_append(sb, "GEOMETRYCOLLECTION"); /* "GEOMETRYCOLLECTION" */ + dimension_qualifiers_to_wkt_sb((LWGEOM*)collection, sb, variant); + } + if( collection->ngeoms < 1 ) + { + stringbuffer_append(sb, " EMPTY"); /* "EMPTY" */ + return; + } + stringbuffer_append(sb, "("); + for( i = 0; i < collection->ngeoms; i++ ) + { + if( i > 0 ) + stringbuffer_append(sb, ","); + lwgeom_to_wkt_sb((LWGEOM*)collection->geoms[i], sb, precision, variant ); + } + stringbuffer_append(sb, ")"); +} + +/** +* Generic GEOMETRY +*/ +static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uchar variant) +{ + switch(TYPE_GETTYPE(geom->type)) + { + case POINTTYPE: + lwpoint_to_wkt_sb((LWPOINT*)geom, sb, precision, variant); + break; + case LINETYPE: + lwline_to_wkt_sb((LWLINE*)geom, sb, precision, variant); + break; + case POLYGONTYPE: + lwpoly_to_wkt_sb((LWPOLY*)geom, sb, precision, variant); + break; + case MULTIPOINTTYPE: + lwmpoint_to_wkt_sb((LWMPOINT*)geom, sb, precision, variant); + break; + case MULTILINETYPE: + lwmline_to_wkt_sb((LWMLINE*)geom, sb, precision, variant); + break; + case MULTIPOLYGONTYPE: + lwmpoly_to_wkt_sb((LWMPOLY*)geom, sb, precision, variant); + break; + case COLLECTIONTYPE: + lwcollection_to_wkt_sb((LWCOLLECTION*)geom, sb, precision, variant); + break; + case CIRCSTRINGTYPE: + lwcircstring_to_wkt_sb((LWCIRCSTRING*)geom, sb, precision, variant); + break; + case COMPOUNDTYPE: + lwcompound_to_wkt_sb((LWCOMPOUND*)geom, sb, precision, variant); + break; + case CURVEPOLYTYPE: + lwcurvepoly_to_wkt_sb((LWCURVEPOLY*)geom, sb, precision, variant); + break; + case MULTICURVETYPE: + lwmcurve_to_wkt_sb((LWMCURVE*)geom, sb, precision, variant); + break; + case MULTISURFACETYPE: + lwmsurface_to_wkt_sb((LWMSURFACE*)geom, sb, precision, variant); + break; + default: + lwerror("lwgeom_to_wkt_sb: Type %d unsupported.", TYPE_GETTYPE(geom->type)); + } +} + +/** +* Public WKT emitter function +*/ +char *lwgeom_to_wkt(const LWGEOM *geom, int precision, uchar variant) +{ + stringbuffer_t *sb; + char *str = NULL; + if( geom == NULL ) + return NULL; + sb = stringbuffer_create(); + lwgeom_to_wkt_sb(geom, sb, precision, variant); + if( stringbuffer_getstring(sb) == NULL ) + { + lwerror("Uh oh"); + return NULL; + } + str = strdup(stringbuffer_getstring(sb)); + stringbuffer_destroy(sb); + return str; +} + diff --git a/liblwgeom/stringbuffer.c b/liblwgeom/stringbuffer.c index 25c3a9931..f0cd85478 100644 --- a/liblwgeom/stringbuffer.c +++ b/liblwgeom/stringbuffer.c @@ -30,7 +30,7 @@ * Therefore the raw string routines will always assume +1 when given length. */ -#include "stringbuffer.h" +#include "liblwgeom.h" /* * * * * * * * * * * * * * raw string routines. * @@ -40,7 +40,7 @@ static char *allocate_string(int len) { char *s; - s = malloc(sizeof(char) * len + 1); /* add for null termination. */ + s = lwalloc(sizeof(char) * len + 1); /* add for null termination. */ s[len] = 0; return s; } @@ -49,7 +49,7 @@ static char *allocate_string(int len) static char *extend_string(char *str, int cur_len, int ex_len) { - str = realloc(str, (cur_len * sizeof(char)) + (ex_len * sizeof(char)) + (1 * sizeof(char))); + str = lwrealloc(str, (cur_len * sizeof(char)) + (ex_len * sizeof(char)) + (1 * sizeof(char))); str[cur_len] = 0; /* make sure it's null terminated. */ return str; @@ -124,7 +124,7 @@ stringbuffer_t *stringbuffer_create(void) { stringbuffer_t *sb; - sb = malloc(sizeof(stringbuffer_t)); + sb = lwalloc(sizeof(stringbuffer_t)); sb->len = 0; sb->capacity = 0; sb->buf = allocate_string(0); @@ -135,8 +135,8 @@ stringbuffer_t *stringbuffer_create(void) /* destroy the stringbuffer */ void stringbuffer_destroy(stringbuffer_t *sb) { - free(sb->buf); - free(sb); + lwfree(sb->buf); + lwfree(sb); } /* clear a string. */ @@ -228,7 +228,7 @@ stringbuffer_t *stringbuffer_trim_whitespace(stringbuffer_t *sb) newbuf[new_len] = 0; /* free up old. */ - free(sb->buf); + lwfree(sb->buf); /* set new. */ sb->buf = newbuf; @@ -409,7 +409,7 @@ void stringbuffer_align(stringbuffer_t *sb, int begin, int end) stringbuffer_append(aligned_string, word_string); stringbuffer_append(aligned_string, "\n"); - free(word_string); + lwfree(word_string); } @@ -430,13 +430,13 @@ void stringbuffer_avprintf_align(stringbuffer_t *sb, int start, int end, const c /* our first malloc is bogus. */ len = 1; - str = malloc(sizeof(char) * len); + str = lwalloc(sizeof(char) * len); total = vsnprintf(str, len, fmt, ap); /* total is the real length needed. */ - free(str); + lwfree(str); len = total + 1; - str = malloc(sizeof(char) * len); + str = lwalloc(sizeof(char) * len); vsnprintf(str, len, fmt, ap); /* now align if we want to align. */ @@ -457,7 +457,7 @@ void stringbuffer_avprintf_align(stringbuffer_t *sb, int start, int end, const c stringbuffer_append(sb, str); } - free(str); + lwfree(str); return; } @@ -566,3 +566,23 @@ int stringbuffer_getlen(stringbuffer_t *sb) { return sb->len; } + +void stringbuffer_vasbappend(stringbuffer_t *sb, char *fmt, ... ) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + + if (!lw_vasprintf (&msg, fmt, ap)) + { + va_end (ap); + return; + } + + /* Append to the stringbuffer */ + stringbuffer_append(sb, msg); + lwfree(msg); + + va_end(ap); +} \ No newline at end of file diff --git a/liblwgeom/stringbuffer.h b/liblwgeom/stringbuffer.h index 15d64088f..20959e005 100644 --- a/liblwgeom/stringbuffer.h +++ b/liblwgeom/stringbuffer.h @@ -66,3 +66,4 @@ extern void stringbuffer_avprintf(stringbuffer_t *sb, const char *fmt, va_list a extern int stringbuffer_marknewlines(stringbuffer_t *sb); extern const char *stringbuffer_getnextline(stringbuffer_t *sb, const char *cptr); extern int stringbuffer_getlen(stringbuffer_t *sb); +extern void stringbuffer_vasbappend(stringbuffer_t *sb, char *fmt, ... ); -- 2.40.0