32 #include "../postgis_config.h" 59 LWDEBUG(2,
"lwgeom_has_arc called.");
80 for (i=0; i<col->
ngeoms; i++)
95 static double interpolate_arc(
double angle,
double a1,
double a2,
double a3,
double zm1,
double zm2,
double zm3)
97 LWDEBUGF(4,
"angle %.05g a1 %.05g a2 %.05g a3 %.05g zm1 %.05g zm2 %.05g zm3 %.05g",angle,a1,a2,a3,zm1,zm2,zm3);
102 return zm1 + (zm2-zm1) * (angle-a1) / (a2-a1);
104 return zm2 + (zm3-zm2) * (angle-a2) / (a3-a2);
110 return zm1 + (zm2-zm1) * (a1-angle) / (a1-a2);
112 return zm2 + (zm3-zm2) * (a2-angle) / (a2-a3);
148 double angle_shift = 0;
149 double a1, a2, a3, angle;
152 int points_added = 0;
155 LWDEBUG(2,
"lwarc_linearize called.");
157 LWDEBUGF(2,
" curve is CIRCULARSTRING(%.15g %.15f, %.15f %.15f, %.15f %15f)",
158 t1->
x, t1->
y, t2->
x, t2->
y, t3->
x, t3->
y);
162 LWDEBUGF(2,
" p2 side is %d", p2_side);
177 LWDEBUGF(2,
" center is POINT(%.15g %.15g) - radius:%g", center.
x, center.
y, radius);
180 if ( p1->
x == p3->
x && p1->
y == p3->
y )
184 if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
196 int perQuad = rint(tol);
198 if ( perQuad != tol )
200 lwerror(
"lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
205 lwerror(
"lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
208 increment = fabs(M_PI_2 / perQuad);
209 LWDEBUGF(2,
"lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);
214 double halfAngle, maxErr;
217 lwerror(
"lwarc_linearize: max deviation must be bigger than 0, got %.15g", tol);
246 if ( maxErr > radius * 2 )
249 LWDEBUGF(2,
"lwarc_linearize: tolerance %g is too big, " 250 "using arc-max 2 * radius == %g", tol, maxErr);
253 halfAngle = acos( 1.0 - maxErr / radius );
256 if ( halfAngle != 0 )
break;
257 LWDEBUGF(2,
"lwarc_linearize: tolerance %g is too small for this arc" 258 " to compute approximation angle, doubling it", maxErr);
261 increment = 2 * halfAngle;
262 LWDEBUGF(2,
"lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
267 if ( increment <= 0 )
269 lwerror(
"lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
275 lwerror(
"lwarc_linearize: unsupported tolerance type %d", tolerance_type);
280 a1 = atan2(p1->
y - center.
y, p1->
x - center.
x);
281 a2 = atan2(p2->
y - center.
y, p2->
x - center.
x);
282 a3 = atan2(p3->
y - center.
y, p3->
x - center.
x);
284 LWDEBUGF(2,
"lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)",
285 a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI);
287 if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC )
290 double angle = clockwise ? a1 - a3 : a3 - a1;
291 if ( angle < 0 ) angle += M_PI * 2;
292 LWDEBUGF(2,
"lwarc_linearize SYMMETRIC requested - total angle %g deg",
297 int steps = trunc(angle / increment);
299 double angle_reminder = angle - ( increment * steps );
300 angle_shift = angle_reminder / 2.0;
302 LWDEBUGF(2,
"lwarc_linearize RETAIN_ANGLE operation requested - " 303 "total angle %g, steps %d, increment %g, reminder %g",
304 angle * 180 / M_PI, steps, increment * 180 / M_PI,
305 angle_reminder * 180 / M_PI);
310 int segs = ceil(angle / increment);
312 increment = angle/segs;
314 LWDEBUGF(2,
"lwarc_linearize SYMMETRIC operation requested - " 315 "total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d - I:%g",
316 angle*180/M_PI, p1->
x, p1->
y, center.
x, center.
y, p3->
x, p3->
y,
317 segs, increment*180/M_PI);
324 LWDEBUG(2,
" Clockwise sweep");
336 LWDEBUG(2,
" Counterclockwise sweep");
347 increment = fabs(increment);
348 a3 = a1 + 2.0 * M_PI;
353 LWDEBUGF(2,
"lwarc_linearize angle_shift:%g, increment:%g",
354 angle_shift * 180/M_PI, increment * 180/M_PI);
357 const int capacity = 8;
367 if ( angle_shift ) angle_shift -= increment;
368 LWDEBUGF(2,
"a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d",
369 a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise);
370 for ( angle = a1 + increment + angle_shift; clockwise ? angle > a3 : angle < a3; angle += increment )
372 LWDEBUGF(2,
" SA: %g ( %g deg )", angle, angle*180/M_PI);
373 pt.
x = center.
x + radius * cos(angle);
374 pt.
y = center.
y + radius * sin(angle);
393 for ( i=pa->
npoints; i>0; i-- ) {
428 LWDEBUGF(3,
"lwcircstring_linearize: arc ending at point %d", i);
434 ret =
lwarc_linearize(ptarray, &p1, &p2, &p3, tol, tolerance_type, flags);
437 LWDEBUGF(3,
"lwcircstring_linearize: generated %d points", ptarray->
npoints);
441 LWDEBUG(3,
"lwcircstring_linearize: points are colinear, returning curve points as line");
443 for (j = i - 2 ; j < i ; j++)
477 POINTARRAY *ptarray = NULL, *ptarray_out = NULL;
482 LWDEBUG(2,
"lwcompound_stroke called.");
486 for (i = 0; i < icompound->
ngeoms; i++)
488 geom = icompound->
geoms[i];
510 lwerror(
"Unsupported geometry type %d found.",
547 LWDEBUG(2,
"lwcurvepoly_linearize called.");
551 for (i = 0; i < curvepoly->
nrings; i++)
553 tmp = curvepoly->
rings[i];
573 lwerror(
"Invalid ring type found in CurvePoly.");
611 for (i = 0; i < mcurve->
ngeoms; i++)
628 lwerror(
"Unsupported geometry found in MultiCurve.");
657 LWDEBUG(2,
"lwmsurface_linearize called.");
661 for (i = 0; i < msurface->
ngeoms; i++)
663 tmp = msurface->
geoms[i];
672 for (j = 0; j < poly->
nrings; j++)
701 LWDEBUG(2,
"lwcollection_linearize called.");
705 for (i=0; i<collection->
ngeoms; i++)
707 tmp = collection->
geoms[i];
787 double dot = (ab.
x * cb.
x + ab.
y * cb.
y);
788 double cross = (ab.
x * cb.
y - ab.
y * cb.
x);
790 double alpha = atan2(cross, dot);
807 double b_distance, diff;
814 diff = fabs(radius - b_distance);
815 LWDEBUGF(4,
"circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius);
826 diff = fabs(angle1 - angle2);
827 LWDEBUGF(4,
" angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff);
835 if ( b_side != a2_side )
847 LWDEBUGF(4,
"srid=%d, start=%d, end=%d", srid, start, end);
848 for( i = start; i < end + 2; i++ )
862 LWDEBUGF(4,
"srid=%d, start=%d, end=%d", srid, start, end);
875 LWDEBUGF(4,
"srid=%d, is_arc=%d, start=%d, end=%d", srid, is_arc, start, end);
896 const unsigned int min_quad_edges = 2;
900 lwerror(
"pta_unstroke called with null pointarray");
910 lwerror(
"pta_unstroke needs implementation for npoints < 4");
914 num_edges = points->
npoints - 1;
915 edges_in_arcs =
lwalloc(num_edges + 1);
916 memset(edges_in_arcs, 0, num_edges + 1);
920 while( i < num_edges-2 )
922 unsigned int arc_edges;
923 double num_quadrants;
931 memcpy(&first, &a1,
sizeof(
POINT4D));
933 for( j = i+3; j < num_edges+1; j++ )
941 LWDEBUGF(4,
"pt_continues_arc #%d", current_arc);
943 for ( k = j-1; k > j-4; k-- )
944 edges_in_arcs[k] = current_arc;
949 LWDEBUG(4,
"pt_continues_arc = false");
954 memcpy(&a1, &a2,
sizeof(
POINT4D));
955 memcpy(&a2, &a3,
sizeof(
POINT4D));
956 memcpy(&a3, &b,
sizeof(
POINT4D));
965 arc_edges = j - 1 - i;
966 LWDEBUGF(4,
"arc defined by %d edges found", arc_edges);
967 if ( first.
x == b.
x && first.
y == b.
y ) {
975 if ( p2_side >= 0 ) angle = -angle;
977 if ( angle < 0 ) angle = 2 * M_PI + angle;
978 num_quadrants = ( 4 * angle ) / ( 2 * M_PI );
979 LWDEBUGF(4,
"arc angle (%g %g, %g %g, %g %g) is %g (side is %d), quandrants:%g", first.
x, first.
y, center.
x, center.
y, b.
x, b.
y, angle, p2_side, num_quadrants);
982 if ( arc_edges < min_quad_edges * num_quadrants ) {
983 LWDEBUGF(4,
"Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants);
984 for ( k = j-1; k >= i; k-- )
985 edges_in_arcs[k] = 0;
993 edges_in_arcs[i] = 0;
998 #if POSTGIS_DEBUG_LEVEL > 3 1000 char *edgestr =
lwalloc(num_edges+1);
1001 for ( i = 0; i < num_edges; i++ )
1003 if ( edges_in_arcs[i] )
1004 edgestr[i] = 48 + edges_in_arcs[i];
1008 edgestr[num_edges] = 0;
1009 LWDEBUGF(3,
"edge pattern %s", edgestr);
1015 edge_type = edges_in_arcs[0];
1017 for( i = 1; i < num_edges; i++ )
1019 if( edge_type != edges_in_arcs[i] )
1024 edge_type = edges_in_arcs[i];
1030 end = num_edges - 1;
1034 if ( outcol->
ngeoms == 1 )
1047 LWDEBUG(2,
"lwline_unstroke called.");
1057 int i, hascurve = 0;
1059 LWDEBUG(2,
"lwpolygon_unstroke called.");
1062 for (i=0; i<poly->
nrings; i++)
1072 for (i=0; i<poly->
nrings; i++)
1086 int i, hascurve = 0;
1088 LWDEBUG(2,
"lwmline_unstroke called.");
1091 for (i=0; i<mline->
ngeoms; i++)
1101 for (i=0; i<mline->
ngeoms; i++)
1114 int i, hascurve = 0;
1116 LWDEBUG(2,
"lwmpoly_unstroke called.");
1119 for (i=0; i<mpoly->
ngeoms; i++)
1129 for (i=0; i<mpoly->
ngeoms; i++)
1141 LWDEBUG(2,
"lwgeom_unstroke called.");
void ptarray_set_point4d(POINTARRAY *pa, int n, const POINT4D *p4d)
double lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result)
Determines the center of the circle defined by the three given points.
static LWMLINE * lwmcurve_linearize(const LWMCURVE *mcurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
LWGEOM * lwgeom_unstroke(const LWGEOM *geom)
POINTARRAY * ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance)
static double lw_arc_angle(const POINT2D *a, const POINT2D *b, const POINT2D *c)
Return ABC angle in radians TODO: move to lwalgorithm.
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
LWCOLLECTION * lwcollection_construct(uint8_t type, int srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Tolerance expresses the maximum angle between the radii generating approximation line vertices...
LWLINE * lwline_clone(const LWLINE *lwgeom)
LWGEOM * lwcircstring_as_lwgeom(const LWCIRCSTRING *obj)
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
void ptarray_free(POINTARRAY *pa)
static LWLINE * lwcompound_linearize(const LWCOMPOUND *icompound, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
void lwline_free(LWLINE *line)
static LWPOLY * lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
#define LWDEBUG(level, msg)
#define POLYHEDRALSURFACETYPE
static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b)
Returns LW_TRUE if b is on the arc formed by a1/a2/a3, but not within that portion already described ...
static LWMPOLY * lwmsurface_linearize(const LWMSURFACE *msurface, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
LWGEOM * pta_unstroke(const POINTARRAY *points, int type, int srid)
LW_LINEARIZE_TOLERANCE_TYPE
Semantic of the tolerance argument passed to lwcurve_linearize.
static LWGEOM * circstring_from_pa(const POINTARRAY *pa, int srid, int start, int end)
double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
The old function nessecary for ptarray_segmentize2d in ptarray.c.
Tolerance expresses the maximum distance between an arbitrary point on the curve and the closest poin...
LWPOLY * lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
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)
Segmentize an arc.
LWGEOM * lwpolygon_unstroke(const LWPOLY *poly)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE, then a duplicate point will not be added.
LWPOLY * lwpoly_construct(int srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
#define EPSILON_SQLMM
Tolerance used to determine equality.
#define LW_TRUE
Return types for functions with status returns.
LWLINE * lwline_construct(int srid, GBOX *bbox, POINTARRAY *points)
LWLINE * lwcompound_stroke(const LWCOMPOUND *icompound, uint32_t perQuad)
static LWGEOM * geom_from_pa(const POINTARRAY *pa, int srid, int is_arc, int start, int end)
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
static LWLINE * lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
#define FLAGS_GET_Z(flags)
Macros for manipulating the 'flags' byte.
static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
LWCIRCSTRING * lwcircstring_construct(int srid, GBOX *bbox, POINTARRAY *points)
Symmetric linearization means that the output vertices would be the same no matter the order of the p...
Tolerance expresses the number of segments to use for each quarter of circle (quadrant).
int ptarray_has_m(const POINTARRAY *pa)
int lwgeom_has_arc(const LWGEOM *geom)
LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
int ptarray_remove_point(POINTARRAY *pa, int where)
Remove a point from an existing POINTARRAY.
LWGEOM * lwline_unstroke(const LWLINE *line)
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
#define FLAGS_GET_M(flags)
Retain angle instructs the engine to try its best to retain the requested angle between generating ra...
static LWGEOM * linestring_from_pa(const POINTARRAY *pa, int srid, int start, int end)
void lwcollection_free(LWCOLLECTION *col)
static LWCOLLECTION * lwcollection_linearize(const LWCOLLECTION *collection, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
int ptarray_has_z(const POINTARRAY *pa)
void * lwalloc(size_t size)
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
#define LWDEBUGF(level, msg,...)
#define FLAGS_NDIMS(flags)
LWGEOM * lwmpolygon_unstroke(const LWMPOLY *mpoly)
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
int getPoint4d_p(const POINTARRAY *pa, int n, POINT4D *point)
LWGEOM * lwmline_unstroke(const LWMLINE *mline)
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)