33 #include "../postgis_config.h"
60 LWDEBUG(2,
"lwgeom_has_arc called.");
79 for (i=0; i<col->
ngeoms; i++)
108 static double interpolate_arc(
double angle,
double a1,
double a2,
double a3,
double zm1,
double zm2,
double zm3)
110 LWDEBUGF(4,
"angle %.05g a1 %.05g a2 %.05g a3 %.05g zm1 %.05g zm2 %.05g zm3 %.05g",angle,a1,a2,a3,zm1,zm2,zm3);
115 return zm1 + (zm2-zm1) * (angle-a1) / (a2-a1);
117 return zm2 + (zm3-zm2) * (angle-a2) / (a3-a2);
123 return zm1 + (zm2-zm1) * (a1-angle) / (a1-a2);
125 return zm2 + (zm3-zm2) * (a2-angle) / (a2-a3);
134 int perQuad = rint(tol);
136 if ( perQuad != tol )
138 lwerror(
"lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
143 lwerror(
"lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
146 increment = fabs(M_PI_2 / perQuad);
147 LWDEBUGF(2,
"lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);
157 double increment, halfAngle, maxErr;
158 if ( max_deviation <= 0 )
160 lwerror(
"lwarc_linearize: max deviation must be bigger than 0, got %.15g", max_deviation);
188 maxErr = max_deviation;
189 if ( maxErr > radius * 2 )
193 "lwarc_linearize: tolerance %g is too big, "
194 "using arc-max 2 * radius == %g",
199 halfAngle = acos( 1.0 - maxErr / radius );
202 if ( halfAngle != 0 )
break;
203 LWDEBUGF(2,
"lwarc_linearize: tolerance %g is too small for this arc"
204 " to compute approximation angle, doubling it", maxErr);
207 increment = 2 * halfAngle;
209 "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)",
214 increment * 180 / M_PI);
225 lwerror(
"lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
265 double angle_shift = 0;
269 int points_added = 0;
273 LWDEBUG(2,
"lwarc_linearize called.");
275 LWDEBUGF(2,
" curve is CIRCULARSTRING(%.15g %.15f, %.15f %.15f, %.15f %15f)",
276 t1->
x, t1->
y, t2->
x, t2->
y, t3->
x, t3->
y);
280 LWDEBUGF(2,
" p2 side is %d", p2_side);
295 LWDEBUGF(2,
" center is POINT(%.15g %.15g) - radius:%g", center.
x, center.
y, radius);
298 if ( p1->
x == p3->
x && p1->
y == p3->
y )
302 if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
314 switch(tolerance_type)
326 lwerror(
"lwarc_linearize: unsupported tolerance type %d", tolerance_type);
339 a1 = atan2(p1->
y - center.
y, p1->
x - center.
x);
340 a2 = atan2(p2->
y - center.
y, p2->
x - center.
x);
341 a3 = atan2(p3->
y - center.
y, p3->
x - center.
x);
343 LWDEBUGF(2,
"lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)",
344 a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI);
347 double total_angle = clockwise ? a1 - a3 : a3 - a1;
348 if ( total_angle <= 0 ) total_angle += M_PI * 2;
354 int min_segs = is_circle ? 3 : 2;
355 segments = ceil(total_angle / increment);
356 if (segments < min_segs)
359 increment = total_angle / min_segs;
364 LWDEBUGF(2,
"lwarc_linearize SYMMETRIC requested - total angle %g deg", total_angle * 180 / M_PI);
369 segments = trunc(total_angle / increment);
374 double angle_remainder = total_angle - (increment * segments);
379 angle_shift = angle_remainder / 2.0;
382 "lwarc_linearize RETAIN_ANGLE operation requested - "
383 "total angle %g, steps %d, increment %g, remainder %g",
384 total_angle * 180 / M_PI,
386 increment * 180 / M_PI,
387 angle_remainder * 180 / M_PI);
392 segments = ceil(total_angle / increment);
394 increment = total_angle / segments;
397 "lwarc_linearize SYMMETRIC operation requested - "
398 "total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d - I:%g",
399 total_angle * 180 / M_PI,
407 increment * 180 / M_PI);
414 LWDEBUG(2,
" Clockwise sweep");
426 LWDEBUG(2,
" Counterclockwise sweep");
437 increment = fabs(increment);
438 segments = ceil(total_angle / increment);
442 increment = total_angle / 3;
444 a3 = a1 + 2.0 * M_PI;
450 LWDEBUGF(2,
"lwarc_linearize angle_shift:%g, increment:%g",
451 angle_shift * 180/M_PI, increment * 180/M_PI);
457 const int capacity = 8;
472 int seg_end = segments;
473 if (angle_shift != 0.0)
478 seg_end = segments + 1;
480 LWDEBUGF(2,
"a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d",
481 a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise);
482 for (
int s = seg_start;
s < seg_end;
s++)
484 double angle = a1 + increment *
s + angle_shift;
485 LWDEBUGF(2,
" SA: %g ( %g deg )", angle, angle*180/M_PI);
486 pt.
x = center.
x + radius * cos(angle);
487 pt.
y = center.
y + radius * sin(angle);
505 for ( i=pa->
npoints; i>0; i-- ) {
540 LWDEBUGF(3,
"lwcircstring_linearize: arc ending at point %d", i);
546 ret =
lwarc_linearize(ptarray, &p1, &p2, &p3, tol, tolerance_type, flags);
549 LWDEBUGF(3,
"lwcircstring_linearize: generated %d points", ptarray->
npoints);
553 LWDEBUG(3,
"lwcircstring_linearize: points are colinear, returning curve points as line");
555 for (j = i - 2 ; j < i ; j++)
594 LWDEBUG(2,
"lwcompound_stroke called.");
598 for (i = 0; i < icompound->
ngeoms; i++)
600 geom = icompound->
geoms[i];
651 LWDEBUG(2,
"lwcurvepoly_linearize called.");
655 for (i = 0; i < curvepoly->
nrings; i++)
657 tmp = curvepoly->
rings[i];
677 lwerror(
"Invalid ring type found in CurvePoly.");
715 for (i = 0; i < mcurve->
ngeoms; i++)
732 lwerror(
"Unsupported geometry found in MultiCurve.");
761 LWDEBUG(2,
"lwmsurface_linearize called.");
765 for (i = 0; i < msurface->
ngeoms; i++)
767 tmp = msurface->
geoms[i];
776 for (j = 0; j < poly->
nrings; j++)
805 LWDEBUG(2,
"lwcollection_linearize called.");
809 for (i=0; i<collection->
ngeoms; i++)
811 tmp = collection->
geoms[i];
891 double dot = (ab.
x * cb.
x + ab.
y * cb.
y);
892 double cross = (ab.
x * cb.
y - ab.
y * cb.
x);
894 double alpha = atan2(cross, dot);
911 double b_distance, diff;
918 diff = fabs(radius - b_distance);
919 LWDEBUGF(4,
"circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius);
930 diff = fabs(angle1 - angle2);
931 LWDEBUGF(4,
" angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff);
939 if ( b_side != a2_side )
951 LWDEBUGF(4,
"srid=%d, start=%d, end=%d", srid, start, end);
952 for( i = start; i < end + 2; i++ )
966 LWDEBUGF(4,
"srid=%d, start=%d, end=%d", srid, start, end);
979 LWDEBUGF(4,
"srid=%d, is_arc=%d, start=%d, end=%d", srid, is_arc, start, end);
1000 const unsigned int min_quad_edges = 2;
1004 lwerror(
"pta_unstroke called with null pointarray");
1014 lwerror(
"pta_unstroke needs implementation for npoints < 4");
1018 num_edges = points->
npoints - 1;
1019 edges_in_arcs =
lwalloc(num_edges + 1);
1020 memset(edges_in_arcs, 0, num_edges + 1);
1024 while( i < num_edges-2 )
1026 unsigned int arc_edges;
1027 double num_quadrants;
1035 memcpy(&first, &a1,
sizeof(
POINT4D));
1037 for( j = i+3; j < num_edges+1; j++ )
1045 LWDEBUGF(4,
"pt_continues_arc #%d", current_arc);
1047 for ( k = j-1; k > j-4; k-- )
1048 edges_in_arcs[k] = current_arc;
1053 LWDEBUG(4,
"pt_continues_arc = false");
1058 memcpy(&a1, &a2,
sizeof(
POINT4D));
1059 memcpy(&a2, &a3,
sizeof(
POINT4D));
1060 memcpy(&a3, &b,
sizeof(
POINT4D));
1069 arc_edges = j - 1 - i;
1070 LWDEBUGF(4,
"arc defined by %d edges found", arc_edges);
1071 if ( first.
x == b.
x && first.
y == b.
y ) {
1072 LWDEBUG(4,
"arc is a circle");
1079 if ( p2_side >= 0 ) angle = -angle;
1081 if ( angle < 0 ) angle = 2 * M_PI + angle;
1082 num_quadrants = ( 4 * angle ) / ( 2 * M_PI );
1083 LWDEBUGF(4,
"arc angle (%g %g, %g %g, %g %g) is %g (side is %d), quadrants:%g", first.
x, first.
y, center.
x, center.
y, b.
x, b.
y, angle, p2_side, num_quadrants);
1086 if ( arc_edges < min_quad_edges * num_quadrants ) {
1087 LWDEBUGF(4,
"Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants);
1088 for ( k = j-1; k >= i; k-- )
1089 edges_in_arcs[k] = 0;
1097 edges_in_arcs[i] = 0;
1102 #if POSTGIS_DEBUG_LEVEL > 3
1104 char *edgestr =
lwalloc(num_edges+1);
1105 for ( i = 0; i < num_edges; i++ )
1107 if ( edges_in_arcs[i] )
1108 edgestr[i] = 48 + edges_in_arcs[i];
1112 edgestr[num_edges] = 0;
1113 LWDEBUGF(3,
"edge pattern %s", edgestr);
1119 edge_type = edges_in_arcs[0];
1121 for( i = 1; i < num_edges; i++ )
1123 if( edge_type != edges_in_arcs[i] )
1128 edge_type = edges_in_arcs[i];
1134 end = num_edges - 1;
1138 if ( outcol->
ngeoms == 1 )
1151 LWDEBUG(2,
"lwline_unstroke called.");
1161 uint32_t i, hascurve = 0;
1163 LWDEBUG(2,
"lwpolygon_unstroke called.");
1166 for (i=0; i<poly->
nrings; i++)
1176 for (i=0; i<poly->
nrings; i++)
1190 uint32_t i, hascurve = 0;
1192 LWDEBUG(2,
"lwmline_unstroke called.");
1195 for (i=0; i<mline->
ngeoms; i++)
1205 for (i=0; i<mline->
ngeoms; i++)
1218 uint32_t i, hascurve = 0;
1220 LWDEBUG(2,
"lwmpoly_unstroke called.");
1223 for (i=0; i<mpoly->
ngeoms; i++)
1233 for (i=0; i<mpoly->
ngeoms; i++)
1252 for (i=0; i < c->
ngeoms; i++)
1273 LWDEBUG(2,
"lwgeom_unstroke called.");
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
int ptarray_remove_point(POINTARRAY *pa, uint32_t where)
Remove a point from an existing POINTARRAY.
LW_LINEARIZE_TOLERANCE_TYPE
Semantic of the tolerance argument passed to lwcurve_linearize.
@ LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE
Tolerance expresses the maximum angle between the radii generating approximation line vertices,...
@ LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD
Tolerance expresses the number of segments to use for each quarter of circle (quadrant).
@ LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION
Tolerance expresses the maximum distance between an arbitrary point on the curve and the closest poin...
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
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...
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
#define FLAGS_GET_Z(flags)
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
@ LW_LINEARIZE_FLAG_SYMMETRIC
Symmetric linearization means that the output vertices would be the same no matter the order of the p...
@ LW_LINEARIZE_FLAG_RETAIN_ANGLE
Retain angle instructs the engine to try its best to retain the requested angle between generating ra...
#define FLAGS_NDIMS(flags)
#define POLYHEDRALSURFACETYPE
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
#define FLAGS_GET_M(flags)
void lwcollection_free(LWCOLLECTION *col)
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
void ptarray_free(POINTARRAY *pa)
LWGEOM * lwcircstring_as_lwgeom(const LWCIRCSTRING *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,...
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
void * lwalloc(size_t size)
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
#define LW_TRUE
Return types for functions with status returns.
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
void lwline_free(LWLINE *line)
LWCIRCSTRING * lwcircstring_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
#define EPSILON_SQLMM
Tolerance used to determine equality.
LWLINE * lwline_clone_deep(const LWLINE *lwgeom)
void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points)
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.
int ptarray_has_z(const POINTARRAY *pa)
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
int ptarray_has_m(const POINTARRAY *pa)
#define LWDEBUG(level, msg)
#define LWDEBUGF(level, msg,...)
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
LWGEOM * lwgeom_unstroke(const LWGEOM *geom)
Convert linearized type into arc type, de-linearizing the strokes where possible.
static LWLINE * lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
static double angle_increment_using_max_deviation(double max_deviation, double radius)
LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
Convert type with arcs into equivalent linearized type.
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.
static LWGEOM * linestring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
static double lw_arc_angle(const POINT2D *a, const POINT2D *b, const POINT2D *c)
Return ABC angle in radians TODO: move to lwalgorithm.
int lwgeom_type_arc(const LWGEOM *geom)
Geometry type is one of the potentially "arc containing" types (circstring, multicurve,...
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 LWGEOM * circstring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
static double angle_increment_using_segments_per_quad(double tol)
static LWMPOLY * lwmsurface_linearize(const LWMSURFACE *msurface, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
static LWLINE * lwcompound_linearize(const LWCOMPOUND *icompound, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
static LWCOLLECTION * lwcollection_linearize(const LWCOLLECTION *collection, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
static LWGEOM * geom_from_pa(const POINTARRAY *pa, int32_t srid, int is_arc, int start, int end)
LWGEOM * lwmpolygon_unstroke(const LWMPOLY *mpoly)
static LWMLINE * lwmcurve_linearize(const LWMCURVE *mcurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
LWPOLY * lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
LWGEOM * lwpolygon_unstroke(const LWPOLY *poly)
static double angle_increment_using_max_angle(double tol)
static LWPOLY * lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
LWGEOM * pta_unstroke(const POINTARRAY *points, int32_t srid)
LWGEOM * lwcollection_unstroke(const LWCOLLECTION *c)
LWGEOM * lwmline_unstroke(const LWMLINE *mline)
LWGEOM * lwline_unstroke(const LWLINE *line)
int lwgeom_has_arc(const LWGEOM *geom)
Geometry includes at least one actual circular arc.