[postgis-tickets] r15430 - Implement extended ST_CurveToLine signature
Sandro Santilli
strk at kbt.io
Mon Jun 19 09:07:00 PDT 2017
Author: strk
Date: 2017-06-19 09:06:59 -0700 (Mon, 19 Jun 2017)
New Revision: 15430
Added:
trunk/liblwgeom/cunit/cu_lwstroke.c
trunk/regress/curvetoline.sql
trunk/regress/curvetoline_expected
Modified:
trunk/doc/reference_processing.xml
trunk/liblwgeom/cunit/Makefile.in
trunk/liblwgeom/cunit/cu_tester.c
trunk/liblwgeom/liblwgeom.h.in
trunk/liblwgeom/liblwgeom_internal.h
trunk/liblwgeom/lwstroke.c
trunk/postgis/lwgeom_sqlmm.c
trunk/postgis/postgis.sql.in
trunk/regress/Makefile.in
trunk/regress/sql-mm-circularstring.sql
Log:
Implement extended ST_CurveToLine signature
Adds lwcurve_linearize function at liblwgeom level.
Turns lwgeom_stroke into a wrapper, keept it for backward compatibility.
Reduces allocations in linearization procedures.
Implements SYMMETRIC and RETAIN_ANGLE flags.
Implements MAX_DEVIATION, MAX_ANGLE and SEGS_PER_QUADRANT configs.
Includes unit and SQL tests.
Includes documentation.
Closes #2464 (maxError configuration is MAX_DEVIATION)
Closes #3772 (balanced output is SYMMETRIC and RETAIN_ANGLE flags)
Document the new ST_CurveToLine signature
Modified: trunk/doc/reference_processing.xml
===================================================================
--- trunk/doc/reference_processing.xml 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/doc/reference_processing.xml 2017-06-19 16:06:59 UTC (rev 15430)
@@ -845,6 +845,13 @@
<paramdef><type>geometry</type> <parameter>curveGeom</parameter></paramdef>
<paramdef><type>integer</type> <parameter>segments_per_qtr_circle</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>geometry <function>ST_CurveToLine</function></funcdef>
+ <paramdef><type>geometry</type> <parameter>curveGeom</parameter></paramdef>
+ <paramdef><type>float</type> <parameter>tolerance</parameter></paramdef>
+ <paramdef><type>integer</type> <parameter>tolerance_type</parameter></paramdef>
+ <paramdef><type>integer</type> <parameter>flags</parameter></paramdef>
+ </funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -852,9 +859,44 @@
<title>Description</title>
<para>Converst a CIRCULAR STRING to regular LINESTRING or CURVEPOLYGON to POLYGON. Useful for outputting to devices that can't support CIRCULARSTRING geometry types</para>
+
<para>Converts a given geometry to a linear geometry.
- Each curved geometry or segment is converted into a linear approximation using the default value of 32 segments per quarter circle</para>
+ Each curved geometry or segment is converted into a linear
+approximation using the given `tolerance` and options (32 segments per
+quadrant and no options by default).</para>
+
+ <para>
+The 'tolerance_type' argument determines interpretation of the
+`tolerance` argument. It can take the following values:
+ <itemizedlist>
+ <listitem>
+ <para>0 (default): Tolerance is max segments per quadrant.</para>
+ </listitem>
+ <listitem>
+ <para>1: Tolerance is max-deviation of line from curve, in source units.</para>
+ </listitem>
+ <listitem>
+ <para>2: Tolerance is max-angle, in radians, between generating radii.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+The 'flags' argument is a bitfield. 0 by default.
+Supported bits are:
+ <itemizedlist>
+ <listitem>
+ <para>1: Symmetric (orientation idependent) output.</para>
+ </listitem>
+ <listitem>
+ <para>2: Retain angle, avoids reducing angles (segment lengths) when producing symmetric output. Has no effect when Symmetric flag is off.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
<para>Availability: 1.2.2?</para>
+ <para>Changed: 2.4.0 added support for max-deviation and max-angle tolerance, and for symmetric output.</para>
+
<para>&sfs_compliant;</para>
<para>&sqlmm_compliant; SQL-MM 3: 7.1.7</para>
<para>&Z_support;</para>
@@ -921,7 +963,19 @@
220244.779251566 150505.61834893,220207.243902439 150496,220187.50360229 150462.657300346,
220197.12195122 150425.12195122,220227 150406)
+-- Ensure approximated line is no further than 20 units away from
+-- original curve, and make the result direction-neutral
+SELECT ST_AsText(ST_CurveToLine(
+ 'CIRCULARSTRING(0 0,100 -100,200 0)'::geometry,
+ 20, -- Tolerance
+ 1, -- Above is max distance between curve and line
+ 1 -- Symmetric flag
+));
+st_astext
+-------------------------------------------------------------------------------------------
+ LINESTRING(0 0,50 -86.6025403784438,150 -86.6025403784439,200 -1.1331077795296e-13,200 0)
+
</programlisting>
</refsection>
Modified: trunk/liblwgeom/cunit/Makefile.in
===================================================================
--- trunk/liblwgeom/cunit/Makefile.in 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/liblwgeom/cunit/Makefile.in 2017-06-19 16:06:59 UTC (rev 15430)
@@ -39,6 +39,7 @@
cu_node.o \
cu_clip_by_rect.o \
cu_libgeom.o \
+ cu_lwstroke.o \
cu_split.o \
cu_stringbuffer.o \
cu_triangulate.o \
Added: trunk/liblwgeom/cunit/cu_lwstroke.c
===================================================================
--- trunk/liblwgeom/cunit/cu_lwstroke.c (rev 0)
+++ trunk/liblwgeom/cunit/cu_lwstroke.c 2017-06-19 16:06:59 UTC (rev 15430)
@@ -0,0 +1,253 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * Copyright (C) 2017 Sandro Santilli <strk at kbt.io>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h> /* for M_PI */
+#include "CUnit/Basic.h"
+#include "CUnit/CUnit.h"
+
+#include "liblwgeom_internal.h"
+#include "cu_tester.h"
+
+
+static LWGEOM* lwgeom_from_text(const char *str)
+{
+ LWGEOM_PARSER_RESULT r;
+ if( LW_FAILURE == lwgeom_parse_wkt(&r, (char*)str, LW_PARSER_CHECK_NONE) )
+ return NULL;
+ return r.geom;
+}
+
+static char* lwgeom_to_text(const LWGEOM *geom, int prec)
+{
+ gridspec grid;
+ LWGEOM *gridded;
+ char *wkt;
+
+ memset(&grid, 0, sizeof(gridspec));
+ grid.xsize = prec;
+ grid.ysize = prec;
+ gridded = lwgeom_grid(geom, &grid);
+
+ wkt = lwgeom_to_wkt(gridded, WKT_ISO, 15, NULL);
+ lwgeom_free(gridded);
+ return wkt;
+}
+
+static void test_lwcurve_linearize(void)
+{
+ LWGEOM *in;
+ LWGEOM *out;
+ char *str;
+ int toltype;
+
+ /***********************************************************
+ *
+ * Segments per quadrant tolerance type
+ *
+ ***********************************************************/
+
+ toltype = LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD;
+
+ /* 2 quadrants arc (180 degrees, PI radians) */
+ in = lwgeom_from_text("CIRCULARSTRING(0 0,100 100,200 0)");
+ /* 2 segment per quadrant */
+ out = lwcurve_linearize(in, 2, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,30 70,100 100,170 70,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* 3 segment per quadrant */
+ out = lwcurve_linearize(in, 3, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,14 50,50 86,100 100,150 86,186 50,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* 3.5 segment per quadrant (invalid) */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, 3.5, toltype, 0);
+ CU_ASSERT( out == NULL );
+ ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: segments per quadrant must be an integer value, got 3.5");
+ lwgeom_free(out);
+ /* -2 segment per quadrant (invalid) */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, -2, toltype, 0);
+ CU_ASSERT( out == NULL );
+ ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: segments per quadrant must be at least 1, got -2");
+ lwgeom_free(out);
+ /* 0 segment per quadrant (invalid) */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, 0, toltype, 0);
+ CU_ASSERT( out == NULL );
+ ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: segments per quadrant must be at least 1, got 0");
+ lwgeom_free(out);
+ lwgeom_free(in);
+
+ /* 1.5 quadrants arc (145 degrees - PI*3/4 radians ) */
+ in = lwgeom_from_text("CIRCULARSTRING(29.2893218813453 70.7106781186548,100 100,200 0)");
+ /* 2 segment per quadrant */
+ out = lwcurve_linearize(in, 2, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,100 100,170 70,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* 3 segment per quadrant - non-symmetric */
+ out = lwcurve_linearize(in, 3, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,74 96,126 96,170 70,196 26,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* 3 segment per quadrant - symmetric */
+ out = lwcurve_linearize(in, 3, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,70 96,116 98,158 80,190 46,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* 3 segment per quadrant - symmetric/retain_angle */
+ out = lwcurve_linearize(in, 3, toltype,
+ LW_LINEARIZE_FLAG_SYMMETRIC |
+ LW_LINEARIZE_FLAG_RETAIN_ANGLE
+ );
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(30 70,62 92,114 100,160 80,192 38,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+
+ lwgeom_free(in);
+
+ /***********************************************************
+ *
+ * Maximum deviation tolerance type
+ *
+ ***********************************************************/
+
+ toltype = LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION;
+
+ in = lwgeom_from_text("CIRCULARSTRING(0 0,100 100,200 0)");
+
+ /* Maximum of 10 units of difference, asymmetric */
+ out = lwcurve_linearize(in, 10, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,38 78,124 98,190 42,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 0 units of difference (invalid) */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, 0, toltype, 0);
+ CU_ASSERT( out == NULL );
+ ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max deviation must be bigger than 0, got 0");
+ /* Maximum of -2 units of difference (invalid) */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, -2, toltype, 0);
+ CU_ASSERT( out == NULL );
+ ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max deviation must be bigger than 0, got -2");
+ /* Maximum of 10 units of difference, symmetric */
+ out = lwcurve_linearize(in, 10, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,30 70,100 100,170 70,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 20 units of difference, asymmetric */
+ out = lwcurve_linearize(in, 20, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,72 96,184 54,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 20 units of difference, symmetric */
+ out = lwcurve_linearize(in, 20, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,50 86,150 86,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 20 units of difference, symmetric/retain angle */
+ out = lwcurve_linearize(in, 20, toltype,
+ LW_LINEARIZE_FLAG_SYMMETRIC |
+ LW_LINEARIZE_FLAG_RETAIN_ANGLE
+ );
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,40 80,160 80,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+
+ lwgeom_free(in);
+
+ /***********************************************************
+ *
+ * Maximum angle tolerance type
+ *
+ ***********************************************************/
+
+ toltype = LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE;
+
+ in = lwgeom_from_text("CIRCULARSTRING(0 0,100 100,200 0)");
+
+ /* Maximum of 45 degrees, asymmetric */
+ out = lwcurve_linearize(in, M_PI / 4.0, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,30 70,100 100,170 70,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 0 degrees (invalid) */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, 0, toltype, 0);
+ CU_ASSERT( out == NULL );
+ ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max angle must be bigger than 0, got 0");
+ /* Maximum of -2 degrees (invalid) */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, -2, toltype, 0);
+ CU_ASSERT( out == NULL );
+ ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max angle must be bigger than 0, got -2");
+ /* Maximum of 360 degrees, just return endpoints... */
+ cu_error_msg_reset();
+ out = lwcurve_linearize(in, M_PI*4, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 70 degrees, asymmetric */
+ out = lwcurve_linearize(in, 70 * M_PI / 180, toltype, 0);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,66 94,176 64,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 70 degrees, symmetric */
+ out = lwcurve_linearize(in, 70 * M_PI / 180, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,50 86,150 86,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+ /* Maximum of 70 degrees, symmetric/retain angle */
+ out = lwcurve_linearize(in, 70 * M_PI / 180, toltype,
+ LW_LINEARIZE_FLAG_SYMMETRIC |
+ LW_LINEARIZE_FLAG_RETAIN_ANGLE
+ );
+ str = lwgeom_to_text(out, 2);
+ ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,42 82,158 82,200 0)");
+ lwfree(str);
+ lwgeom_free(out);
+
+ lwgeom_free(in);
+
+}
+
+
+/*
+** Used by the test harness to register the tests in this file.
+*/
+void lwstroke_suite_setup(void);
+void lwstroke_suite_setup(void)
+{
+ CU_pSuite suite = CU_add_suite("lwstroke", NULL, NULL);
+ PG_ADD_TEST(suite, test_lwcurve_linearize);
+}
Modified: trunk/liblwgeom/cunit/cu_tester.c
===================================================================
--- trunk/liblwgeom/cunit/cu_tester.c 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/liblwgeom/cunit/cu_tester.c 2017-06-19 16:06:59 UTC (rev 15430)
@@ -44,6 +44,7 @@
extern void iterator_suite_setup(void);
extern void twkb_in_suite_setup(void);
extern void libgeom_suite_setup(void);
+extern void lwstroke_suite_setup(void);
extern void measures_suite_setup(void);
extern void effectivearea_suite_setup(void);
extern void minimum_bounding_circle_suite_setup(void);
@@ -93,6 +94,7 @@
iterator_suite_setup,
twkb_in_suite_setup,
libgeom_suite_setup,
+ lwstroke_suite_setup,
measures_suite_setup,
effectivearea_suite_setup,
minimum_bounding_circle_suite_setup,
Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/liblwgeom/liblwgeom.h.in 2017-06-19 16:06:59 UTC (rev 15430)
@@ -2158,13 +2158,80 @@
extern uint8_t* lwgeom_to_twkb_with_idlist(const LWGEOM *geom, int64_t *idlist, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size);
/*******************************************************************************
- * SQLMM internal functions - TODO: Move into separate header files
+ * SQLMM internal functions
******************************************************************************/
int lwgeom_has_arc(const LWGEOM *geom);
LWGEOM *lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad);
LWGEOM *lwgeom_unstroke(const LWGEOM *geom);
+/**
+ * Semantic of the `tolerance` argument passed to
+ * lwcurve_linearize
+ */
+typedef enum {
+ /**
+ * Tolerance expresses the number of segments to use
+ * for each quarter of circle (quadrant). Must be
+ * an integer.
+ */
+ LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD = 0,
+ /**
+ * Tolerance expresses the maximum distance between
+ * an arbitrary point on the curve and the closest
+ * point to it on the resulting approximation, in
+ * cartesian units.
+ */
+ LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION = 1,
+ /**
+ * Tolerance expresses the maximum angle between
+ * the radii generating approximation line vertices,
+ * given in radiuses. A value of 1 would result
+ * in an approximation of a semicircle composed by
+ * 180 segments
+ */
+ LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE = 2
+} LW_LINEARIZE_TOLERANCE_TYPE;
+
+typedef enum {
+ /**
+ * Symmetric linearization means that the output
+ * vertices would be the same no matter the order
+ * of the points defining the input curve.
+ */
+ LW_LINEARIZE_FLAG_SYMMETRIC = 1 << 0,
+
+ /**
+ * Retain angle instructs the engine to try its best
+ * to retain the requested angle between generating
+ * radii (where angle can be given explicitly with
+ * LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE or implicitly
+ * with LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD or
+ * LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION).
+ *
+ * It only makes sense with LW_LINEARIZE_FLAG_SYMMETRIC
+ * which would otherwise reduce the angle as needed to
+ * keep it constant among all radiis so that all
+ * segments are of the same length.
+ *
+ * When this flag is set, the first and last generating
+ * angles (and thus the first and last segments) may
+ * instead be smaller (shorter) than the others.
+ *
+ */
+ LW_LINEARIZE_FLAG_RETAIN_ANGLE = 1 << 1
+} LW_LINEARIZE_FLAGS;
+
+/**
+ * @param geom input geometry
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags bitwise OR of operational flags, see LW_LINEARIZE_FLAGS
+ *
+ * @return a newly allocated LWGEOM
+ */
+extern LWGEOM* lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags);
+
/*******************************************************************************
* GEOS proxy functions on LWGEOM
******************************************************************************/
Modified: trunk/liblwgeom/liblwgeom_internal.h
===================================================================
--- trunk/liblwgeom/liblwgeom_internal.h 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/liblwgeom/liblwgeom_internal.h 2017-06-19 16:06:59 UTC (rev 15430)
@@ -305,7 +305,6 @@
/*
* Segmentization
*/
-LWLINE *lwcircstring_stroke(const LWCIRCSTRING *icurve, uint32_t perQuad);
LWLINE *lwcompound_stroke(const LWCOMPOUND *icompound, uint32_t perQuad);
LWPOLY *lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad);
Modified: trunk/liblwgeom/lwstroke.c
===================================================================
--- trunk/liblwgeom/lwstroke.c 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/liblwgeom/lwstroke.c 2017-06-19 16:06:59 UTC (rev 15430)
@@ -19,6 +19,7 @@
**********************************************************************
*
* Copyright (C) 2001-2006 Refractions Research Inc.
+ * Copyright (C) 2017 Sandro Santilli <strk at kbt.io>
*
**********************************************************************/
@@ -35,10 +36,6 @@
#include "lwgeom_log.h"
-LWMLINE* lwmcurve_stroke(const LWMCURVE *mcurve, uint32_t perQuad);
-LWMPOLY* lwmsurface_stroke(const LWMSURFACE *msurface, uint32_t perQuad);
-LWCOLLECTION* lwcollection_stroke(const LWCOLLECTION *collection, uint32_t perQuad);
-
LWGEOM* pta_unstroke(const POINTARRAY *points, int type, int srid);
LWGEOM* lwline_unstroke(const LWLINE *line);
LWGEOM* lwpolygon_unstroke(const LWPOLY *poly);
@@ -112,8 +109,25 @@
}
}
-static POINTARRAY *
-lwcircle_stroke(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, uint32_t perQuad)
+/**
+ * Segmentize an arc
+ *
+ * @param to POINTARRAY to append segmentized vertices to
+ * @param p1 first point defining the arc
+ * @param p2 second point defining the arc
+ * @param p3 third point defining the arc
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags LW_LINEARIZE_FLAGS
+ *
+ * @return number of points appended (0 if collinear),
+ * or -1 on error (lwerror would be called).
+ */
+static int
+lwarc_linearize(POINTARRAY *to,
+ const POINT4D *p1, const POINT4D *p2, const POINT4D *p3,
+ double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
+ int flags)
{
POINT2D center;
POINT2D *t1 = (POINT2D*)p1;
@@ -124,11 +138,13 @@
int clockwise = LW_TRUE;
double radius; /* Arc radius */
double increment; /* Angle per segment */
+ double angle_shift = 0;
double a1, a2, a3, angle;
- POINTARRAY *pa;
+ POINTARRAY *pa = to;
int is_circle = LW_FALSE;
+ int points_added = 0;
- LWDEBUG(2, "lwcircle_calculate_gbox called.");
+ LWDEBUG(2, "lwarc_linearize called.");
radius = lw_arc_center(t1, t2, t3, ¢er);
p2_side = lw_segment_side(t1, t3, t2);
@@ -139,7 +155,7 @@
/* Negative radius signals straight line, p1/p2/p3 are colinear */
if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
- return NULL;
+ return 0;
/* The side of the p1/p3 line that p2 falls on dictates the sweep
direction from p1 to p3. */
@@ -148,17 +164,94 @@
else
clockwise = LW_FALSE;
- increment = fabs(M_PI_2 / perQuad);
+ if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD )
+ {{
+ int perQuad = rint(tol);
+ // error out if tol != perQuad ? (not-round)
+ if ( perQuad != tol )
+ {
+ lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
+ return -1;
+ }
+ if ( perQuad < 1 )
+ {
+ lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
+ return -1;
+ }
+ increment = fabs(M_PI_2 / perQuad);
+ LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g", perQuad, increment);
+ }}
+ else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION )
+ {{
+ double halfAngle;
+ if ( tol <= 0 )
+ {
+ lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", tol);
+ return -1;
+ }
+ halfAngle = acos( -tol / radius + 1 );
+ increment = 2 * halfAngle;
+ LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g", tol, radius, halfAngle, increment);
+ }}
+ else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE )
+ {
+ increment = tol;
+ if ( increment <= 0 )
+ {
+ lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
+ return -1;
+ }
+ }
+ else
+ {
+ lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type);
+ return LW_FALSE;
+ }
+
/* Angles of each point that defines the arc section */
a1 = atan2(p1->y - center.y, p1->x - center.x);
a2 = atan2(p2->y - center.y, p2->x - center.x);
a3 = atan2(p3->y - center.y, p3->x - center.x);
+ if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC )
+ {
+ if ( flags & LW_LINEARIZE_FLAG_RETAIN_ANGLE )
+ {{
+ /* Total arc angle, in radians */
+ double angle = a3 - a1;
+ /* Number of steps */
+ int steps = floor(angle / increment);
+ /* Angle reminder */
+ double angle_reminder = angle - ( increment * steps );
+ angle_shift = angle_reminder / 2.0;
+
+ LWDEBUGF(2, "lwarc_linearize SYMMETRIC/RETAIN_ANGLE operation requested - "
+ "A:%g - LINESTRING(%g %g,%g %g,%g %g) - R:%g",
+ angle, p1->x, p1->y, center.x, center.y, p3->x, p3->y,
+ angle_reminder);
+ }}
+ else
+ {{
+ /* Total arc angle, in radians */
+ double angle = fabs(a3 - a1);
+ /* Number of segments in output */
+ int segs = ceil(angle / increment);
+ /* Tweak increment to be regular for all the arc */
+ increment = angle/segs;
+
+ LWDEBUGF(2, "lwarc_linearize SYMMETRIC operation requested - "
+ "A:%g - LINESTRING(%g %g,%g %g,%g %g) - S:%d - I:%g",
+ angle, p1->x, p1->y, center.x, center.y, p3->x, p3->y,
+ segs, increment);
+ }}
+ }
+
/* p2 on left side => clockwise sweep */
if ( clockwise )
{
increment *= -1;
+ angle_shift *= -1;
/* Adjust a3 down so we can decrement from a1 to a3 cleanly */
if ( a3 > a1 )
a3 -= 2.0 * M_PI;
@@ -184,58 +277,61 @@
clockwise = LW_FALSE;
}
- /* Initialize point array */
- pa = ptarray_construct_empty(1, 1, 32);
-
/* Sweep from a1 to a3 */
ptarray_append_point(pa, p1, LW_FALSE);
- for ( angle = a1 + increment; clockwise ? angle > a3 : angle < a3; angle += increment )
+ ++points_added;
+ for ( angle = a1 + increment - angle_shift; clockwise ? angle > a3 : angle < a3; angle += increment )
{
pt.x = center.x + radius * cos(angle);
pt.y = center.y + radius * sin(angle);
pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z);
pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m);
ptarray_append_point(pa, &pt, LW_FALSE);
+ ++points_added;
+ angle_shift = 0;
}
- return pa;
+ return points_added;
}
-LWLINE *
-lwcircstring_stroke(const LWCIRCSTRING *icurve, uint32_t perQuad)
+/*
+ * @param icurve input curve
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags see flags in lwarc_linearize
+ *
+ * @return a newly allocated LWLINE
+ */
+static LWLINE *
+lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol,
+ LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
+ int flags)
{
LWLINE *oline;
POINTARRAY *ptarray;
- POINTARRAY *tmp;
uint32_t i, j;
POINT4D p1, p2, p3, p4;
+ int ret;
- LWDEBUGF(2, "lwcircstring_stroke called., dim = %d", icurve->points->flags);
+ LWDEBUGF(2, "lwcircstring_linearize called., dim = %d", icurve->points->flags);
ptarray = ptarray_construct_empty(FLAGS_GET_Z(icurve->points->flags), FLAGS_GET_M(icurve->points->flags), 64);
for (i = 2; i < icurve->points->npoints; i+=2)
{
- LWDEBUGF(3, "lwcircstring_stroke: arc ending at point %d", i);
+ LWDEBUGF(3, "lwcircstring_linearize: arc ending at point %d", i);
getPoint4d_p(icurve->points, i - 2, &p1);
getPoint4d_p(icurve->points, i - 1, &p2);
getPoint4d_p(icurve->points, i, &p3);
- tmp = lwcircle_stroke(&p1, &p2, &p3, perQuad);
- if (tmp)
+ ret = lwarc_linearize(ptarray, &p1, &p2, &p3, tol, tolerance_type, flags);
+ if ( ret > 0 )
{
- LWDEBUGF(3, "lwcircstring_stroke: generated %d points", tmp->npoints);
-
- for (j = 0; j < tmp->npoints; j++)
- {
- getPoint4d_p(tmp, j, &p4);
- ptarray_append_point(ptarray, &p4, LW_TRUE);
- }
- ptarray_free(tmp);
+ LWDEBUGF(3, "lwcircstring_linearize: generated %d points", tmp->npoints);
}
- else
+ else if ( ret == 0 )
{
- LWDEBUG(3, "lwcircstring_stroke: points are colinear, returning curve points as line");
+ LWDEBUG(3, "lwcircstring_linearize: points are colinear, returning curve points as line");
for (j = i - 2 ; j < i ; j++)
{
@@ -243,7 +339,12 @@
ptarray_append_point(ptarray, &p4, LW_TRUE);
}
}
-
+ else
+ {
+ /* An error occurred, lwerror should have been called by now */
+ ptarray_free(ptarray);
+ return NULL;
+ }
}
getPoint4d_p(icurve->points, icurve->points->npoints-1, &p1);
ptarray_append_point(ptarray, &p1, LW_TRUE);
@@ -252,8 +353,18 @@
return oline;
}
-LWLINE *
-lwcompound_stroke(const LWCOMPOUND *icompound, uint32_t perQuad)
+/*
+ * @param icompound input compound curve
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags see flags in lwarc_linearize
+ *
+ * @return a newly allocated LWLINE
+ */
+static LWLINE *
+lwcompound_linearize(const LWCOMPOUND *icompound, double tol,
+ LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
+ int flags)
{
LWGEOM *geom;
POINTARRAY *ptarray = NULL, *ptarray_out = NULL;
@@ -270,7 +381,7 @@
geom = icompound->geoms[i];
if (geom->type == CIRCSTRINGTYPE)
{
- tmp = lwcircstring_stroke((LWCIRCSTRING *)geom, perQuad);
+ tmp = lwcircstring_linearize((LWCIRCSTRING *)geom, tol, tolerance_type, flags);
for (j = 0; j < tmp->points->npoints; j++)
{
getPoint4d_p(tmp->points, j, &p);
@@ -299,16 +410,34 @@
return lwline_construct(icompound->srid, NULL, ptarray_out);
}
-LWPOLY *
-lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
+/* Kept for backward compatibility - TODO: drop */
+LWLINE *
+lwcompound_stroke(const LWCOMPOUND *icompound, uint32_t perQuad)
{
+ return lwcompound_linearize(icompound, perQuad, LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD, 0);
+}
+
+
+/*
+ * @param icompound input curve polygon
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags see flags in lwarc_linearize
+ *
+ * @return a newly allocated LWPOLY
+ */
+static LWPOLY *
+lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol,
+ LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
+ int flags)
+{
LWPOLY *ogeom;
LWGEOM *tmp;
LWLINE *line;
POINTARRAY **ptarray;
int i;
- LWDEBUG(2, "lwcurvepoly_stroke called.");
+ LWDEBUG(2, "lwcurvepoly_linearize called.");
ptarray = lwalloc(sizeof(POINTARRAY *)*curvepoly->nrings);
@@ -317,7 +446,7 @@
tmp = curvepoly->rings[i];
if (tmp->type == CIRCSTRINGTYPE)
{
- line = lwcircstring_stroke((LWCIRCSTRING *)tmp, perQuad);
+ line = lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, tolerance_type, flags);
ptarray[i] = ptarray_clone_deep(line->points);
lwline_free(line);
}
@@ -328,7 +457,7 @@
}
else if (tmp->type == COMPOUNDTYPE)
{
- line = lwcompound_stroke((LWCOMPOUND *)tmp, perQuad);
+ line = lwcompound_linearize((LWCOMPOUND *)tmp, tol, tolerance_type, flags);
ptarray[i] = ptarray_clone_deep(line->points);
lwline_free(line);
}
@@ -343,14 +472,32 @@
return ogeom;
}
-LWMLINE *
-lwmcurve_stroke(const LWMCURVE *mcurve, uint32_t perQuad)
+/* Kept for backward compatibility - TODO: drop */
+LWPOLY *
+lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
{
+ return lwcurvepoly_linearize(curvepoly, perQuad, LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD, 0);
+}
+
+
+/**
+ * @param mcurve input compound curve
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags see flags in lwarc_linearize
+ *
+ * @return a newly allocated LWMLINE
+ */
+static LWMLINE *
+lwmcurve_linearize(const LWMCURVE *mcurve, double tol,
+ LW_LINEARIZE_TOLERANCE_TYPE type,
+ int flags)
+{
LWMLINE *ogeom;
LWGEOM **lines;
int i;
- LWDEBUGF(2, "lwmcurve_stroke called, geoms=%d, dim=%d.", mcurve->ngeoms, FLAGS_NDIMS(mcurve->flags));
+ LWDEBUGF(2, "lwmcurve_linearize called, geoms=%d, dim=%d.", mcurve->ngeoms, FLAGS_NDIMS(mcurve->flags));
lines = lwalloc(sizeof(LWGEOM *)*mcurve->ngeoms);
@@ -359,7 +506,7 @@
const LWGEOM *tmp = mcurve->geoms[i];
if (tmp->type == CIRCSTRINGTYPE)
{
- lines[i] = (LWGEOM *)lwcircstring_stroke((LWCIRCSTRING *)tmp, perQuad);
+ lines[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags);
}
else if (tmp->type == LINETYPE)
{
@@ -367,7 +514,7 @@
}
else if (tmp->type == COMPOUNDTYPE)
{
- lines[i] = (LWGEOM *)lwcompound_stroke((LWCOMPOUND *)tmp, perQuad);
+ lines[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags);
}
else
{
@@ -380,8 +527,18 @@
return ogeom;
}
-LWMPOLY *
-lwmsurface_stroke(const LWMSURFACE *msurface, uint32_t perQuad)
+/**
+ * @param msurface input multi surface
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags see flags in lwarc_linearize
+ *
+ * @return a newly allocated LWMPOLY
+ */
+static LWMPOLY *
+lwmsurface_linearize(const LWMSURFACE *msurface, double tol,
+ LW_LINEARIZE_TOLERANCE_TYPE type,
+ int flags)
{
LWMPOLY *ogeom;
LWGEOM *tmp;
@@ -390,7 +547,7 @@
POINTARRAY **ptarray;
int i, j;
- LWDEBUG(2, "lwmsurface_stroke called.");
+ LWDEBUG(2, "lwmsurface_linearize called.");
polys = lwalloc(sizeof(LWGEOM *)*msurface->ngeoms);
@@ -399,7 +556,7 @@
tmp = msurface->geoms[i];
if (tmp->type == CURVEPOLYTYPE)
{
- polys[i] = (LWGEOM *)lwcurvepoly_stroke((LWCURVEPOLY *)tmp, perQuad);
+ polys[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags);
}
else if (tmp->type == POLYGONTYPE)
{
@@ -416,15 +573,25 @@
return ogeom;
}
-LWCOLLECTION *
-lwcollection_stroke(const LWCOLLECTION *collection, uint32_t perQuad)
+/**
+ * @param collection input geometry collection
+ * @param tol tolerance, semantic driven by tolerance_type
+ * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
+ * @param flags see flags in lwarc_linearize
+ *
+ * @return a newly allocated LWCOLLECTION
+ */
+static LWCOLLECTION *
+lwcollection_linearize(const LWCOLLECTION *collection, double tol,
+ LW_LINEARIZE_TOLERANCE_TYPE type,
+ int flags)
{
LWCOLLECTION *ocol;
LWGEOM *tmp;
LWGEOM **geoms;
int i;
- LWDEBUG(2, "lwcollection_stroke called.");
+ LWDEBUG(2, "lwcollection_linearize called.");
geoms = lwalloc(sizeof(LWGEOM *)*collection->ngeoms);
@@ -434,18 +601,18 @@
switch (tmp->type)
{
case CIRCSTRINGTYPE:
- geoms[i] = (LWGEOM *)lwcircstring_stroke((LWCIRCSTRING *)tmp, perQuad);
+ geoms[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags);
break;
case COMPOUNDTYPE:
- geoms[i] = (LWGEOM *)lwcompound_stroke((LWCOMPOUND *)tmp, perQuad);
+ geoms[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags);
break;
case CURVEPOLYTYPE:
- geoms[i] = (LWGEOM *)lwcurvepoly_stroke((LWCURVEPOLY *)tmp, perQuad);
+ geoms[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags);
break;
case MULTICURVETYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
- geoms[i] = (LWGEOM *)lwcollection_stroke((LWCOLLECTION *)tmp, perQuad);
+ geoms[i] = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)tmp, tol, type, flags);
break;
default:
geoms[i] = lwgeom_clone(tmp);
@@ -457,28 +624,30 @@
}
LWGEOM *
-lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
+lwcurve_linearize(const LWGEOM *geom, double tol,
+ LW_LINEARIZE_TOLERANCE_TYPE type,
+ int flags)
{
LWGEOM * ogeom = NULL;
switch (geom->type)
{
case CIRCSTRINGTYPE:
- ogeom = (LWGEOM *)lwcircstring_stroke((LWCIRCSTRING *)geom, perQuad);
+ ogeom = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)geom, tol, type, flags);
break;
case COMPOUNDTYPE:
- ogeom = (LWGEOM *)lwcompound_stroke((LWCOMPOUND *)geom, perQuad);
+ ogeom = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)geom, tol, type, flags);
break;
case CURVEPOLYTYPE:
- ogeom = (LWGEOM *)lwcurvepoly_stroke((LWCURVEPOLY *)geom, perQuad);
+ ogeom = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)geom, tol, type, flags);
break;
case MULTICURVETYPE:
- ogeom = (LWGEOM *)lwmcurve_stroke((LWMCURVE *)geom, perQuad);
+ ogeom = (LWGEOM *)lwmcurve_linearize((LWMCURVE *)geom, tol, type, flags);
break;
case MULTISURFACETYPE:
- ogeom = (LWGEOM *)lwmsurface_stroke((LWMSURFACE *)geom, perQuad);
+ ogeom = (LWGEOM *)lwmsurface_linearize((LWMSURFACE *)geom, tol, type, flags);
break;
case COLLECTIONTYPE:
- ogeom = (LWGEOM *)lwcollection_stroke((LWCOLLECTION *)geom, perQuad);
+ ogeom = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)geom, tol, type, flags);
break;
default:
ogeom = lwgeom_clone(geom);
@@ -486,6 +655,13 @@
return ogeom;
}
+/* Kept for backward compatibility - TODO: drop */
+LWGEOM *
+lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
+{
+ return lwcurve_linearize(geom, perQuad, LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD, 0);
+}
+
/**
* Return ABC angle in radians
* TODO: move to lwalgorithm
Modified: trunk/postgis/lwgeom_sqlmm.c
===================================================================
--- trunk/postgis/lwgeom_sqlmm.c 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/postgis/lwgeom_sqlmm.c 2017-06-19 16:06:59 UTC (rev 15430)
@@ -62,6 +62,8 @@
* Curve centers are determined by projecting the defining points into the 2d
* plane. Z and M values are assigned by linear interpolation between
* defining points.
+ *
+ * TODO: drop, use ST_CurveToLine instead
*/
PG_FUNCTION_INFO_V1(LWGEOM_curve_segmentize);
Datum LWGEOM_curve_segmentize(PG_FUNCTION_ARGS)
@@ -84,16 +86,43 @@
igeom = lwgeom_from_gserialized(geom);
ogeom = lwgeom_stroke(igeom, perQuad);
lwgeom_free(igeom);
-
+
if (ogeom == NULL)
PG_RETURN_NULL();
-
+
ret = geometry_serialize(ogeom);
lwgeom_free(ogeom);
PG_FREE_IF_COPY(geom, 0);
PG_RETURN_POINTER(ret);
}
+PG_FUNCTION_INFO_V1(ST_CurveToLine);
+Datum ST_CurveToLine(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
+ double tol = PG_GETARG_FLOAT8(1);
+ int toltype = PG_GETARG_INT32(2);
+ int flags = PG_GETARG_INT32(3);
+ GSERIALIZED *ret;
+ LWGEOM *igeom = NULL, *ogeom = NULL;
+
+ POSTGIS_DEBUG(2, "ST_CurveToLine called.");
+
+ POSTGIS_DEBUGF(3, "tol = %g, typ = %d, flg = %d", tol, toltype, flags);
+
+ igeom = lwgeom_from_gserialized(geom);
+ ogeom = lwcurve_linearize(igeom, tol, toltype, flags);
+ lwgeom_free(igeom);
+
+ if (ogeom == NULL)
+ PG_RETURN_NULL();
+
+ ret = geometry_serialize(ogeom);
+ lwgeom_free(ogeom);
+ PG_FREE_IF_COPY(geom, 0);
+ PG_RETURN_POINTER(ret);
+}
+
PG_FUNCTION_INFO_V1(LWGEOM_line_desegmentize);
Datum LWGEOM_line_desegmentize(PG_FUNCTION_ARGS)
{
Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/postgis/postgis.sql.in 2017-06-19 16:06:59 UTC (rev 15430)
@@ -5572,15 +5572,38 @@
--
-- SQL-MM
--
+-- ST_CurveToLine(Geometry geometry, Tolerance float8, ToleranceType integer, Flags integer)
+--
+-- Converts a given geometry to a linear geometry. Each curveed
+-- geometry or segment is converted into a linear approximation using
+-- the given tolerance.
+--
+-- Semantic of tolerance depends on the `toltype` argument, which can be:
+-- 0: Tolerance is number of segments per quadrant
+-- 1: Tolerance is max distance between curve and line
+-- 2: Tolerance is max angle between radii defining line vertices
+--
+-- Supported flags:
+-- 1: Symmetric output (result in same vertices when inverting the curve)
+--
+-- Availability: 2.4.0
+--
+CREATE OR REPLACE FUNCTION ST_CurveToLine(geom geometry, tol float8, toltype integer, flags integer)
+ RETURNS geometry
+ AS 'MODULE_PATHNAME', 'ST_CurveToLine'
+ LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
+--
+-- SQL-MM
+--
-- ST_CurveToLine(Geometry geometry, SegmentsPerQuarter integer)
--
-- Converts a given geometry to a linear geometry. Each curveed
-- geometry or segment is converted into a linear approximation using
-- the given number of segments per quarter circle.
+--
CREATE OR REPLACE FUNCTION ST_CurveToLine(geometry, integer)
- RETURNS geometry
- AS 'MODULE_PATHNAME', 'LWGEOM_curve_segmentize'
- LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
+ RETURNS geometry AS 'SELECT ST_CurveToLine($1, $2::float8, 0, 0)'
+ LANGUAGE 'sql' IMMUTABLE STRICT _PARALLEL;
--
-- SQL-MM
--
@@ -5590,7 +5613,7 @@
-- geometry or segment is converted into a linear approximation using
-- the default value of 32 segments per quarter circle
CREATE OR REPLACE FUNCTION ST_CurveToLine(geometry)
- RETURNS geometry AS 'SELECT ST_CurveToLine($1, 32)'
+ RETURNS geometry AS 'SELECT ST_CurveToLine($1, 32::integer)'
LANGUAGE 'sql' IMMUTABLE STRICT _PARALLEL;
CREATE OR REPLACE FUNCTION ST_HasArc(Geometry geometry)
Modified: trunk/regress/Makefile.in
===================================================================
--- trunk/regress/Makefile.in 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/regress/Makefile.in 2017-06-19 16:06:59 UTC (rev 15430)
@@ -83,6 +83,7 @@
cluster \
concave_hull\
ctors \
+ curvetoline \
dump \
dumppoints \
empty \
Added: trunk/regress/curvetoline.sql
===================================================================
--- trunk/regress/curvetoline.sql (rev 0)
+++ trunk/regress/curvetoline.sql 2017-06-19 16:06:59 UTC (rev 15430)
@@ -0,0 +1,52 @@
+
+
+-- Semantic of tolerance depends on the `toltype` argument, which can be:
+-- 0: Tolerance is number of segments per quadrant
+-- 1: Tolerance is max distance between curve and line
+-- 2: Tolerance is max angle between radii defining line vertices
+--
+-- Supported flags:
+-- 1: Symmetric output (result in same vertices when inverting the curve)
+
+SELECT 'semicircle1', ST_AsText(ST_SnapToGrid(ST_CurveToLine(
+ 'CIRCULARSTRING(0 0,100 -100,200 0)'::geometry,
+ 3, -- Tolerance
+ 0, -- Above is number of segments per quadrant
+ 0 -- no flags
+), 2));
+
+SELECT 'semicircle2', ST_AsText(ST_SnapToGrid(ST_CurveToLine(
+ 'CIRCULARSTRING(0 0,100 -100,200 0)'::geometry,
+ 20, -- Tolerance
+ 1, -- Above is max distance between curve and line
+ 0 -- no flags
+), 2));
+
+SELECT 'semicircle2.sym', ST_AsText(ST_SnapToGrid(ST_CurveToLine(
+ 'CIRCULARSTRING(0 0,100 -100,200 0)'::geometry,
+ 20, -- Tolerance
+ 1, -- Above is max distance between curve and line
+ 1 -- Symmetric flag
+), 2));
+
+SELECT 'semicircle3', ST_AsText(ST_SnapToGrid(ST_CurveToLine(
+ 'CIRCULARSTRING(0 0,100 -100,200 0)'::geometry,
+ radians(40), -- Tolerance
+ 2, -- Above is max angle between generating radii
+ 0 -- no flags
+), 2));
+
+SELECT 'semicircle3.sym', ST_AsText(ST_SnapToGrid(ST_CurveToLine(
+ 'CIRCULARSTRING(0 0,100 -100,200 0)'::geometry,
+ radians(40), -- Tolerance
+ 2, -- Above is max angle between generating radii
+ 1 -- Symmetric flag
+), 2));
+SELECT 'semicircle3.sym.ret', ST_AsText(ST_SnapToGrid(ST_CurveToLine(
+ 'CIRCULARSTRING(0 0,100 -100,200 0)'::geometry,
+ radians(40), -- Tolerance
+ 2, -- Above is max angle between generating radii
+ 3 -- Symmetric and RetainAngle flags
+), 2));
+
+
Added: trunk/regress/curvetoline_expected
===================================================================
--- trunk/regress/curvetoline_expected (rev 0)
+++ trunk/regress/curvetoline_expected 2017-06-19 16:06:59 UTC (rev 15430)
@@ -0,0 +1,6 @@
+semicircle1|LINESTRING(0 0,14 -50,50 -86,100 -100,150 -86,186 -50,200 0)
+semicircle2|LINESTRING(0 0,72 -96,184 -54,200 0)
+semicircle2.sym|LINESTRING(0 0,50 -86,150 -86,200 0)
+semicircle3|LINESTRING(0 0,24 -64,82 -98,150 -86,194 -34,200 0)
+semicircle3.sym|LINESTRING(0 0,20 -58,70 -96,130 -96,180 -58,200 0)
+semicircle3.sym.ret|LINESTRING(0 0,14 -50,66 -94,134 -94,186 -50,200 0)
Modified: trunk/regress/sql-mm-circularstring.sql
===================================================================
--- trunk/regress/sql-mm-circularstring.sql 2017-06-13 13:29:29 UTC (rev 15429)
+++ trunk/regress/sql-mm-circularstring.sql 2017-06-19 16:06:59 UTC (rev 15430)
@@ -233,4 +233,3 @@
-- See http://trac.osgeo.org/postgis/ticket/2410
SELECT 'straight_curve', ST_AsText(ST_CurveToLine(ST_GeomFromEWKT('CIRCULARSTRING(0 0,1 0,2 0,3 0,4 0)')));
-
More information about the postgis-tickets
mailing list