2010-02-22 19:53:22 +00:00
|
|
|
/**********************************************************************
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* PostGIS - Spatial Types for PostgreSQL
|
|
|
|
* Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
|
|
|
|
*
|
|
|
|
* This is free software; you can redistribute and/or modify it under
|
|
|
|
* the terms of the GNU General Public Licence. See the COPYING file.
|
|
|
|
*
|
|
|
|
**********************************************************************/
|
|
|
|
|
2010-10-31 02:31:34 +00:00
|
|
|
#include "liblwgeom_internal.h"
|
2010-02-22 19:53:22 +00:00
|
|
|
|
|
|
|
static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uchar variant);
|
|
|
|
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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) */
|
2010-12-07 21:08:05 +00:00
|
|
|
if ( (variant & WKT_EXTENDED) && ! (variant & WKT_IS_CHILD) && FLAGS_GET_M(geom->flags) && (!FLAGS_GET_Z(geom->flags)) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "M"); /* "M" */
|
|
|
|
return;
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
|
2010-02-22 19:53:22 +00:00
|
|
|
/* ISO WKT: POINT ZM (0 0 0 0) */
|
2010-11-21 19:02:23 +00:00
|
|
|
if ( (variant & WKT_ISO) && (FLAGS_NDIMS(geom->flags) > 2) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-02-24 14:40:17 +00:00
|
|
|
stringbuffer_append(sb, " ");
|
2010-11-21 19:02:23 +00:00
|
|
|
if ( FLAGS_GET_Z(geom->flags) )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, "Z");
|
2010-11-21 19:02:23 +00:00
|
|
|
if ( FLAGS_GET_M(geom->flags) )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, "M");
|
2010-02-24 14:40:17 +00:00
|
|
|
stringbuffer_append(sb, " ");
|
2010-08-15 08:30:08 +00:00
|
|
|
}
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
|
2010-10-22 23:27:13 +00:00
|
|
|
/*
|
|
|
|
* Write an empty token out, padding with a space if
|
|
|
|
* necessary.
|
|
|
|
*/
|
|
|
|
static void empty_to_wkt_sb(stringbuffer_t *sb)
|
|
|
|
{
|
|
|
|
if ( stringbuffer_lastchar(sb) != ' ' ) /* "EMPTY" */
|
|
|
|
{
|
|
|
|
stringbuffer_append(sb, " ");
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, "EMPTY");
|
|
|
|
}
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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;
|
2010-08-15 08:30:08 +00:00
|
|
|
|
2010-02-22 19:53:22 +00:00
|
|
|
/* ISO and extended formats include all dimensions */
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( variant & ( WKT_ISO | WKT_EXTENDED ) )
|
2010-11-25 18:38:20 +00:00
|
|
|
dimensions = FLAGS_NDIMS(ptarray->flags);
|
2010-02-22 19:53:22 +00:00
|
|
|
|
|
|
|
/* Opening paren? */
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_PARENS) )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, "(");
|
2010-08-15 08:30:08 +00:00
|
|
|
|
2010-02-22 19:53:22 +00:00
|
|
|
/* Digits and commas */
|
2010-08-15 08:30:08 +00:00
|
|
|
for (i = 0; i < ptarray->npoints; i++)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
uchar *p = getPoint_internal(ptarray, i);
|
|
|
|
double d;
|
|
|
|
|
|
|
|
/* Commas before ever coord but the first */
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
for (j = 0; j < dimensions; j++)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
memcpy(&d, p + j * sizeof(double), sizeof(double));
|
2010-02-22 19:53:22 +00:00
|
|
|
/* Spaces before every ordinate but the first */
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( j > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, " ");
|
2010-05-17 23:38:04 +00:00
|
|
|
stringbuffer_aprintf(sb, "%.*g", precision, d);
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
|
2010-02-22 19:53:22 +00:00
|
|
|
/* Closing paren? */
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_PARENS) )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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)
|
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "POINT"); /* "POINT" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)pt, sb, variant);
|
|
|
|
}
|
|
|
|
|
2010-10-22 02:14:06 +00:00
|
|
|
if ( (! pt->point) || (pt->point->npoints < 1) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
2010-08-15 08:30:08 +00:00
|
|
|
}
|
2010-02-22 19:53:22 +00:00
|
|
|
|
|
|
|
ptarray_to_wkt_sb(pt->point, sb, precision, variant);
|
|
|
|
}
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* LINESTRING(0 0 0, 1 1 1)
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwline_to_wkt_sb(const LWLINE *line, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "LINESTRING"); /* "LINESTRING" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)line, sb, variant);
|
|
|
|
}
|
2010-10-22 02:14:06 +00:00
|
|
|
if ( (! line->points) || (line->points->npoints < 1) )
|
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptarray_to_wkt_sb(line->points, sb, precision, variant);
|
|
|
|
}
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* POLYGON(0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwpoly_to_wkt_sb(const LWPOLY *poly, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "POLYGON"); /* "POLYGON" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)poly, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( poly->nrings < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < poly->nrings; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
ptarray_to_wkt_sb(poly->rings[i], sb, precision, variant);
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* CIRCULARSTRING
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwcircstring_to_wkt_sb(const LWCIRCSTRING *circ, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "CIRCULARSTRING"); /* "CIRCULARSTRING" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)circ, sb, variant);
|
|
|
|
}
|
2010-10-22 02:14:06 +00:00
|
|
|
if ( (! circ->points) || (circ->points->npoints < 1) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
ptarray_to_wkt_sb(circ->points, sb, precision, variant);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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))
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwmpoint_to_wkt_sb(const LWMPOINT *mpoint, stringbuffer_t *sb, int precision, uchar variant)
|
|
|
|
{
|
2010-02-22 19:53:22 +00:00
|
|
|
int i = 0;
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "MULTIPOINT"); /* "MULTIPOINT" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoint, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( mpoint->ngeoms < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < mpoint->ngeoms; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
/* We don't want type strings or parens on our subgeoms */
|
2010-03-03 01:15:16 +00:00
|
|
|
lwpoint_to_wkt_sb(mpoint->geoms[i], sb, precision, variant | WKT_NO_PARENS | WKT_NO_TYPE );
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* MULTILINESTRING
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwmline_to_wkt_sb(const LWMLINE *mline, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
2010-08-15 08:30:08 +00:00
|
|
|
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "MULTILINESTRING"); /* "MULTILINESTRING" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)mline, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( mline->ngeoms < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < mline->ngeoms; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
/* We don't want type strings on our subgeoms */
|
2010-03-03 01:15:16 +00:00
|
|
|
lwline_to_wkt_sb(mline->geoms[i], sb, precision, variant | WKT_NO_TYPE );
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* MULTIPOLYGON
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwmpoly_to_wkt_sb(const LWMPOLY *mpoly, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
2010-08-15 08:30:08 +00:00
|
|
|
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "MULTIPOLYGON"); /* "MULTIPOLYGON" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoly, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( mpoly->ngeoms < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < mpoly->ngeoms; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
/* We don't want type strings on our subgeoms */
|
2010-03-03 01:15:16 +00:00
|
|
|
lwpoly_to_wkt_sb(mpoly->geoms[i], sb, precision, variant | WKT_NO_TYPE );
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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))
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwcompound_to_wkt_sb(const LWCOMPOUND *comp, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
2010-08-15 08:30:08 +00:00
|
|
|
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "COMPOUNDCURVE"); /* "COMPOUNDCURVE" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)comp, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( comp->ngeoms < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < comp->ngeoms; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
int type = comp->geoms[i]->type;
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
/* Linestring subgeoms don't get type identifiers */
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( type == LINETYPE )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-03-03 01:15:16 +00:00
|
|
|
lwline_to_wkt_sb((LWLINE*)comp->geoms[i], sb, precision, variant | WKT_NO_TYPE );
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
/* But circstring subgeoms *do* get type identifiers */
|
2010-08-15 08:30:08 +00:00
|
|
|
else if ( type == CIRCSTRINGTYPE )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
lwcircstring_to_wkt_sb((LWCIRCSTRING*)comp->geoms[i], sb, precision, variant );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
lwerror("lwcompound_to_wkt_sb: Unknown type recieved %d - %s", type, lwtype_name(type));
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
stringbuffer_append(sb, ")");
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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))
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwcurvepoly_to_wkt_sb(const LWCURVEPOLY *cpoly, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
2010-08-15 08:30:08 +00:00
|
|
|
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "CURVEPOLYGON"); /* "CURVEPOLYGON" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)cpoly, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( cpoly->nrings < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < cpoly->nrings; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
int type = cpoly->rings[i]->type;
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
2010-08-15 08:30:08 +00:00
|
|
|
switch (type)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
case LINETYPE:
|
|
|
|
/* Linestring subgeoms don't get type identifiers */
|
|
|
|
lwline_to_wkt_sb((LWLINE*)cpoly->rings[i], sb, precision, variant | WKT_NO_TYPE );
|
|
|
|
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:
|
2010-10-22 23:27:13 +00:00
|
|
|
lwerror("lwcurvepoly_to_wkt_sb: Unknown type recieved %d - %s", type, lwtype_name(type));
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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))
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwmcurve_to_wkt_sb(const LWMCURVE *mcurv, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "MULTICURVE"); /* "MULTICURVE" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)mcurv, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( mcurv->ngeoms < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < mcurv->ngeoms; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
int type = mcurv->geoms[i]->type;
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
2010-08-15 08:30:08 +00:00
|
|
|
switch (type)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
case LINETYPE:
|
|
|
|
/* Linestring subgeoms don't get type identifiers */
|
|
|
|
lwline_to_wkt_sb((LWLINE*)mcurv->geoms[i], sb, precision, variant | WKT_NO_TYPE );
|
|
|
|
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:
|
2010-10-22 23:27:13 +00:00
|
|
|
lwerror("lwmcurve_to_wkt_sb: Unknown type recieved %d - %s", type, lwtype_name(type));
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
}
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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)))
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwmsurface_to_wkt_sb(const LWMSURFACE *msurf, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "MULTISURFACE"); /* "MULTISURFACE" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)msurf, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( msurf->ngeoms < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < msurf->ngeoms; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
int type = msurf->geoms[i]->type;
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ",");
|
2010-08-15 08:30:08 +00:00
|
|
|
switch (type)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
case POLYGONTYPE:
|
|
|
|
/* Linestring subgeoms don't get type identifiers */
|
|
|
|
lwpoly_to_wkt_sb((LWPOLY*)msurf->geoms[i], sb, precision, variant | WKT_NO_TYPE );
|
|
|
|
break;
|
|
|
|
case CURVEPOLYTYPE:
|
|
|
|
/* But circstring subgeoms *do* get type identifiers */
|
2010-12-07 21:08:05 +00:00
|
|
|
lwcurvepoly_to_wkt_sb((LWCURVEPOLY*)msurf->geoms[i], sb, precision, variant);
|
2010-08-15 08:30:08 +00:00
|
|
|
break;
|
|
|
|
default:
|
2010-10-22 23:27:13 +00:00
|
|
|
lwerror("lwmsurface_to_wkt_sb: Unknown type recieved %d - %s", type, lwtype_name(type));
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
}
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* 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)))
|
|
|
|
*/
|
2010-08-15 08:30:08 +00:00
|
|
|
static void lwcollection_to_wkt_sb(const LWCOLLECTION *collection, stringbuffer_t *sb, int precision, uchar variant)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
2010-08-15 08:30:08 +00:00
|
|
|
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "GEOMETRYCOLLECTION"); /* "GEOMETRYCOLLECTION" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)collection, sb, variant);
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( collection->ngeoms < 1 )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-10-22 23:27:13 +00:00
|
|
|
empty_to_wkt_sb(sb);
|
2010-02-22 19:53:22 +00:00
|
|
|
return;
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
stringbuffer_append(sb, "(");
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
2010-08-15 08:30:08 +00:00
|
|
|
for ( i = 0; i < collection->ngeoms; i++ )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( i > 0 )
|
|
|
|
stringbuffer_append(sb, ",");
|
2010-02-22 19:53:22 +00:00
|
|
|
lwgeom_to_wkt_sb((LWGEOM*)collection->geoms[i], sb, precision, variant );
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
stringbuffer_append(sb, ")");
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
|
2010-10-22 23:27:13 +00:00
|
|
|
/*
|
|
|
|
* TRIANGLE
|
|
|
|
*/
|
|
|
|
static void lwtriangle_to_wkt_sb(const LWTRIANGLE *tri, stringbuffer_t *sb, int precision, uchar variant)
|
|
|
|
{
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "TRIANGLE"); /* "TRIANGLE" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)tri, sb, variant);
|
|
|
|
}
|
|
|
|
if ( (! tri->points) || (tri->points->npoints < 1) )
|
|
|
|
{
|
|
|
|
empty_to_wkt_sb(sb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringbuffer_append(sb, "("); /* Triangles have extraneous brackets */
|
|
|
|
ptarray_to_wkt_sb(tri->points, sb, precision, variant);
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TIN
|
|
|
|
*/
|
|
|
|
static void lwtin_to_wkt_sb(const LWTIN *tin, stringbuffer_t *sb, int precision, uchar variant)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
|
|
|
{
|
|
|
|
stringbuffer_append(sb, "TIN"); /* "TIN" */
|
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)tin, sb, variant);
|
|
|
|
}
|
|
|
|
if ( tin->ngeoms < 1 )
|
|
|
|
{
|
|
|
|
empty_to_wkt_sb(sb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringbuffer_append(sb, "(");
|
|
|
|
for ( i = 0; i < tin->ngeoms; i++ )
|
|
|
|
{
|
|
|
|
if ( i > 0 )
|
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
/* We don't want type strings on our subgeoms */
|
|
|
|
lwtriangle_to_wkt_sb(tin->geoms[i], sb, precision, variant | WKT_NO_TYPE );
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* POLYHEDRALSURFACE
|
|
|
|
*/
|
|
|
|
static void lwpsurface_to_wkt_sb(const LWPSURFACE *psurf, stringbuffer_t *sb, int precision, uchar variant)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if ( ! (variant & WKT_NO_TYPE) )
|
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
stringbuffer_append(sb, "POLYHEDRALSURFACE"); /* "POLYHEDRALSURFACE" */
|
2010-10-22 23:27:13 +00:00
|
|
|
dimension_qualifiers_to_wkt_sb((LWGEOM*)psurf, sb, variant);
|
|
|
|
}
|
|
|
|
if ( psurf->ngeoms < 1 )
|
|
|
|
{
|
|
|
|
empty_to_wkt_sb(sb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-12-07 21:08:05 +00:00
|
|
|
variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
|
|
|
|
|
2010-10-22 23:27:13 +00:00
|
|
|
stringbuffer_append(sb, "(");
|
|
|
|
for ( i = 0; i < psurf->ngeoms; i++ )
|
|
|
|
{
|
|
|
|
if ( i > 0 )
|
|
|
|
stringbuffer_append(sb, ",");
|
|
|
|
/* We don't want type strings on our subgeoms */
|
|
|
|
lwpoly_to_wkt_sb(psurf->geoms[i], sb, precision, variant | WKT_NO_TYPE );
|
|
|
|
}
|
|
|
|
stringbuffer_append(sb, ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-03 05:40:29 +00:00
|
|
|
/*
|
2010-02-22 19:53:22 +00:00
|
|
|
* Generic GEOMETRY
|
|
|
|
*/
|
|
|
|
static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uchar variant)
|
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
LWDEBUGF(4, "lwgeom_to_wkt_sb: type %s, hasz %d, hasm %d",
|
|
|
|
lwtype_name(geom->type), (geom->type),
|
|
|
|
FLAGS_GET_Z(geom->flags)?1:0, FLAGS_GET_M(geom->flags)?1:0);
|
|
|
|
|
|
|
|
switch (geom->type)
|
2010-08-15 08:30:08 +00:00
|
|
|
{
|
|
|
|
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;
|
2010-10-22 23:27:13 +00:00
|
|
|
case TRIANGLETYPE:
|
|
|
|
lwtriangle_to_wkt_sb((LWTRIANGLE*)geom, sb, precision, variant);
|
|
|
|
break;
|
|
|
|
case TINTYPE:
|
|
|
|
lwtin_to_wkt_sb((LWTIN*)geom, sb, precision, variant);
|
|
|
|
break;
|
|
|
|
case POLYHEDRALSURFACETYPE:
|
|
|
|
lwpsurface_to_wkt_sb((LWPSURFACE*)geom, sb, precision, variant);
|
|
|
|
break;
|
2010-08-15 08:30:08 +00:00
|
|
|
default:
|
|
|
|
lwerror("lwgeom_to_wkt_sb: Type %d - %s unsupported.",
|
2010-11-21 19:02:23 +00:00
|
|
|
geom->type, lwtype_name(geom->type));
|
2010-02-22 19:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-08-15 08:30:08 +00:00
|
|
|
* WKT emitter function. Allocates a new *char and fills it with the WKT
|
2010-03-02 23:18:39 +00:00
|
|
|
* representation. If size_out is not NULL, it will be set to the size of the
|
|
|
|
* allocated *char.
|
2010-08-15 08:30:08 +00:00
|
|
|
*
|
2010-03-03 05:38:38 +00:00
|
|
|
* @param variant Bitmasked value, accepts one of WKT_ISO, WKT_SFSQL, WKT_EXTENDED.
|
|
|
|
* @param precision Number of significant digits in the output doubles.
|
2010-08-15 08:30:08 +00:00
|
|
|
* @param size_out If supplied, will return the size of the returned string,
|
2010-03-03 05:38:38 +00:00
|
|
|
* including the null terminator.
|
2010-02-22 19:53:22 +00:00
|
|
|
*/
|
2010-03-02 23:18:39 +00:00
|
|
|
char* lwgeom_to_wkt(const LWGEOM *geom, uchar variant, int precision, size_t *size_out)
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
stringbuffer_t *sb;
|
|
|
|
char *str = NULL;
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( geom == NULL )
|
2010-02-22 19:53:22 +00:00
|
|
|
return NULL;
|
|
|
|
sb = stringbuffer_create();
|
2010-02-24 13:50:22 +00:00
|
|
|
/* Extended mode starts with an "SRID=" section for geoms that have one */
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( (variant & WKT_EXTENDED) && lwgeom_has_srid(geom) )
|
2010-02-24 13:50:22 +00:00
|
|
|
{
|
2010-11-25 17:34:21 +00:00
|
|
|
stringbuffer_aprintf(sb, "SRID=%d;", geom->srid);
|
2010-02-24 13:50:22 +00:00
|
|
|
}
|
2010-02-22 19:53:22 +00:00
|
|
|
lwgeom_to_wkt_sb(geom, sb, precision, variant);
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( stringbuffer_getstring(sb) == NULL )
|
2010-02-22 19:53:22 +00:00
|
|
|
{
|
|
|
|
lwerror("Uh oh");
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-03-02 23:18:39 +00:00
|
|
|
str = stringbuffer_getstringcopy(sb);
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( size_out )
|
2010-03-02 23:18:39 +00:00
|
|
|
*size_out = stringbuffer_getlength(sb) + 1;
|
2010-02-22 19:53:22 +00:00
|
|
|
stringbuffer_destroy(sb);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|