[postgis-tickets] [SCM] PostGIS branch master updated. 3.3.0rc2-373-gc23debb38

git at osgeo.org git at osgeo.org
Mon Dec 5 15:08:56 PST 2022


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".

The branch, master has been updated
       via  c23debb38a4b031fe8041bd3c7e904dca935412c (commit)
      from  8cf36e0e99af15065ab0ba1a83bc0ad41ffb507f (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c23debb38a4b031fe8041bd3c7e904dca935412c
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Mon Dec 5 15:07:32 2022 -0800

    Convert GeoJSON output to use stringbuffer instead of count-n-fill procedure. Much less code.
    Performance for large-amounts-of-small objects is about 10% faster. Performance for larger
    objects (natural earth admin for example) is 3%-5% slower.

diff --git a/liblwgeom/lwout_geojson.c b/liblwgeom/lwout_geojson.c
index 721839986..aa14748a1 100644
--- a/liblwgeom/lwout_geojson.c
+++ b/liblwgeom/lwout_geojson.c
@@ -25,763 +25,383 @@
 
 
 #include "liblwgeom_internal.h"
+#include "stringbuffer.h"
 #include <string.h>	/* strlen */
 #include <assert.h>
 
-static lwvarlena_t *asgeojson_point(const LWPOINT *point, const char *srs, GBOX *bbox, int precision);
-static lwvarlena_t *asgeojson_line(const LWLINE *line, const char *srs, GBOX *bbox, int precision);
-static lwvarlena_t *asgeojson_triangle(const LWTRIANGLE *tri, const char *srs, GBOX *bbox, int precision);
-static lwvarlena_t *asgeojson_poly(const LWPOLY *poly, const char *srs, GBOX *bbox, int precision);
-static lwvarlena_t *asgeojson_multipoint(const LWMPOINT *mpoint, const char *srs, GBOX *bbox, int precision);
-static lwvarlena_t *asgeojson_multiline(const LWMLINE *mline, const char *srs, GBOX *bbox, int precision);
-static lwvarlena_t *asgeojson_multipolygon(const LWMPOLY *mpoly, const char *srs, GBOX *bbox, int precision);
-static lwvarlena_t *asgeojson_collection(const LWCOLLECTION *col, const char *srs, GBOX *bbox, int precision);
-static size_t asgeojson_geom_size(const LWGEOM *geom, GBOX *bbox, int precision);
-static size_t asgeojson_geom_buf(const LWGEOM *geom, char *output, GBOX *bbox, int precision);
 
-static size_t pointArray_to_geojson(POINTARRAY *pa, char *buf, int precision);
-static size_t pointArray_geojson_size(POINTARRAY *pa, int precision);
+typedef struct geojson_opts {
+	const char *srs;
+	GBOX       *bbox;
+	int         precision;
+	int         hasz;
+	int         isCollectionElement;
+} geojson_opts;
 
-/**
- * Takes a GEOMETRY and returns a GeoJson representation
- */
-lwvarlena_t *
-lwgeom_to_geojson(const LWGEOM *geom, const char *srs, int precision, int has_bbox)
-{
-	int type = geom->type;
-	GBOX *bbox = NULL;
-	GBOX tmp = {0};
+enum {
+	geojson_tagged,
+	geojson_untagged
+};
 
-	if (has_bbox)
-	{
-		/* Whether these are geography or geometry,
-		   the GeoJSON expects a cartesian bounding box */
-		lwgeom_calculate_gbox_cartesian(geom, &tmp);
-		bbox = &tmp;
-	}
+static void asgeojson_geometry(stringbuffer_t *sb, const LWGEOM *geom, const geojson_opts *opts);
 
-	switch (type)
-	{
-	case POINTTYPE:
-		return asgeojson_point((LWPOINT*)geom, srs, bbox, precision);
-	case LINETYPE:
-		return asgeojson_line((LWLINE*)geom, srs, bbox, precision);
-	case POLYGONTYPE:
-		return asgeojson_poly((LWPOLY*)geom, srs, bbox, precision);
-	case MULTIPOINTTYPE:
-		return asgeojson_multipoint((LWMPOINT*)geom, srs, bbox, precision);
-	case MULTILINETYPE:
-		return asgeojson_multiline((LWMLINE*)geom, srs, bbox, precision);
-	case MULTIPOLYGONTYPE:
-		return asgeojson_multipolygon((LWMPOLY*)geom, srs, bbox, precision);
-	case TRIANGLETYPE:
-		return asgeojson_triangle((LWTRIANGLE *)geom, srs, bbox, precision);
-	case TINTYPE:
-	case COLLECTIONTYPE:
-		return asgeojson_collection((LWCOLLECTION*)geom, srs, bbox, precision);
-	default:
-		lwerror("lwgeom_to_geojson: '%s' geometry type not supported",
-		        lwtype_name(type));
-	}
-
-	/* Never get here */
-	return NULL;
-}
 
 
-
-/**
- * Handle SRS
- */
-static size_t
-asgeojson_srs_size(const char *srs)
-{
-	int size;
-
-	size = sizeof("'crs':{'type':'name',");
-	size += sizeof("'properties':{'name':''}},");
-	size += strlen(srs) * sizeof(char);
-
-	return size;
-}
-
-static size_t
-asgeojson_srs_buf(char *output, const char *srs)
+static void
+coordinate_to_geojson(stringbuffer_t *sb, const POINTARRAY *pa, uint32_t i, const geojson_opts *opts)
 {
-	char *ptr = output;
-
-	ptr += sprintf(ptr, "\"crs\":{\"type\":\"name\",");
-	ptr += sprintf(ptr, "\"properties\":{\"name\":\"%s\"}},", srs);
-
-	return (ptr-output);
+	if (!FLAGS_GET_Z(pa->flags))
+	{
+		const POINT2D *pt = getPoint2d_cp(pa, i);
+		stringbuffer_append_char(sb, '[');
+		stringbuffer_append_double(sb, pt->x, opts->precision);
+		stringbuffer_append_char(sb, ',');
+		stringbuffer_append_double(sb, pt->y, opts->precision);
+		stringbuffer_append_char(sb, ']');
+	}
+	else
+	{
+		const POINT3D *pt = getPoint3d_cp(pa, i);
+		stringbuffer_append_char(sb, '[');
+		stringbuffer_append_double(sb, pt->x, opts->precision);
+		stringbuffer_append_char(sb, ',');
+		stringbuffer_append_double(sb, pt->y, opts->precision);
+		stringbuffer_append_char(sb, ',');
+		stringbuffer_append_double(sb, pt->z, opts->precision);
+		stringbuffer_append_char(sb, ']');
+	}
 }
 
-
-
-/**
- * Handle Bbox
- */
-static size_t
-asgeojson_bbox_size(int hasz, int precision)
+static void
+pointArray_to_geojson(stringbuffer_t *sb, const POINTARRAY *pa, const geojson_opts *opts)
 {
-	int size;
-
-	if (!hasz)
+	if (!pa || pa->npoints == 0)
 	{
-		size = sizeof("\"bbox\":[,,,],");
-		size += 2 * 2 * (OUT_MAX_BYTES_DOUBLE + precision);
+		stringbuffer_append_len(sb, "[]", 2);
+		return;
 	}
-	else
+
+	stringbuffer_append_char(sb, '[');
+	for (uint32_t i = 0; i < pa->npoints; i++)
 	{
-		size = sizeof("\"bbox\":[,,,,,],");
-		size += 2 * 3 * (OUT_MAX_BYTES_DOUBLE + precision);
+		if (i) stringbuffer_append_char(sb, ',');
+		coordinate_to_geojson(sb, pa, i, opts);
 	}
-
-	return size;
+	stringbuffer_append_char(sb, ']');
+	return;
 }
 
-static size_t
-asgeojson_bbox_buf(char *output, GBOX *bbox, int hasz, int precision)
+static void
+asgeojson_srs(stringbuffer_t *sb, const geojson_opts *opts)
 {
-	char *ptr = output;
+	if (!opts->srs) return;
+	stringbuffer_append_len(sb, "\"crs\":{\"type\":\"name\",", 21);
+	stringbuffer_aprintf(sb, "\"properties\":{\"name\":\"%s\"}},", opts->srs);
+	return;
+}
 
-	if (!hasz)
-		ptr += sprintf(ptr, "\"bbox\":[%.*f,%.*f,%.*f,%.*f],",
-		               precision, bbox->xmin, precision, bbox->ymin,
-		               precision, bbox->xmax, precision, bbox->ymax);
-	else
-		ptr += sprintf(ptr, "\"bbox\":[%.*f,%.*f,%.*f,%.*f,%.*f,%.*f],",
-		               precision, bbox->xmin, precision, bbox->ymin, precision, bbox->zmin,
-		               precision, bbox->xmax, precision, bbox->ymax, precision, bbox->zmax);
 
-	return (ptr-output);
+static void
+asgeojson_bbox(stringbuffer_t *sb, const geojson_opts *opts)
+{
+	if (!opts->bbox) return;
+	if (!opts->hasz)
+		stringbuffer_aprintf(sb, "\"bbox\":[%.*f,%.*f,%.*f,%.*f],",
+			opts->precision, opts->bbox->xmin,
+			opts->precision, opts->bbox->ymin,
+			opts->precision, opts->bbox->xmax,
+			opts->precision, opts->bbox->ymax);
+	else
+		stringbuffer_aprintf(sb, "\"bbox\":[%.*f,%.*f,%.*f,%.*f,%.*f,%.*f],",
+			opts->precision, opts->bbox->xmin,
+			opts->precision, opts->bbox->ymin,
+			opts->precision, opts->bbox->zmin,
+			opts->precision, opts->bbox->xmax,
+			opts->precision, opts->bbox->ymax,
+			opts->precision, opts->bbox->zmax);
+	return;
 }
 
 
-
 /**
  * Point Geometry
  */
-
-static size_t
-asgeojson_point_size(const LWPOINT *point, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_point_coords(stringbuffer_t *sb, const LWPOINT *point, const geojson_opts *opts, int tagged)
 {
-	int size;
-
-	size = pointArray_geojson_size(point->point, precision);
-	size += sizeof("{'type':'Point',");
-	size += sizeof("'coordinates':}");
-
-	if ( lwpoint_is_empty(point) )
-		size += 2; /* [] */
-
-	if (srs) size += asgeojson_srs_size(srs);
-	if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(point->flags), precision);
-
-	return size;
+	if (tagged == geojson_tagged) stringbuffer_append_len(sb, "\"coordinates\":", 14);
+	if (lwgeom_is_empty((LWGEOM*)point))
+		stringbuffer_append_len(sb, "[]", 2);
+	else
+		coordinate_to_geojson(sb, point->point, 0, opts);
+	return;
 }
 
-static size_t
-asgeojson_point_buf(const LWPOINT *point, const char *srs, char *output, GBOX *bbox, int precision)
+static void
+asgeojson_line_coords(stringbuffer_t *sb, const LWLINE *line, const geojson_opts *opts, int tagged)
 {
-	char *ptr = output;
-
-	ptr += sprintf(ptr, "{\"type\":\"Point\",");
-	if (srs) ptr += asgeojson_srs_buf(ptr, srs);
-	if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(point->flags), precision);
-
-	ptr += sprintf(ptr, "\"coordinates\":");
-	if ( lwpoint_is_empty(point) )
-		ptr += sprintf(ptr, "[]");
-	ptr += pointArray_to_geojson(point->point, ptr, precision);
-	ptr += sprintf(ptr, "}");
-
-	return (ptr-output);
+	if (tagged == geojson_tagged) stringbuffer_append_len(sb, "\"coordinates\":", 14);
+	if (lwgeom_is_empty((LWGEOM*)line))
+		stringbuffer_append_len(sb, "[]", 2);
+	else
+		pointArray_to_geojson(sb, line->points, opts);
+	return;
 }
 
-static lwvarlena_t *
-asgeojson_point(const LWPOINT *point, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_poly_coords(stringbuffer_t *sb, const LWPOLY *poly, const geojson_opts *opts, int tagged)
 {
-	uint32_t size = asgeojson_point_size(point, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_point_buf(point, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
+	uint32_t i;
+	if (tagged == geojson_tagged) stringbuffer_append_len(sb, "\"coordinates\":", 14);
+	if (lwgeom_is_empty((LWGEOM*)poly))
+		stringbuffer_append_len(sb, "[]", 2);
+	else
+	{
+		stringbuffer_append_char(sb, '[');
+		for (i = 0; i < poly->nrings; i++)
+		{
+			if (i) stringbuffer_append_char(sb, ',');
+			pointArray_to_geojson(sb, poly->rings[i], opts);
+		}
+		stringbuffer_append_char(sb, ']');
+	}
+	return;
 }
 
 /**
- * Triangle Geometry
+ * Point Geometry
  */
-
-static size_t
-asgeojson_triangle_size(const LWTRIANGLE *tri, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_point(stringbuffer_t *sb, const LWPOINT *point, const geojson_opts *opts)
 {
-	int size;
-
-	size = sizeof("{'type':'Polygon',");
-	if (srs)
-		size += asgeojson_srs_size(srs);
-	if (bbox)
-		size += asgeojson_bbox_size(FLAGS_GET_Z(tri->flags), precision);
-	size += sizeof("'coordinates':[[]]}");
-	size += pointArray_geojson_size(tri->points, precision);
-
-	return size;
-}
-
-static size_t
-asgeojson_triangle_buf(const LWTRIANGLE *tri, const char *srs, char *output, GBOX *bbox, int precision)
-{
-	char *ptr = output;
-
-	ptr += sprintf(ptr, "{\"type\":\"Polygon\",");
-	if (srs)
-		ptr += asgeojson_srs_buf(ptr, srs);
-	if (bbox)
-		ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(tri->flags), precision);
-	ptr += sprintf(ptr, "\"coordinates\":[[");
-	ptr += pointArray_to_geojson(tri->points, ptr, precision);
-	ptr += sprintf(ptr, "]]}");
-
-	return (ptr - output);
+	stringbuffer_append_len(sb, "{\"type\":\"Point\",", 16);
+	asgeojson_srs(sb, opts);
+	asgeojson_bbox(sb, opts);
+	asgeojson_point_coords(sb, point, opts, geojson_tagged);
+	stringbuffer_append_char(sb, '}');
+	return;
 }
 
-static lwvarlena_t *
-asgeojson_triangle(const LWTRIANGLE *tri, const char *srs, GBOX *bbox, int precision)
-{
-	uint32_t size = asgeojson_triangle_size(tri, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_triangle_buf(tri, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
+/**
+ * Triangle Geometry
+ */
+static void
+asgeojson_triangle(stringbuffer_t *sb, const LWTRIANGLE *tri, const geojson_opts *opts)
+{
+	stringbuffer_append_len(sb, "{\"type\":\"Polygon\",", 18);
+	asgeojson_srs(sb, opts);
+	asgeojson_bbox(sb, opts);
+	stringbuffer_append_len(sb, "\"coordinates\":[", 15);
+	if (lwtriangle_is_empty(tri))
+		stringbuffer_append_len(sb, "[]", 2);
+	else
+		pointArray_to_geojson(sb, tri->points, opts);
+	stringbuffer_append_len(sb, "]}", 2);
+	return;
 }
 
 /**
  * Line Geometry
  */
-
-static size_t
-asgeojson_line_size(const LWLINE *line, const char *srs, GBOX *bbox, int precision)
-{
-	int size;
-
-	size = sizeof("{'type':'LineString',");
-	if (srs) size += asgeojson_srs_size(srs);
-	if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(line->flags), precision);
-	size += sizeof("'coordinates':[]}");
-	size += pointArray_geojson_size(line->points, precision);
-
-	return size;
-}
-
-static size_t
-asgeojson_line_buf(const LWLINE *line, const char *srs, char *output, GBOX *bbox, int precision)
-{
-	char *ptr=output;
-
-	ptr += sprintf(ptr, "{\"type\":\"LineString\",");
-	if (srs) ptr += asgeojson_srs_buf(ptr, srs);
-	if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(line->flags), precision);
-	ptr += sprintf(ptr, "\"coordinates\":[");
-	ptr += pointArray_to_geojson(line->points, ptr, precision);
-	ptr += sprintf(ptr, "]}");
-
-	return (ptr-output);
-}
-
-static lwvarlena_t *
-asgeojson_line(const LWLINE *line, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_line(stringbuffer_t *sb, const LWLINE *line, const geojson_opts *opts)
 {
-	uint32_t size = asgeojson_line_size(line, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_line_buf(line, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
+	const char tmpl[] = "{\"type\":\"LineString\",";
+	stringbuffer_append_len(sb, tmpl, sizeof(tmpl)-1);
+	asgeojson_srs(sb, opts);
+	asgeojson_bbox(sb, opts);
+	asgeojson_line_coords(sb, line, opts, geojson_tagged);
+	stringbuffer_append_char(sb, '}');
+	return;
 }
 
-
-
 /**
  * Polygon Geometry
  */
-
-static size_t
-asgeojson_poly_size(const LWPOLY *poly, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_poly(stringbuffer_t *sb, const LWPOLY *poly, const geojson_opts *opts)
 {
-	size_t size;
-	uint32_t i;
-
-	size = sizeof("{\"type\":\"Polygon\",");
-	if (srs) size += asgeojson_srs_size(srs);
-	if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(poly->flags), precision);
-	size += sizeof("\"coordinates\":[");
-	for (i=0; i<poly->nrings; i++)
-	{
-		size += pointArray_geojson_size(poly->rings[i], precision);
-		size += sizeof("[]");
-	}
-	size += sizeof(",") * i;
-	size += sizeof("]}");
-
-	return size;
-}
-
-static size_t
-asgeojson_poly_buf(const LWPOLY *poly, const char *srs, char *output, GBOX *bbox, int precision)
-{
-	uint32_t i;
-
-	char *ptr=output;
-
-	ptr += sprintf(ptr, "{\"type\":\"Polygon\",");
-	if (srs) ptr += asgeojson_srs_buf(ptr, srs);
-	if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(poly->flags), precision);
-	ptr += sprintf(ptr, "\"coordinates\":[");
-	for (i=0; i<poly->nrings; i++)
-	{
-		if (i) ptr += sprintf(ptr, ",");
-		ptr += sprintf(ptr, "[");
-		ptr += pointArray_to_geojson(poly->rings[i], ptr, precision);
-		ptr += sprintf(ptr, "]");
-	}
-	ptr += sprintf(ptr, "]}");
-
-	return (ptr-output);
-}
-
-static lwvarlena_t *
-asgeojson_poly(const LWPOLY *poly, const char *srs, GBOX *bbox, int precision)
-{
-	uint32_t size = asgeojson_poly_size(poly, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_poly_buf(poly, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
+	stringbuffer_append_len(sb, "{\"type\":\"Polygon\",", 18);
+	asgeojson_srs(sb, opts);
+	asgeojson_bbox(sb, opts);
+	asgeojson_poly_coords(sb, poly, opts, geojson_tagged);
+	stringbuffer_append_char(sb, '}');
+	return;
 }
 
-
-
 /**
  * Multipoint Geometry
  */
-
-static size_t
-asgeojson_multipoint_size(const LWMPOINT *mpoint, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_multipoint(stringbuffer_t *sb, const LWMPOINT *mpoint, const geojson_opts *opts)
 {
-	LWPOINT * point;
-	int size;
 	uint32_t i, ngeoms = mpoint->ngeoms;
-
-	size = sizeof("{'type':'MultiPoint',");
-	if (srs) size += asgeojson_srs_size(srs);
-	if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mpoint->flags), precision);
-	size += sizeof("'coordinates':[]}");
+	stringbuffer_append_len(sb, "{\"type\":\"MultiPoint\",", 21);
+	asgeojson_srs(sb, opts);
+	asgeojson_bbox(sb, opts);
+	stringbuffer_append_len(sb, "\"coordinates\":[", 15);
 
 	if (lwgeom_is_empty((LWGEOM*)mpoint))
 		ngeoms = 0;
 
-	for (i=0; i<ngeoms; i++)
-	{
-		point = mpoint->geoms[i];
-		size += pointArray_geojson_size(point->point, precision);
-	}
-	size += sizeof(",") * i;
-
-	return size;
-}
-
-static size_t
-asgeojson_multipoint_buf(const LWMPOINT *mpoint, const char *srs, char *output, GBOX *bbox, int precision)
-{
-	LWPOINT *point;
-	uint32_t i, ngeoms = mpoint->ngeoms;
-	char *ptr=output;
-
-	ptr += sprintf(ptr, "{\"type\":\"MultiPoint\",");
-	if (srs) ptr += asgeojson_srs_buf(ptr, srs);
-	if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mpoint->flags), precision);
-	ptr += sprintf(ptr, "\"coordinates\":[");
-
-	if (lwgeom_is_empty((LWGEOM*)mpoint))
-		ngeoms = 0;
-
-	for (i=0; i<ngeoms; i++)
+	for (i=0; i < ngeoms; i++)
 	{
-		if (i) ptr += sprintf(ptr, ",");
-		point = mpoint->geoms[i];
-		ptr += pointArray_to_geojson(point->point, ptr, precision);
+		if (i) stringbuffer_append_char(sb, ',');
+		asgeojson_point_coords(sb, mpoint->geoms[i], opts, geojson_untagged);
 	}
-	ptr += sprintf(ptr, "]}");
-
-	return (ptr - output);
-}
-
-static lwvarlena_t *
-asgeojson_multipoint(const LWMPOINT *mpoint, const char *srs, GBOX *bbox, int precision)
-{
-	uint32_t size = asgeojson_multipoint_size(mpoint, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_multipoint_buf(mpoint, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
+	stringbuffer_append_len(sb, "]}", 2);
+	return;
 }
 
 
-
 /**
- * Multiline Geometry
+ * Multipoint Geometry
  */
-
-static size_t
-asgeojson_multiline_size(const LWMLINE *mline, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_multiline(stringbuffer_t *sb, const LWMLINE *mline, const geojson_opts *opts)
 {
-	LWLINE * line;
-	int size;
 	uint32_t i, ngeoms = mline->ngeoms;
-
-	size = sizeof("{'type':'MultiLineString',");
-	if (srs) size += asgeojson_srs_size(srs);
-	if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mline->flags), precision);
-	size += sizeof("'coordinates':[]}");
+	stringbuffer_append_len(sb, "{\"type\":\"MultiLineString\",", 26);
+	asgeojson_srs(sb, opts);
+	asgeojson_bbox(sb, opts);
+	stringbuffer_append_len(sb, "\"coordinates\":[", 15);
 
 	if (lwgeom_is_empty((LWGEOM*)mline))
 		ngeoms = 0;
 
-	for (i=0 ; i<ngeoms; i++)
-	{
-		line = mline->geoms[i];
-		size += pointArray_geojson_size(line->points, precision);
-		size += sizeof("[]");
-	}
-	size += sizeof(",") * i;
-
-	return size;
-}
-
-static size_t
-asgeojson_multiline_buf(const LWMLINE *mline, const char *srs, char *output, GBOX *bbox, int precision)
-{
-	LWLINE *line;
-	uint32_t i, ngeoms = mline->ngeoms;
-	char *ptr=output;
-
-	ptr += sprintf(ptr, "{\"type\":\"MultiLineString\",");
-	if (srs) ptr += asgeojson_srs_buf(ptr, srs);
-	if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mline->flags), precision);
-	ptr += sprintf(ptr, "\"coordinates\":[");
-
-	if (lwgeom_is_empty((LWGEOM*)mline))
-		ngeoms = 0;
-
-	for (i=0; i<ngeoms; i++)
-	{
-		if (i) ptr += sprintf(ptr, ",");
-		ptr += sprintf(ptr, "[");
-		line = mline->geoms[i];
-		ptr += pointArray_to_geojson(line->points, ptr, precision);
-		ptr += sprintf(ptr, "]");
-	}
-
-	ptr += sprintf(ptr, "]}");
-
-	return (ptr - output);
-}
-
-static lwvarlena_t *
-asgeojson_multiline(const LWMLINE *mline, const char *srs, GBOX *bbox, int precision)
-{
-	uint32_t size = asgeojson_multiline_size(mline, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_multiline_buf(mline, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
-}
-
-
-
-/**
- * MultiPolygon Geometry
- */
-
-static size_t
-asgeojson_multipolygon_size(const LWMPOLY *mpoly, const char *srs, GBOX *bbox, int precision)
-{
-	LWPOLY *poly;
-	int size;
-	uint32_t i, j, ngeoms = mpoly->ngeoms;
-
-	size = sizeof("{'type':'MultiPolygon',");
-	if (srs) size += asgeojson_srs_size(srs);
-	if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mpoly->flags), precision);
-	size += sizeof("'coordinates':[]}");
-
-	if (lwgeom_is_empty((LWGEOM*)mpoly))
-		ngeoms = 0;
-
 	for (i=0; i < ngeoms; i++)
 	{
-		poly = mpoly->geoms[i];
-		for (j=0 ; j <poly->nrings ; j++)
-		{
-			size += pointArray_geojson_size(poly->rings[j], precision);
-			size += sizeof("[]");
-		}
-		size += sizeof("[]");
+		if (i) stringbuffer_append_char(sb, ',');
+		asgeojson_line_coords(sb, mline->geoms[i], opts, geojson_untagged);
 	}
-	size += sizeof(",") * i;
-	size += sizeof("]}");
-
-	return size;
+	stringbuffer_append_len(sb, "]}", 2);
+	return;
 }
 
-static size_t
-asgeojson_multipolygon_buf(const LWMPOLY *mpoly, const char *srs, char *output, GBOX *bbox, int precision)
+
+static void
+asgeojson_multipolygon(stringbuffer_t *sb, const LWMPOLY *mpoly, const geojson_opts *opts)
 {
-	LWPOLY *poly;
-	uint32_t i, j, ngeoms = mpoly->ngeoms;
-	char *ptr=output;
+	uint32_t i, ngeoms = mpoly->ngeoms;
 
-	ptr += sprintf(ptr, "{\"type\":\"MultiPolygon\",");
-	if (srs) ptr += asgeojson_srs_buf(ptr, srs);
-	if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mpoly->flags), precision);
-	ptr += sprintf(ptr, "\"coordinates\":[");
+	stringbuffer_append_len(sb, "{\"type\":\"MultiPolygon\",", 23);
+	asgeojson_srs(sb, opts);
+	asgeojson_bbox(sb, opts);
+	stringbuffer_append_len(sb, "\"coordinates\":[", 15);
 
 	if (lwgeom_is_empty((LWGEOM*)mpoly))
 		ngeoms = 0;
 
 	for (i=0; i < ngeoms; i++)
 	{
-		if (i) ptr += sprintf(ptr, ",");
-		ptr += sprintf(ptr, "[");
-		poly = mpoly->geoms[i];
-		for (j=0 ; j < poly->nrings ; j++)
-		{
-			if (j) ptr += sprintf(ptr, ",");
-			ptr += sprintf(ptr, "[");
-			ptr += pointArray_to_geojson(poly->rings[j], ptr, precision);
-			ptr += sprintf(ptr, "]");
-		}
-		ptr += sprintf(ptr, "]");
+		if (i) stringbuffer_append_char(sb, ',');
+		asgeojson_poly_coords(sb, mpoly->geoms[i], opts, geojson_untagged);
 	}
-	ptr += sprintf(ptr, "]}");
-
-	return (ptr - output);
-}
-
-static lwvarlena_t *
-asgeojson_multipolygon(const LWMPOLY *mpoly, const char *srs, GBOX *bbox, int precision)
-{
-	uint32_t size = asgeojson_multipolygon_size(mpoly, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_multipolygon_buf(mpoly, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
+	stringbuffer_append_len(sb, "]}", 2);
+	return;
 }
 
-
-
 /**
  * Collection Geometry
  */
-
-static size_t
-asgeojson_collection_size(const LWCOLLECTION *col, const char *srs, GBOX *bbox, int precision)
+static void
+asgeojson_collection(stringbuffer_t *sb, const LWCOLLECTION *col, const geojson_opts *opts)
 {
 	uint32_t i, ngeoms = col->ngeoms;
-	size_t size;
-	LWGEOM *subgeom;
 
-	size = sizeof("{'type':'GeometryCollection',");
-	if (srs) size += asgeojson_srs_size(srs);
-	if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(col->flags), precision);
-	size += sizeof("'geometries':");
+	/* subgeometries don't get boxes or srs */
+	geojson_opts subopts = *opts;
+	subopts.bbox = NULL;
+	subopts.srs = NULL;
+	subopts.isCollectionElement = LW_TRUE;
 
-	if (lwgeom_is_empty((LWGEOM*)col))
-		ngeoms = 0;
-
-	for (i=0; i<ngeoms; i++)
-	{
-		subgeom = col->geoms[i];
-		size += asgeojson_geom_size(subgeom, NULL, precision);
-	}
-	size += sizeof(",") * i;
-	size += sizeof("]}");
-
-	return size;
-}
-
-static size_t
-asgeojson_collection_buf(const LWCOLLECTION *col, const char *srs, char *output, GBOX *bbox, int precision)
-{
-	uint32_t i, ngeoms = col->ngeoms;
-	char *ptr=output;
-	LWGEOM *subgeom;
-
-	ptr += sprintf(ptr, "{\"type\":\"GeometryCollection\",");
-	if (srs) ptr += asgeojson_srs_buf(ptr, srs);
-	if (col->ngeoms && bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(col->flags), precision);
-	ptr += sprintf(ptr, "\"geometries\":[");
+	stringbuffer_append_len(sb, "{\"type\":\"GeometryCollection\",", 29);
+	asgeojson_srs(sb, opts);
+	if (col->ngeoms) asgeojson_bbox(sb, opts);
+	stringbuffer_append_len(sb, "\"geometries\":[", 14);
 
 	if (lwgeom_is_empty((LWGEOM*)col))
 		ngeoms = 0;
 
 	for (i=0; i<ngeoms; i++)
 	{
-		if (i) ptr += sprintf(ptr, ",");
-		subgeom = col->geoms[i];
-		ptr += asgeojson_geom_buf(subgeom, ptr, NULL, precision);
+		if (i) stringbuffer_append_char(sb, ',');
+		asgeojson_geometry(sb, col->geoms[i], &subopts);
 	}
 
-	ptr += sprintf(ptr, "]}");
-
-	return (ptr - output);
-}
-
-static lwvarlena_t *
-asgeojson_collection(const LWCOLLECTION *col, const char *srs, GBOX *bbox, int precision)
-{
-	uint32_t size = asgeojson_collection_size(col, srs, bbox, precision);
-	lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
-	size = asgeojson_collection_buf(col, srs, output->data, bbox, precision);
-	LWSIZE_SET(output->size, size + LWVARHDRSZ);
-	return output;
+	stringbuffer_append_len(sb, "]}", 2);
+	return;
 }
 
-
-
-static size_t
-asgeojson_geom_size(const LWGEOM *geom, GBOX *bbox, int precision)
+static void
+asgeojson_geometry(stringbuffer_t *sb, const LWGEOM *geom, const geojson_opts *opts)
 {
 	switch (geom->type)
 	{
 	case POINTTYPE:
-		return asgeojson_point_size((LWPOINT *)geom, NULL, bbox, precision);
-	case LINETYPE:
-		return asgeojson_line_size((LWLINE *)geom, NULL, bbox, precision);
-	case TRIANGLETYPE:
-		return asgeojson_triangle_size((LWTRIANGLE *)geom, NULL, bbox, precision);
-	case POLYGONTYPE:
-		return asgeojson_poly_size((LWPOLY *)geom, NULL, bbox, precision);
-	case MULTIPOINTTYPE:
-		return asgeojson_multipoint_size((LWMPOINT *)geom, NULL, bbox, precision);
-	case MULTILINETYPE:
-		return asgeojson_multiline_size((LWMLINE *)geom, NULL, bbox, precision);
-	case MULTIPOLYGONTYPE:
-		return asgeojson_multipolygon_size((LWMPOLY *)geom, NULL, bbox, precision);
-	default:
-		lwerror("GeoJson: geometry not supported.");
-		return 0;
-	}
-}
-
-
-static size_t
-asgeojson_geom_buf(const LWGEOM *geom, char *output, GBOX *bbox, int precision)
-{
-	int type = geom->type;
-	char *ptr=output;
-
-	switch (type)
-	{
-	case POINTTYPE:
-		ptr += asgeojson_point_buf((LWPOINT*)geom, NULL, ptr, bbox, precision);
+		asgeojson_point(sb, (LWPOINT*)geom, opts);
 		break;
-
 	case LINETYPE:
-		ptr += asgeojson_line_buf((LWLINE*)geom, NULL, ptr, bbox, precision);
+		asgeojson_line(sb, (LWLINE*)geom, opts);
 		break;
-
 	case POLYGONTYPE:
-		ptr += asgeojson_poly_buf((LWPOLY*)geom, NULL, ptr, bbox, precision);
-		break;
-
-	case TRIANGLETYPE:
-		ptr += asgeojson_triangle_buf((LWTRIANGLE *)geom, NULL, ptr, bbox, precision);
+		asgeojson_poly(sb, (LWPOLY*)geom, opts);
 		break;
-
 	case MULTIPOINTTYPE:
-		ptr += asgeojson_multipoint_buf((LWMPOINT*)geom, NULL, ptr, bbox, precision);
+		asgeojson_multipoint(sb, (LWMPOINT*)geom, opts);
 		break;
-
 	case MULTILINETYPE:
-		ptr += asgeojson_multiline_buf((LWMLINE*)geom, NULL, ptr, bbox, precision);
+		asgeojson_multiline(sb, (LWMLINE*)geom, opts);
 		break;
-
 	case MULTIPOLYGONTYPE:
-		ptr += asgeojson_multipolygon_buf((LWMPOLY*)geom, NULL, ptr, bbox, precision);
+		asgeojson_multipolygon(sb, (LWMPOLY*)geom, opts);
+		break;
+	case TRIANGLETYPE:
+		asgeojson_triangle(sb, (LWTRIANGLE *)geom, opts);
+		break;
+	case TINTYPE:
+	case COLLECTIONTYPE:
+		if (opts->isCollectionElement) {
+			lwerror("GeoJson: geometry not supported.");
+		}
+		asgeojson_collection(sb, (LWCOLLECTION*)geom, opts);
 		break;
-
 	default:
-		if (bbox) lwfree(bbox);
-		lwerror("GeoJson: geometry not supported.");
+		lwerror("lwgeom_to_geojson: '%s' geometry type not supported", lwtype_name(geom->type));
 	}
-
-	return (ptr-output);
 }
 
-static size_t
-pointArray_to_geojson(POINTARRAY *pa, char *output, int precision)
+/**
+ * Takes a GEOMETRY and returns a GeoJson representation
+ */
+lwvarlena_t *
+lwgeom_to_geojson(const LWGEOM *geom, const char *srs, int precision, int has_bbox)
 {
-	char *ptr = output;
+	GBOX static_bbox = {0};
+	geojson_opts opts;
+	stringbuffer_t sb;
 
-	if (!FLAGS_GET_Z(pa->flags))
-	{
-		for (uint32_t i = 0; i < pa->npoints; i++)
-		{
-			if (i)
-			{
-				*ptr = ',';
-				ptr++;
-			}
-			const POINT2D *pt = getPoint2d_cp(pa, i);
-
-			*ptr = '[';
-			ptr++;
-			ptr += lwprint_double(pt->x, precision, ptr);
-			*ptr = ',';
-			ptr++;
-			ptr += lwprint_double(pt->y, precision, ptr);
-			*ptr = ']';
-			ptr++;
-		}
-	}
-	else
+	memset(&opts, 0, sizeof(opts));
+	opts.precision = precision;
+	opts.hasz = FLAGS_GET_Z(geom->flags);
+	opts.srs = srs;
+
+	if (has_bbox)
 	{
-		for (uint32_t i = 0; i < pa->npoints; i++)
-		{
-			if (i)
-			{
-				*ptr = ',';
-				ptr++;
-			}
-
-			const POINT3D *pt = getPoint3d_cp(pa, i);
-			*ptr = '[';
-			ptr++;
-			ptr += lwprint_double(pt->x, precision, ptr);
-			*ptr = ',';
-			ptr++;
-			ptr += lwprint_double(pt->y, precision, ptr);
-			*ptr = ',';
-			ptr++;
-			ptr += lwprint_double(pt->z, precision, ptr);
-			*ptr = ']';
-			ptr++;
-		}
+		/* Whether these are geography or geometry,
+		   the GeoJSON expects a cartesian bounding box */
+		lwgeom_calculate_gbox_cartesian(geom, &static_bbox);
+		opts.bbox = &static_bbox;
 	}
-	*ptr = '\0';
-
-	return (ptr-output);
-}
-
-/**
- * Returns maximum size of rendered pointarray in bytes.
- */
-static size_t
-pointArray_geojson_size(POINTARRAY *pa, int precision)
-{
-	if (FLAGS_NDIMS(pa->flags) == 2)
-		return (OUT_MAX_BYTES_DOUBLE + precision + sizeof(",")) * 2 * pa->npoints + sizeof(",[]");
 
-	return (OUT_MAX_BYTES_DOUBLE + precision + sizeof(",,")) * 3 * pa->npoints + sizeof(",[]");
+	/* To avoid taking a copy of the output, we make */
+	/* space for the VARLENA header before starting to */
+	/* serialize the geom */
+	stringbuffer_init_varlena(&sb);
+	/* Now serialize the geometry */
+	asgeojson_geometry(&sb, geom, &opts);
+	/* Leave the initially allocated buffer in place */
+	/* and write the varlena_t metadata into the slot we */
+	/* left at the start */
+	return stringbuffer_getvarlena(&sb);
 }
diff --git a/liblwgeom/stringbuffer.c b/liblwgeom/stringbuffer.c
index d71d5f1ec..0c7101997 100644
--- a/liblwgeom/stringbuffer.c
+++ b/liblwgeom/stringbuffer.c
@@ -56,6 +56,15 @@ stringbuffer_init(stringbuffer_t *s)
 	stringbuffer_init_with_size(s, STRINGBUFFER_STARTSIZE);
 }
 
+void
+stringbuffer_init_varlena(stringbuffer_t *s)
+{
+	stringbuffer_init_with_size(s, STRINGBUFFER_STARTSIZE + LWVARHDRSZ);
+	/* Zero out LWVARHDRSZ bytes at the front of the buffer */
+	stringbuffer_append_len(s, "\0\0\0\0\0\0\0\0", LWVARHDRSZ);
+}
+
+
 /**
 * Allocate a new stringbuffer_t. Use stringbuffer_destroy to free.
 */
@@ -130,6 +139,14 @@ stringbuffer_getstringcopy(stringbuffer_t *s)
 	return str;
 }
 
+lwvarlena_t *
+stringbuffer_getvarlena(stringbuffer_t *s)
+{
+	lwvarlena_t *output = (lwvarlena_t *)(s->str_start);
+	LWSIZE_SET(output->size, (s->str_end - s->str_start));
+	return output;
+}
+
 lwvarlena_t *
 stringbuffer_getvarlenacopy(stringbuffer_t *s)
 {
diff --git a/liblwgeom/stringbuffer.h b/liblwgeom/stringbuffer.h
index a0324f6a5..aa95ef4ca 100644
--- a/liblwgeom/stringbuffer.h
+++ b/liblwgeom/stringbuffer.h
@@ -47,6 +47,7 @@ stringbuffer_t;
 extern stringbuffer_t *stringbuffer_create_with_size(size_t size);
 extern stringbuffer_t *stringbuffer_create(void);
 extern void stringbuffer_init(stringbuffer_t *s);
+extern void stringbuffer_init_varlena(stringbuffer_t *s);
 extern void stringbuffer_release(stringbuffer_t *s);
 extern void stringbuffer_destroy(stringbuffer_t *sb);
 extern void stringbuffer_clear(stringbuffer_t *sb);
@@ -56,6 +57,7 @@ extern int stringbuffer_aprintf(stringbuffer_t *sb, const char *fmt, ...);
 extern const char *stringbuffer_getstring(stringbuffer_t *sb);
 extern char *stringbuffer_getstringcopy(stringbuffer_t *sb);
 extern lwvarlena_t *stringbuffer_getvarlenacopy(stringbuffer_t *s);
+extern lwvarlena_t * stringbuffer_getvarlena(stringbuffer_t *s);
 extern int stringbuffer_getlength(stringbuffer_t *sb);
 extern char stringbuffer_lastchar(stringbuffer_t *s);
 extern int stringbuffer_trim_trailing_white(stringbuffer_t *s);
@@ -112,4 +114,14 @@ stringbuffer_append_double(stringbuffer_t *s, double d, int precision)
 	stringbuffer_makeroom(s, OUT_MAX_BYTES_DOUBLE);
 	s->str_end += lwprint_double(d, precision, s->str_end);
 }
+
+inline static void
+stringbuffer_append_char(stringbuffer_t *s, char c)
+{
+	stringbuffer_makeroom(s, 1);
+	*(s->str_end) = c;
+	s->str_end++;
+}
+
+
 #endif /* _STRINGBUFFER_H */

-----------------------------------------------------------------------

Summary of changes:
 liblwgeom/lwout_geojson.c | 868 +++++++++++++---------------------------------
 liblwgeom/stringbuffer.c  |  17 +
 liblwgeom/stringbuffer.h  |  12 +
 3 files changed, 273 insertions(+), 624 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list