From 9f7e3edaa113373cc9c78c22beb03a00d40f8bbf Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Wed, 17 Oct 2012 11:43:47 +0000 Subject: [PATCH] Do not print more digits than available from lwgeom_to_geojson See http://trac.osgeo.org/postgis/ticket/2051 Adds tests for the ticket cases. git-svn-id: http://svn.osgeo.org/postgis/trunk@10451 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/cunit/cu_out_geojson.c | 17 +++++++ liblwgeom/lwout_geojson.c | 85 ++++++++++++++++++++------------ liblwgeom/lwutil.c | 1 - 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/liblwgeom/cunit/cu_out_geojson.c b/liblwgeom/cunit/cu_out_geojson.c index a3141a0ca..8a13782de 100644 --- a/liblwgeom/cunit/cu_out_geojson.c +++ b/liblwgeom/cunit/cu_out_geojson.c @@ -88,6 +88,23 @@ static void out_geojson_test_precision(void) "{\"type\":\"Point\",\"coordinates\":[1,2]}", NULL, 100, 0); + /* double precision, see http://trac.osgeo.org/postgis/ticket/2051 */ + do_geojson_test( + "POINT(59.99 -59.99)", + "{\"type\":\"Point\",\"coordinates\":[59.99,-59.99]}", + NULL, 15, 0); + + /* small numbers */ + /* NOTE: precision of 300 will be converted to max precision (15) + * and being there no significant digit within that range + * only zeroes will be returned + * See http://trac.osgeo.org/postgis/ticket/2051#comment:11 + */ + do_geojson_test( + "POINT(1E-300 -2E-200)", + "{\"type\":\"Point\",\"coordinates\":[0,-0]}", + NULL, 300, 0); + } diff --git a/liblwgeom/lwout_geojson.c b/liblwgeom/lwout_geojson.c index 3542d8a61..015dc8bc6 100644 --- a/liblwgeom/lwout_geojson.c +++ b/liblwgeom/lwout_geojson.c @@ -659,20 +659,61 @@ asgeojson_geom_buf(const LWGEOM *geom, char *output, GBOX *bbox, int precision) return (ptr-output); } +/* + * Print an ordinate value using at most the given number of decimal digits + * + * The actual number of printed decimal digits may be less than the + * requested ones if out of significant digits. + * + * The function will not write more than maxsize bytes, including the + * terminating NULL. Returns the number of bytes that would have been + * written if there was enough space (excluding terminating NULL). + * So a return of ``bufsize'' or more means that the string was + * truncated and misses a terminating NULL. + * + * TODO: export ? + * + */ +static int +lwprint_double(double d, int maxdd, char *buf, size_t bufsize) +{ + double ad = fabs(d); + int ndd = ad < 1 ? 0 : floor(log10(ad))+1; /* non-decimal digits */ + if (fabs(d) < OUT_MAX_DOUBLE) + { + if ( maxdd > (OUT_MAX_DOUBLE_PRECISION - ndd) ) maxdd -= ndd; + return snprintf(buf, bufsize, "%.*f", maxdd, d); + } + else + { + return snprintf(buf, bufsize, "%g", d); + } +} + + static size_t pointArray_to_geojson(POINTARRAY *pa, char *output, int precision) { int i; char *ptr; - char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; - char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; - char z[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; +#define BUFSIZE OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION + char x[BUFSIZE+1]; + char y[BUFSIZE+1]; + char z[BUFSIZE+1]; assert ( precision <= OUT_MAX_DOUBLE_PRECISION ); + /* Ensure a terminating NULL at the end of buffers + * so that we don't need to check for truncation + * inprint_double */ + x[BUFSIZE] = '\0'; + y[BUFSIZE] = '\0'; + z[BUFSIZE] = '\0'; + ptr = output; + /* TODO: rewrite this loop to be simpler and possibly quicker */ if (!FLAGS_GET_Z(pa->flags)) { for (i=0; inpoints; i++) @@ -680,17 +721,10 @@ pointArray_to_geojson(POINTARRAY *pa, char *output, int precision) POINT2D pt; getPoint2d_p(pa, i, &pt); - if (fabs(pt.x) < OUT_MAX_DOUBLE) - sprintf(x, "%.*f", precision, pt.x); - else - sprintf(x, "%g", pt.x); - trim_trailing_zeros(x); - - if (fabs(pt.y) < OUT_MAX_DOUBLE) - sprintf(y, "%.*f", precision, pt.y); - else - sprintf(y, "%g", pt.y); - trim_trailing_zeros(y); + lwprint_double(pt.x, precision, x, BUFSIZE); + trim_trailing_zeros(x); + lwprint_double(pt.y, precision, y, BUFSIZE); + trim_trailing_zeros(y); if ( i ) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "[%s,%s]", x, y); @@ -703,23 +737,12 @@ pointArray_to_geojson(POINTARRAY *pa, char *output, int precision) POINT4D pt; getPoint4d_p(pa, i, &pt); - if (fabs(pt.x) < OUT_MAX_DOUBLE) - sprintf(x, "%.*f", precision, pt.x); - else - sprintf(x, "%g", pt.x); - trim_trailing_zeros(x); - - if (fabs(pt.y) < OUT_MAX_DOUBLE) - sprintf(y, "%.*f", precision, pt.y); - else - sprintf(y, "%g", pt.y); - trim_trailing_zeros(y); - - if (fabs(pt.z) < OUT_MAX_DOUBLE) - sprintf(z, "%.*f", precision, pt.z); - else - sprintf(z, "%g", pt.z); - trim_trailing_zeros(z); + lwprint_double(pt.x, precision, x, BUFSIZE); + trim_trailing_zeros(x); + lwprint_double(pt.y, precision, y, BUFSIZE); + trim_trailing_zeros(y); + lwprint_double(pt.z, precision, z, BUFSIZE); + trim_trailing_zeros(z); if ( i ) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "[%s,%s,%s]", x, y, z); diff --git a/liblwgeom/lwutil.c b/liblwgeom/lwutil.c index f09de91fc..549e095dc 100644 --- a/liblwgeom/lwutil.c +++ b/liblwgeom/lwutil.c @@ -271,7 +271,6 @@ trim_trailing_zeros(char *str) LWDEBUGF(3, "output: %s", str); } - /* * Returns a new string which contains a maximum of maxlength characters starting * from startpos and finishing at endpos (0-based indexing). If the string is -- 2.50.1