From e7ddc8d3ed6558c920fb65ba0f5ba1d4993e5d65 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Mon, 30 Nov 2015 16:31:18 +0000 Subject: [PATCH] #2232, avoid accumulated error in SVG rounding git-svn-id: http://svn.osgeo.org/postgis/trunk@14457 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/lwout_svg.c | 80 ++++++++++++++++++++++++++-------------- regress/tickets.sql | 2 + regress/tickets_expected | 1 + 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/liblwgeom/lwout_svg.c b/liblwgeom/lwout_svg.c index e2194da87..9674a4fbb 100644 --- a/liblwgeom/lwout_svg.c +++ b/liblwgeom/lwout_svg.c @@ -547,54 +547,78 @@ pointArray_svg_rel(POINTARRAY *pa, char *output, int close_ring, int precision) { int i, end; char *ptr; - char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; - char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; - POINT2D pt, lpt; + char sx[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; + char sy[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; + const POINT2D *pt; + + double f = 1.0; + double dx, dy, x, y, accum_x, accum_y; ptr = output; + if (precision >= 0) + { + f = pow(10, precision); + } + if (close_ring) end = pa->npoints; else end = pa->npoints - 1; /* Starting point */ - getPoint2d_p(pa, 0, &pt); + pt = getPoint2d_cp(pa, 0); - if (fabs(pt.x) < OUT_MAX_DOUBLE) - sprintf(x, "%.*f", precision, pt.x); + x = round(pt->x*f)/f; + y = round(pt->y*f)/f; + + if (fabs(x) < OUT_MAX_DOUBLE) + sprintf(sx, "%.*f", precision, x); else - sprintf(x, "%g", pt.x); - trim_trailing_zeros(x); + sprintf(sx, "%g", x); + trim_trailing_zeros(sx); - if (fabs(pt.y) < OUT_MAX_DOUBLE) - sprintf(y, "%.*f", precision, fabs(pt.y) ? pt.y * -1 : pt.y); + if (fabs(y) < OUT_MAX_DOUBLE) + sprintf(sy, "%.*f", precision, fabs(y) ? y * -1 : y); else - sprintf(y, "%g", fabs(pt.y) ? pt.y * -1 : pt.y); - trim_trailing_zeros(y); + sprintf(sy, "%g", fabs(y) ? y * -1 : y); + trim_trailing_zeros(sy); - ptr += sprintf(ptr,"%s %s l", x, y); + ptr += sprintf(ptr,"%s %s l", sx, sy); + + /* accum */ + accum_x = x; + accum_y = y; /* All the following ones */ for (i=1 ; i < end ; i++) { - lpt = pt; - - getPoint2d_p(pa, i, &pt); - if (fabs(pt.x -lpt.x) < OUT_MAX_DOUBLE) - sprintf(x, "%.*f", precision, pt.x -lpt.x); + // lpt = pt; + + pt = getPoint2d_cp(pa, i); + + x = round(pt->x*f)/f; + y = round(pt->y*f)/f; + dx = x - accum_x; + dy = y - accum_y; + + if (fabs(dx) < OUT_MAX_DOUBLE) + sprintf(sx, "%.*f", precision, dx); else - sprintf(x, "%g", pt.x -lpt.x); - trim_trailing_zeros(x); + sprintf(sx, "%g", dx); + trim_trailing_zeros(sx); /* SVG Y axis is reversed, an no need to transform 0 into -0 */ - if (fabs(pt.y -lpt.y) < OUT_MAX_DOUBLE) - sprintf(y, "%.*f", precision, - fabs(pt.y -lpt.y) ? (pt.y - lpt.y) * -1: (pt.y - lpt.y)); + if (fabs(dy) < OUT_MAX_DOUBLE) + sprintf(sy, "%.*f", precision, + fabs(dy) ? dy * -1: dy); else - sprintf(y, "%g", - fabs(pt.y -lpt.y) ? (pt.y - lpt.y) * -1: (pt.y - lpt.y)); - trim_trailing_zeros(y); - - ptr += sprintf(ptr," %s %s", x, y); + sprintf(sy, "%g", + fabs(dy) ? dy * -1: dy); + trim_trailing_zeros(sy); + + accum_x += dx; + accum_y += dy; + + ptr += sprintf(ptr," %s %s", sx, sy); } return (ptr-output); diff --git a/regress/tickets.sql b/regress/tickets.sql index 7a5fdfeea..a9ced6e8a 100644 --- a/regress/tickets.sql +++ b/regress/tickets.sql @@ -822,6 +822,8 @@ SELECT '#2110.3', 'POINT(0 0)'::geometry = 'POINT(0 0)'::geometry; SELECT '#2145', round(ST_Length(St_Segmentize(ST_GeographyFromText('LINESTRING(-89.3000030518 28.2000007629,-89.1999969482 89.1999969482,-89.1999969482 89.1999969482)'), 10000))::numeric,0); +SELECT '#2232', ST_AsSVG('LINESTRING(0 0, 0.4 0, 0.8 0, 1.2 0,1.6 0, 2 0)'::geometry,1,0); + -- #2307 -- SELECT '#2307', ST_AsText(ST_SnapToGrid(ST_MakeValid('0106000020E6100000010000000103000000010000000A0000004B7DA956B99844C0DB0790FE8B4D1DC010BA74A9AF9444C049AFFC5B8C4D1DC03FC6CC690D9844C0DD67E5628C4D1DC07117B56B0D9844C0C80ABA67C45E1DC0839166ABAF9444C0387D4568C45E1DC010BA74A9AF9444C049AFFC5B8C4D1DC040C3CD74169444C0362EC0608C4D1DC07C1A3B77169444C0DC3ADB40B2641DC03AAE5F68B99844C0242948DEB1641DC04B7DA956B99844C0DB0790FE8B4D1DC0'::geometry),0.0001)); diff --git a/regress/tickets_expected b/regress/tickets_expected index 2aed1ee13..8a83aeb71 100644 --- a/regress/tickets_expected +++ b/regress/tickets_expected @@ -244,6 +244,7 @@ ERROR: invalid GML representation #2110.2|t #2110.3|t #2145|6792004 +#2232|M 0 0 l 0 0 1 0 0 0 1 0 0 0 #2307|MULTIPOLYGON(((-41.1932 -7.3257,-41.1616 -7.3257,-41.1569 -7.3257,-41.1569 -7.3483,-41.1932 -7.3483,-41.1932 -7.3257),(-41.1616 -7.3257,-41.1879 -7.3257,-41.1879 -7.3425,-41.1616 -7.3425,-41.1616 -7.3257))) #2409|GeometryCollection[B] with 2 elements MultiSurface[] with 2 elements -- 2.50.1