PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwstroke.c
Go to the documentation of this file.
1/**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * PostGIS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * PostGIS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright (C) 2001-2006 Refractions Research Inc.
22 * Copyright (C) 2017 Sandro Santilli <strk@kbt.io>
23 * Copyright (C) 2018 Daniel Baston <dbaston@gmail.com>
24 *
25 **********************************************************************/
26
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <stdarg.h>
31#include <string.h>
32
33#include "../postgis_config.h"
34
35/*#define POSTGIS_DEBUG_LEVEL 3*/
36
37#include "lwgeom_log.h"
38
39#include "liblwgeom_internal.h"
40
41LWGEOM *pta_unstroke(const POINTARRAY *points, int32_t srid);
42LWGEOM* lwline_unstroke(const LWLINE *line);
43LWGEOM* lwpolygon_unstroke(const LWPOLY *poly);
44LWGEOM* lwmline_unstroke(const LWMLINE *mline);
47LWGEOM* lwgeom_unstroke(const LWGEOM *geom);
48
49
50/*
51 * Determines (recursively in the case of collections) whether the geometry
52 * contains at least on arc geometry or segment.
53 */
54int
56{
57 LWCOLLECTION *col;
58 uint32_t i;
59
60 LWDEBUG(2, "lwgeom_has_arc called.");
61
62 switch (geom->type)
63 {
64 case POINTTYPE:
65 case LINETYPE:
66 case POLYGONTYPE:
67 case TRIANGLETYPE:
68 case MULTIPOINTTYPE:
69 case MULTILINETYPE:
72 case TINTYPE:
73 return LW_FALSE;
74 case CIRCSTRINGTYPE:
75 return LW_TRUE;
76 /* It's a collection that MAY contain an arc */
77 default:
78 col = (LWCOLLECTION *)geom;
79 for (i=0; i<col->ngeoms; i++)
80 {
81 if (lwgeom_has_arc(col->geoms[i]) == LW_TRUE)
82 return LW_TRUE;
83 }
84 return LW_FALSE;
85 }
86}
87
88int
90{
91 switch (geom->type)
92 {
93 case COMPOUNDTYPE:
94 case CIRCSTRINGTYPE:
95 case CURVEPOLYTYPE:
97 case MULTICURVETYPE:
98 return LW_TRUE;
99 default:
100 return LW_FALSE;
101 }
102}
103
104/*******************************************************************************
105 * Begin curve segmentize functions
106 ******************************************************************************/
107
108static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
109{
110 LWDEBUGF(4,"angle %.05g a1 %.05g a2 %.05g a3 %.05g zm1 %.05g zm2 %.05g zm3 %.05g",angle,a1,a2,a3,zm1,zm2,zm3);
111 /* Counter-clockwise sweep */
112 if ( a1 < a2 )
113 {
114 if ( angle <= a2 )
115 return zm1 + (zm2-zm1) * (angle-a1) / (a2-a1);
116 else
117 return zm2 + (zm3-zm2) * (angle-a2) / (a3-a2);
118 }
119 /* Clockwise sweep */
120 else
121 {
122 if ( angle >= a2 )
123 return zm1 + (zm2-zm1) * (a1-angle) / (a1-a2);
124 else
125 return zm2 + (zm3-zm2) * (a2-angle) / (a2-a3);
126 }
127}
128
129/* Compute the angle covered by a single segment such that
130 * a given number of segments per quadrant is achieved. */
132{
133 double increment;
134 int perQuad = rint(tol);
135 // error out if tol != perQuad ? (not-round)
136 if ( perQuad != tol )
137 {
138 lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol);
139 return -1;
140 }
141 if ( perQuad < 1 )
142 {
143 lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
144 return -1;
145 }
146 increment = fabs(M_PI_2 / perQuad);
147 LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);
148
149 return increment;
150}
151
152/* Compute the angle covered by a single quadrant such that
153 * the segment deviates from the arc by no more than a given
154 * amount. */
155static double angle_increment_using_max_deviation(double max_deviation, double radius)
156{
157 double increment, halfAngle, maxErr;
158 if ( max_deviation <= 0 )
159 {
160 lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", max_deviation);
161 return -1;
162 }
163
164 /*
165 * Ref: https://en.wikipedia.org/wiki/Sagitta_(geometry)
166 *
167 * An arc "sagitta" (distance between middle point of arc and
168 * middle point of corresponding chord) is defined as:
169 *
170 * sagitta = radius * ( 1 - cos( angle ) );
171 *
172 * We want our sagitta to be at most "tolerance" long,
173 * and we want to find out angle, so we use the inverse
174 * formula:
175 *
176 * tol = radius * ( 1 - cos( angle ) );
177 * 1 - cos( angle ) = tol/radius
178 * - cos( angle ) = tol/radius - 1
179 * cos( angle ) = - tol/radius + 1
180 * angle = acos( 1 - tol/radius )
181 *
182 * Constraints: 1.0 - tol/radius must be between -1 and 1
183 * which means tol must be between 0 and 2 times
184 * the radius, which makes sense as you cannot have a
185 * sagitta bigger than twice the radius!
186 *
187 */
188 maxErr = max_deviation;
189 if ( maxErr > radius * 2 )
190 {
191 maxErr = radius * 2;
192 LWDEBUGF(2,
193 "lwarc_linearize: tolerance %g is too big, "
194 "using arc-max 2 * radius == %g",
195 max_deviation,
196 maxErr);
197 }
198 do {
199 halfAngle = acos( 1.0 - maxErr / radius );
200 /* TODO: avoid a loop here, going rather straight to
201 * a minimum angle value */
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);
205 maxErr *= 2;
206 } while(1);
207 increment = 2 * halfAngle;
208 LWDEBUGF(2,
209 "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)",
210 max_deviation,
211 radius,
212 halfAngle,
213 increment,
214 increment * 180 / M_PI);
215
216 return increment;
217}
218
219/* Check that a given angle is positive and, if so, take
220 * it to be the angle covered by a single segment. */
221static double angle_increment_using_max_angle(double tol)
222{
223 if ( tol <= 0 )
224 {
225 lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
226 return -1;
227 }
228
229 return tol;
230}
231
232
250static int
252 const POINT4D *p1, const POINT4D *p2, const POINT4D *p3,
253 double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
254 int flags)
255{
256 POINT2D center;
257 POINT2D *t1 = (POINT2D*)p1;
258 POINT2D *t2 = (POINT2D*)p2;
259 POINT2D *t3 = (POINT2D*)p3;
260 POINT4D pt;
261 int p2_side = 0;
262 int clockwise = LW_TRUE;
263 double radius; /* Arc radius */
264 double increment; /* Angle per segment */
265 double angle_shift = 0;
266 double a1, a2, a3;
267 POINTARRAY *pa;
268 int is_circle = LW_FALSE;
269 int points_added = 0;
270 int reverse = 0;
271 int segments = 0;
272
273 LWDEBUG(2, "lwarc_linearize called.");
274
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);
277
278 p2_side = lw_segment_side(t1, t3, t2);
279
280 LWDEBUGF(2, " p2 side is %d", p2_side);
281
282 /* Force counterclockwise scan if SYMMETRIC operation is requested */
283 if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC )
284 {
285 /* swap p1-p3 */
286 t1 = (POINT2D*)p3;
287 t3 = (POINT2D*)p1;
288 p1 = (POINT4D*)t1;
289 p3 = (POINT4D*)t3;
290 p2_side = 1;
291 reverse = 1;
292 }
293
294 radius = lw_arc_center(t1, t2, t3, &center);
295 LWDEBUGF(2, " center is POINT(%.15g %.15g) - radius:%g", center.x, center.y, radius);
296
297 /* Matched start/end points imply circle */
298 if ( p1->x == p3->x && p1->y == p3->y )
299 is_circle = LW_TRUE;
300
301 /* Negative radius signals straight line, p1/p2/p3 are collinear */
302 if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
303 return 0;
304
305 /* The side of the p1/p3 line that p2 falls on dictates the sweep
306 direction from p1 to p3. */
307 if ( p2_side == -1 )
308 clockwise = LW_TRUE;
309 else
310 clockwise = LW_FALSE;
311
312 /* Compute the increment (angle per segment) depending on
313 * our tolerance type. */
314 switch(tolerance_type)
315 {
318 break;
320 increment = angle_increment_using_max_deviation(tol, radius);
321 break;
323 increment = angle_increment_using_max_angle(tol);
324 break;
325 default:
326 lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type);
327 return -1;
328 }
329
330 if (increment < 0)
331 {
332 /* Error occurred in increment calculation somewhere
333 * (lwerror already called)
334 */
335 return -1;
336 }
337
338 /* Angles of each point that defines the arc section */
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);
342
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);
345
346 /* Calculate total arc angle, in radians */
347 double total_angle = clockwise ? a1 - a3 : a3 - a1;
348 if ( total_angle <= 0 ) total_angle += M_PI * 2;
349
350 /* At extreme tolerance values (very low or very high, depending on
351 * the semantic) we may cause our arc to collapse. In this case,
352 * we want shrink the increment enough so that we get two segments
353 * for a standard arc, or three segments for a complete circle. */
354 int min_segs = is_circle ? 3 : 2;
355 segments = ceil(total_angle / increment);
356 if (segments < min_segs)
357 {
358 segments = min_segs;
359 increment = total_angle / min_segs;
360 }
361
362 if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC )
363 {
364 LWDEBUGF(2, "lwarc_linearize SYMMETRIC requested - total angle %g deg", total_angle * 180 / M_PI);
365
366 if ( flags & LW_LINEARIZE_FLAG_RETAIN_ANGLE )
367 {
368 /* Number of complete steps */
369 segments = trunc(total_angle / increment);
370
371 /* Figure out the angle remainder, i.e. the amount of the angle
372 * that is left after we can take no more complete angle
373 * increments. */
374 double angle_remainder = total_angle - (increment * segments);
375
376 /* Shift the starting angle by half of the remainder. This
377 * will have the effect of evenly distributing the remainder
378 * among the first and last segments in the arc. */
379 angle_shift = angle_remainder / 2.0;
380
381 LWDEBUGF(2,
382 "lwarc_linearize RETAIN_ANGLE operation requested - "
383 "total angle %g, steps %d, increment %g, remainder %g",
384 total_angle * 180 / M_PI,
385 segments,
386 increment * 180 / M_PI,
387 angle_remainder * 180 / M_PI);
388 }
389 else
390 {
391 /* Number of segments in output */
392 segments = ceil(total_angle / increment);
393 /* Tweak increment to be regular for all the arc */
394 increment = total_angle / segments;
395
396 LWDEBUGF(2,
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,
400 p1->x,
401 p1->y,
402 center.x,
403 center.y,
404 p3->x,
405 p3->y,
406 segments,
407 increment * 180 / M_PI);
408 }
409 }
410
411 /* p2 on left side => clockwise sweep */
412 if ( clockwise )
413 {
414 LWDEBUG(2, " Clockwise sweep");
415 increment *= -1;
416 angle_shift *= -1;
417 /* Adjust a3 down so we can decrement from a1 to a3 cleanly */
418 if ( a3 > a1 )
419 a3 -= 2.0 * M_PI;
420 if ( a2 > a1 )
421 a2 -= 2.0 * M_PI;
422 }
423 /* p2 on right side => counter-clockwise sweep */
424 else
425 {
426 LWDEBUG(2, " Counterclockwise sweep");
427 /* Adjust a3 up so we can increment from a1 to a3 cleanly */
428 if ( a3 < a1 )
429 a3 += 2.0 * M_PI;
430 if ( a2 < a1 )
431 a2 += 2.0 * M_PI;
432 }
433
434 /* Override angles for circle case */
435 if( is_circle )
436 {
437 increment = fabs(increment);
438 segments = ceil(total_angle / increment);
439 if (segments < 3)
440 {
441 segments = 3;
442 increment = total_angle / 3;
443 }
444 a3 = a1 + 2.0 * M_PI;
445 a2 = a1 + M_PI;
446 clockwise = LW_FALSE;
447 angle_shift = 0.0;
448 }
449
450 LWDEBUGF(2, "lwarc_linearize angle_shift:%g, increment:%g",
451 angle_shift * 180/M_PI, increment * 180/M_PI);
452
453 if ( reverse )
454 {
455 /* Append points in order to a temporary POINTARRAY and
456 * reverse them before writing to the output POINTARRAY. */
457 const int capacity = 8; /* TODO: compute exactly ? */
458 pa = ptarray_construct_empty(ptarray_has_z(to), ptarray_has_m(to), capacity);
459 }
460 else
461 {
462 /* Append points directly to the output POINTARRAY,
463 * starting with p1. */
464 pa = to;
465
467 ++points_added;
468 }
469
470 /* Sweep from a1 to a3 */
471 int seg_start = 1; /* First point is added manually */
472 int seg_end = segments;
473 if (angle_shift != 0.0)
474 {
475 /* When we have extra angles we need to add the extra segments at the
476 * start and end that cover those parts of the arc */
477 seg_start = 0;
478 seg_end = segments + 1;
479 }
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++)
483 {
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);
488 pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z);
489 pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m);
491 ++points_added;
492 }
493
494 /* Ensure the final point is EXACTLY the same as the first for the circular case */
495 if ( is_circle )
496 {
497 ptarray_remove_point(pa, pa->npoints - 1);
499 }
500
501 if ( reverse )
502 {
503 int i;
505 for ( i=pa->npoints; i>0; i-- ) {
506 getPoint4d_p(pa, i-1, &pt);
508 }
509 ptarray_free(pa);
510 }
511
512 return points_added;
513}
514
515/*
516 * @param icurve input curve
517 * @param tol tolerance, semantic driven by tolerance_type
518 * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
519 * @param flags see flags in lwarc_linearize
520 *
521 * @return a newly allocated LWLINE
522 */
523static LWLINE *
524lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol,
525 LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
526 int flags)
527{
528 LWLINE *oline;
529 POINTARRAY *ptarray;
530 uint32_t i, j;
531 POINT4D p1, p2, p3, p4;
532 int ret;
533
534 LWDEBUGF(2, "lwcircstring_linearize called., dim = %d", icurve->points->flags);
535
536 ptarray = ptarray_construct_empty(FLAGS_GET_Z(icurve->points->flags), FLAGS_GET_M(icurve->points->flags), 64);
537
538 for (i = 2; i < icurve->points->npoints; i+=2)
539 {
540 LWDEBUGF(3, "lwcircstring_linearize: arc ending at point %d", i);
541
542 getPoint4d_p(icurve->points, i - 2, &p1);
543 getPoint4d_p(icurve->points, i - 1, &p2);
544 getPoint4d_p(icurve->points, i, &p3);
545
546 ret = lwarc_linearize(ptarray, &p1, &p2, &p3, tol, tolerance_type, flags);
547 if ( ret > 0 )
548 {
549 LWDEBUGF(3, "lwcircstring_linearize: generated %d points", ptarray->npoints);
550 }
551 else if ( ret == 0 )
552 {
553 LWDEBUG(3, "lwcircstring_linearize: points are colinear, returning curve points as line");
554
555 for (j = i - 2 ; j < i ; j++)
556 {
557 getPoint4d_p(icurve->points, j, &p4);
558 ptarray_append_point(ptarray, &p4, LW_TRUE);
559 }
560 }
561 else
562 {
563 /* An error occurred, lwerror should have been called by now */
564 ptarray_free(ptarray);
565 return NULL;
566 }
567 }
568 getPoint4d_p(icurve->points, icurve->points->npoints-1, &p1);
569 ptarray_append_point(ptarray, &p1, LW_FALSE);
570
571 oline = lwline_construct(icurve->srid, NULL, ptarray);
572 return oline;
573}
574
575/*
576 * @param icompound input compound curve
577 * @param tol tolerance, semantic driven by tolerance_type
578 * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
579 * @param flags see flags in lwarc_linearize
580 *
581 * @return a newly allocated LWLINE
582 */
583static LWLINE *
584lwcompound_linearize(const LWCOMPOUND *icompound, double tol,
585 LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
586 int flags)
587{
588 LWGEOM *geom;
589 POINTARRAY *ptarray = NULL;
590 LWLINE *tmp = NULL;
591 uint32_t i, j;
592 POINT4D p;
593
594 LWDEBUG(2, "lwcompound_stroke called.");
595
596 ptarray = ptarray_construct_empty(FLAGS_GET_Z(icompound->flags), FLAGS_GET_M(icompound->flags), 64);
597
598 for (i = 0; i < icompound->ngeoms; i++)
599 {
600 geom = icompound->geoms[i];
601 if (geom->type == CIRCSTRINGTYPE)
602 {
603 tmp = lwcircstring_linearize((LWCIRCSTRING *)geom, tol, tolerance_type, flags);
604 for (j = 0; j < tmp->points->npoints; j++)
605 {
606 getPoint4d_p(tmp->points, j, &p);
607 ptarray_append_point(ptarray, &p, LW_TRUE);
608 }
609 lwline_free(tmp);
610 }
611 else if (geom->type == LINETYPE)
612 {
613 tmp = (LWLINE *)geom;
614 for (j = 0; j < tmp->points->npoints; j++)
615 {
616 getPoint4d_p(tmp->points, j, &p);
617 ptarray_append_point(ptarray, &p, LW_TRUE);
618 }
619 }
620 else
621 {
622 lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type));
623 return NULL;
624 }
625 }
626
628 return lwline_construct(icompound->srid, NULL, ptarray);
629}
630
631
632/*
633 * @param icompound input curve polygon
634 * @param tol tolerance, semantic driven by tolerance_type
635 * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
636 * @param flags see flags in lwarc_linearize
637 *
638 * @return a newly allocated LWPOLY
639 */
640static LWPOLY *
641lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol,
642 LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
643 int flags)
644{
645 LWPOLY *ogeom;
646 LWGEOM *tmp;
647 LWLINE *line;
648 POINTARRAY **ptarray;
649 uint32_t i;
650
651 LWDEBUG(2, "lwcurvepoly_linearize called.");
652
653 ptarray = lwalloc(sizeof(POINTARRAY *)*curvepoly->nrings);
654
655 for (i = 0; i < curvepoly->nrings; i++)
656 {
657 tmp = curvepoly->rings[i];
658 if (tmp->type == CIRCSTRINGTYPE)
659 {
660 line = lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, tolerance_type, flags);
661 ptarray[i] = ptarray_clone_deep(line->points);
662 lwline_free(line);
663 }
664 else if (tmp->type == LINETYPE)
665 {
666 line = (LWLINE *)tmp;
667 ptarray[i] = ptarray_clone_deep(line->points);
668 }
669 else if (tmp->type == COMPOUNDTYPE)
670 {
671 line = lwcompound_linearize((LWCOMPOUND *)tmp, tol, tolerance_type, flags);
672 ptarray[i] = ptarray_clone_deep(line->points);
673 lwline_free(line);
674 }
675 else
676 {
677 lwerror("Invalid ring type found in CurvePoly.");
678 return NULL;
679 }
680 }
681
682 ogeom = lwpoly_construct(curvepoly->srid, NULL, curvepoly->nrings, ptarray);
683 return ogeom;
684}
685
686/* Kept for backward compatibility - TODO: drop */
687LWPOLY *
688lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
689{
691}
692
693
702static LWMLINE *
703lwmcurve_linearize(const LWMCURVE *mcurve, double tol,
705 int flags)
706{
707 LWMLINE *ogeom;
708 LWGEOM **lines;
709 uint32_t i;
710
711 LWDEBUGF(2, "lwmcurve_linearize called, geoms=%d, dim=%d.", mcurve->ngeoms, FLAGS_NDIMS(mcurve->flags));
712
713 lines = lwalloc(sizeof(LWGEOM *)*mcurve->ngeoms);
714
715 for (i = 0; i < mcurve->ngeoms; i++)
716 {
717 const LWGEOM *tmp = mcurve->geoms[i];
718 if (tmp->type == CIRCSTRINGTYPE)
719 {
720 lines[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags);
721 }
722 else if (tmp->type == LINETYPE)
723 {
724 lines[i] = (LWGEOM *)lwline_construct(mcurve->srid, NULL, ptarray_clone_deep(((LWLINE *)tmp)->points));
725 }
726 else if (tmp->type == COMPOUNDTYPE)
727 {
728 lines[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags);
729 }
730 else
731 {
732 lwerror("Unsupported geometry found in MultiCurve.");
733 return NULL;
734 }
735 }
736
737 ogeom = (LWMLINE *)lwcollection_construct(MULTILINETYPE, mcurve->srid, NULL, mcurve->ngeoms, lines);
738 return ogeom;
739}
740
749static LWMPOLY *
750lwmsurface_linearize(const LWMSURFACE *msurface, double tol,
752 int flags)
753{
754 LWMPOLY *ogeom;
755 LWGEOM *tmp;
756 LWPOLY *poly;
757 LWGEOM **polys;
758 POINTARRAY **ptarray;
759 uint32_t i, j;
760
761 LWDEBUG(2, "lwmsurface_linearize called.");
762
763 polys = lwalloc(sizeof(LWGEOM *)*msurface->ngeoms);
764
765 for (i = 0; i < msurface->ngeoms; i++)
766 {
767 tmp = msurface->geoms[i];
768 if (tmp->type == CURVEPOLYTYPE)
769 {
770 polys[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags);
771 }
772 else if (tmp->type == POLYGONTYPE)
773 {
774 poly = (LWPOLY *)tmp;
775 ptarray = lwalloc(sizeof(POINTARRAY *)*poly->nrings);
776 for (j = 0; j < poly->nrings; j++)
777 {
778 ptarray[j] = ptarray_clone_deep(poly->rings[j]);
779 }
780 polys[i] = (LWGEOM *)lwpoly_construct(msurface->srid, NULL, poly->nrings, ptarray);
781 }
782 }
783 ogeom = (LWMPOLY *)lwcollection_construct(MULTIPOLYGONTYPE, msurface->srid, NULL, msurface->ngeoms, polys);
784 return ogeom;
785}
786
795static LWCOLLECTION *
796lwcollection_linearize(const LWCOLLECTION *collection, double tol,
798 int flags)
799{
800 LWCOLLECTION *ocol;
801 LWGEOM *tmp;
802 LWGEOM **geoms;
803 uint32_t i;
804
805 LWDEBUG(2, "lwcollection_linearize called.");
806
807 geoms = lwalloc(sizeof(LWGEOM *)*collection->ngeoms);
808
809 for (i=0; i<collection->ngeoms; i++)
810 {
811 tmp = collection->geoms[i];
812 switch (tmp->type)
813 {
814 case CIRCSTRINGTYPE:
815 geoms[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags);
816 break;
817 case COMPOUNDTYPE:
818 geoms[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags);
819 break;
820 case CURVEPOLYTYPE:
821 geoms[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags);
822 break;
823 case MULTICURVETYPE:
824 case MULTISURFACETYPE:
825 case COLLECTIONTYPE:
826 geoms[i] = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)tmp, tol, type, flags);
827 break;
828 default:
829 geoms[i] = lwgeom_clone_deep(tmp);
830 break;
831 }
832 }
833 ocol = lwcollection_construct(COLLECTIONTYPE, collection->srid, NULL, collection->ngeoms, geoms);
834 return ocol;
835}
836
837LWGEOM *
838lwcurve_linearize(const LWGEOM *geom, double tol,
840 int flags)
841{
842 LWGEOM * ogeom = NULL;
843 switch (geom->type)
844 {
845 case CIRCSTRINGTYPE:
846 ogeom = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)geom, tol, type, flags);
847 break;
848 case COMPOUNDTYPE:
849 ogeom = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)geom, tol, type, flags);
850 break;
851 case CURVEPOLYTYPE:
852 ogeom = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)geom, tol, type, flags);
853 break;
854 case MULTICURVETYPE:
855 ogeom = (LWGEOM *)lwmcurve_linearize((LWMCURVE *)geom, tol, type, flags);
856 break;
857 case MULTISURFACETYPE:
858 ogeom = (LWGEOM *)lwmsurface_linearize((LWMSURFACE *)geom, tol, type, flags);
859 break;
860 case COLLECTIONTYPE:
861 ogeom = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)geom, tol, type, flags);
862 break;
863 default:
864 ogeom = lwgeom_clone_deep(geom);
865 }
866 return ogeom;
867}
868
869/* Kept for backward compatibility - TODO: drop */
870LWGEOM *
871lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
872{
874}
875
880static double
881lw_arc_angle(const POINT2D *a, const POINT2D *b, const POINT2D *c)
882{
883 POINT2D ab, cb;
884
885 ab.x = b->x - a->x;
886 ab.y = b->y - a->y;
887
888 cb.x = b->x - c->x;
889 cb.y = b->y - c->y;
890
891 double dot = (ab.x * cb.x + ab.y * cb.y); /* dot product */
892 double cross = (ab.x * cb.y - ab.y * cb.x); /* cross product */
893
894 double alpha = atan2(cross, dot);
895
896 return alpha;
897}
898
903static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b)
904{
905 POINT2D center;
906 POINT2D *t1 = (POINT2D*)a1;
907 POINT2D *t2 = (POINT2D*)a2;
908 POINT2D *t3 = (POINT2D*)a3;
909 POINT2D *tb = (POINT2D*)b;
910 double radius = lw_arc_center(t1, t2, t3, &center);
911 double b_distance, diff;
912
913 /* Co-linear a1/a2/a3 */
914 if ( radius < 0.0 )
915 return LW_FALSE;
916
917 b_distance = distance2d_pt_pt(tb, &center);
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);
920
921 /* Is the point b on the circle? */
922 if ( diff < EPSILON_SQLMM )
923 {
924 int a2_side = lw_segment_side(t1, t3, t2);
925 int b_side = lw_segment_side(t1, t3, tb);
926 double angle1 = lw_arc_angle(t1, t2, t3);
927 double angle2 = lw_arc_angle(t2, t3, tb);
928
929 /* Is the angle similar to the previous one ? */
930 diff = fabs(angle1 - angle2);
931 LWDEBUGF(4, " angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff);
932 if ( diff > EPSILON_SQLMM )
933 {
934 return LW_FALSE;
935 }
936
937 /* Is the point b on the same side of a1/a3 as the mid-point a2 is? */
938 /* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */
939 if ( b_side != a2_side )
940 return LW_TRUE;
941 }
942 return LW_FALSE;
943}
944
945static LWGEOM *
946linestring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
947{
948 int i = 0, j = 0;
949 POINT4D p;
950 POINTARRAY *pao = ptarray_construct(ptarray_has_z(pa), ptarray_has_m(pa), end-start+2);
951 LWDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end);
952 for( i = start; i < end + 2; i++ )
953 {
954 getPoint4d_p(pa, i, &p);
955 ptarray_set_point4d(pao, j++, &p);
956 }
957 return lwline_as_lwgeom(lwline_construct(srid, NULL, pao));
958}
959
960static LWGEOM *
961circstring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
962{
963
964 POINT4D p0, p1, p2;
966 LWDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end);
967 getPoint4d_p(pa, start, &p0);
968 ptarray_set_point4d(pao, 0, &p0);
969 getPoint4d_p(pa, (start+end+1)/2, &p1);
970 ptarray_set_point4d(pao, 1, &p1);
971 getPoint4d_p(pa, end+1, &p2);
972 ptarray_set_point4d(pao, 2, &p2);
973 return lwcircstring_as_lwgeom(lwcircstring_construct(srid, NULL, pao));
974}
975
976static LWGEOM *
977geom_from_pa(const POINTARRAY *pa, int32_t srid, int is_arc, int start, int end)
978{
979 LWDEBUGF(4, "srid=%d, is_arc=%d, start=%d, end=%d", srid, is_arc, start, end);
980 if ( is_arc )
981 return circstring_from_pa(pa, srid, start, end);
982 else
983 return linestring_from_pa(pa, srid, start, end);
984}
985
986LWGEOM *
987pta_unstroke(const POINTARRAY *points, int32_t srid)
988{
989 int i = 0, j, k;
990 POINT4D a1, a2, a3, b;
991 POINT4D first, center;
992 char *edges_in_arcs;
993 int found_arc = LW_FALSE;
994 int current_arc = 1;
995 int num_edges;
996 int edge_type; /* non-zero if edge is part of an arc */
997 int start, end;
998 LWCOLLECTION *outcol;
999 /* Minimum number of edges, per quadrant, required to define an arc */
1000 const unsigned int min_quad_edges = 2;
1001
1002 /* Die on null input */
1003 if ( ! points )
1004 lwerror("pta_unstroke called with null pointarray");
1005
1006 /* Null on empty input? */
1007 if ( points->npoints == 0 )
1008 return NULL;
1009
1010 /* We can't desegmentize anything shorter than four points */
1011 if ( points->npoints < 4 )
1012 {
1013 /* Return a linestring here*/
1014 lwerror("pta_unstroke needs implementation for npoints < 4");
1015 }
1016
1017 /* Allocate our result array of vertices that are part of arcs */
1018 num_edges = points->npoints - 1;
1019 edges_in_arcs = lwalloc(num_edges + 1);
1020 memset(edges_in_arcs, 0, num_edges + 1);
1021
1022 /* We make a candidate arc of the first two edges, */
1023 /* And then see if the next edge follows it */
1024 while( i < num_edges-2 )
1025 {
1026 unsigned int arc_edges;
1027 double num_quadrants;
1028 double angle;
1029
1030 found_arc = LW_FALSE;
1031 /* Make candidate arc */
1032 getPoint4d_p(points, i , &a1);
1033 getPoint4d_p(points, i+1, &a2);
1034 getPoint4d_p(points, i+2, &a3);
1035 memcpy(&first, &a1, sizeof(POINT4D));
1036
1037 for( j = i+3; j < num_edges+1; j++ )
1038 {
1039 LWDEBUGF(4, "i=%d, j=%d", i, j);
1040 getPoint4d_p(points, j, &b);
1041 /* Does this point fall on our candidate arc? */
1042 if ( pt_continues_arc(&a1, &a2, &a3, &b) )
1043 {
1044 /* Yes. Mark this edge and the two preceding it as arc components */
1045 LWDEBUGF(4, "pt_continues_arc #%d", current_arc);
1046 found_arc = LW_TRUE;
1047 for ( k = j-1; k > j-4; k-- )
1048 edges_in_arcs[k] = current_arc;
1049 }
1050 else
1051 {
1052 /* No. So we're done with this candidate arc */
1053 LWDEBUG(4, "pt_continues_arc = false");
1054 current_arc++;
1055 break;
1056 }
1057
1058 memcpy(&a1, &a2, sizeof(POINT4D));
1059 memcpy(&a2, &a3, sizeof(POINT4D));
1060 memcpy(&a3, &b, sizeof(POINT4D));
1061 }
1062 /* Jump past all the edges that were added to the arc */
1063 if ( found_arc )
1064 {
1065 /* Check if an arc was composed by enough edges to be
1066 * really considered an arc
1067 * See http://trac.osgeo.org/postgis/ticket/2420
1068 */
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");
1073 num_quadrants = 4;
1074 }
1075 else {
1076 lw_arc_center((POINT2D*)&first, (POINT2D*)&b, (POINT2D*)&a1, (POINT2D*)&center);
1077 angle = lw_arc_angle((POINT2D*)&first, (POINT2D*)&center, (POINT2D*)&b);
1078 int p2_side = lw_segment_side((POINT2D*)&first, (POINT2D*)&a1, (POINT2D*)&b);
1079 if ( p2_side >= 0 ) angle = -angle;
1080
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);
1084 }
1085 /* a1 is first point, b is last point */
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;
1090 }
1091
1092 i = j-1;
1093 }
1094 else
1095 {
1096 /* Mark this edge as a linear edge */
1097 edges_in_arcs[i] = 0;
1098 i = i+1;
1099 }
1100 }
1101
1102#if POSTGIS_DEBUG_LEVEL > 3
1103 {
1104 char *edgestr = lwalloc(num_edges+1);
1105 for ( i = 0; i < num_edges; i++ )
1106 {
1107 if ( edges_in_arcs[i] )
1108 edgestr[i] = 48 + edges_in_arcs[i];
1109 else
1110 edgestr[i] = '.';
1111 }
1112 edgestr[num_edges] = 0;
1113 LWDEBUGF(3, "edge pattern %s", edgestr);
1114 lwfree(edgestr);
1115 }
1116#endif
1117
1118 start = 0;
1119 edge_type = edges_in_arcs[0];
1120 outcol = lwcollection_construct_empty(COMPOUNDTYPE, srid, ptarray_has_z(points), ptarray_has_m(points));
1121 for( i = 1; i < num_edges; i++ )
1122 {
1123 if( edge_type != edges_in_arcs[i] )
1124 {
1125 end = i - 1;
1126 lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end));
1127 start = i;
1128 edge_type = edges_in_arcs[i];
1129 }
1130 }
1131 lwfree(edges_in_arcs); /* not needed anymore */
1132
1133 /* Roll out last item */
1134 end = num_edges - 1;
1135 lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end));
1136
1137 /* Strip down to singleton if only one entry */
1138 if ( outcol->ngeoms == 1 )
1139 {
1140 LWGEOM *outgeom = outcol->geoms[0];
1141 outcol->ngeoms = 0; lwcollection_free(outcol);
1142 return outgeom;
1143 }
1144 return lwcollection_as_lwgeom(outcol);
1145}
1146
1147
1148LWGEOM *
1150{
1151 LWDEBUG(2, "lwline_unstroke called.");
1152
1153 if ( line->points->npoints < 4 ) return lwline_as_lwgeom(lwline_clone_deep(line));
1154 else return pta_unstroke(line->points, line->srid);
1155}
1156
1157LWGEOM *
1159{
1160 LWGEOM **geoms;
1161 uint32_t i, hascurve = 0;
1162
1163 LWDEBUG(2, "lwpolygon_unstroke called.");
1164
1165 geoms = lwalloc(sizeof(LWGEOM *)*poly->nrings);
1166 for (i=0; i<poly->nrings; i++)
1167 {
1168 geoms[i] = pta_unstroke(poly->rings[i], poly->srid);
1169 if (geoms[i]->type == CIRCSTRINGTYPE || geoms[i]->type == COMPOUNDTYPE)
1170 {
1171 hascurve = 1;
1172 }
1173 }
1174 if (hascurve == 0)
1175 {
1176 for (i=0; i<poly->nrings; i++)
1177 {
1178 lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */
1179 }
1180 return lwgeom_clone_deep((LWGEOM *)poly);
1181 }
1182
1183 return (LWGEOM *)lwcollection_construct(CURVEPOLYTYPE, poly->srid, NULL, poly->nrings, geoms);
1184}
1185
1186LWGEOM *
1188{
1189 LWGEOM **geoms;
1190 uint32_t i, hascurve = 0;
1191
1192 LWDEBUG(2, "lwmline_unstroke called.");
1193
1194 geoms = lwalloc(sizeof(LWGEOM *)*mline->ngeoms);
1195 for (i=0; i<mline->ngeoms; i++)
1196 {
1197 geoms[i] = lwline_unstroke((LWLINE *)mline->geoms[i]);
1198 if (geoms[i]->type == CIRCSTRINGTYPE || geoms[i]->type == COMPOUNDTYPE)
1199 {
1200 hascurve = 1;
1201 }
1202 }
1203 if (hascurve == 0)
1204 {
1205 for (i=0; i<mline->ngeoms; i++)
1206 {
1207 lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */
1208 }
1209 return lwgeom_clone_deep((LWGEOM *)mline);
1210 }
1211 return (LWGEOM *)lwcollection_construct(MULTICURVETYPE, mline->srid, NULL, mline->ngeoms, geoms);
1212}
1213
1214LWGEOM *
1216{
1217 LWGEOM **geoms;
1218 uint32_t i, hascurve = 0;
1219
1220 LWDEBUG(2, "lwmpoly_unstroke called.");
1221
1222 geoms = lwalloc(sizeof(LWGEOM *)*mpoly->ngeoms);
1223 for (i=0; i<mpoly->ngeoms; i++)
1224 {
1225 geoms[i] = lwpolygon_unstroke((LWPOLY *)mpoly->geoms[i]);
1226 if (geoms[i]->type == CURVEPOLYTYPE)
1227 {
1228 hascurve = 1;
1229 }
1230 }
1231 if (hascurve == 0)
1232 {
1233 for (i=0; i<mpoly->ngeoms; i++)
1234 {
1235 lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */
1236 }
1237 return lwgeom_clone_deep((LWGEOM *)mpoly);
1238 }
1239 return (LWGEOM *)lwcollection_construct(MULTISURFACETYPE, mpoly->srid, NULL, mpoly->ngeoms, geoms);
1240}
1241
1242LWGEOM *
1244{
1245 LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
1246 memcpy(ret, c, sizeof(LWCOLLECTION));
1247
1248 if (c->ngeoms > 0)
1249 {
1250 uint32_t i;
1251 ret->geoms = lwalloc(sizeof(LWGEOM *)*c->ngeoms);
1252 for (i=0; i < c->ngeoms; i++)
1253 {
1254 ret->geoms[i] = lwgeom_unstroke(c->geoms[i]);
1255 }
1256 if (c->bbox)
1257 {
1258 ret->bbox = gbox_copy(c->bbox);
1259 }
1260 }
1261 else
1262 {
1263 ret->bbox = NULL;
1264 ret->geoms = NULL;
1265 }
1266 return (LWGEOM *)ret;
1267}
1268
1269
1270LWGEOM *
1272{
1273 LWDEBUG(2, "lwgeom_unstroke called.");
1274
1275 switch (geom->type)
1276 {
1277 case LINETYPE:
1278 return lwline_unstroke((LWLINE *)geom);
1279 case POLYGONTYPE:
1280 return lwpolygon_unstroke((LWPOLY *)geom);
1281 case MULTILINETYPE:
1282 return lwmline_unstroke((LWMLINE *)geom);
1283 case MULTIPOLYGONTYPE:
1284 return lwmpolygon_unstroke((LWMPOLY *)geom);
1285 case COLLECTIONTYPE:
1286 return lwcollection_unstroke((LWCOLLECTION *)geom);
1287 default:
1288 return lwgeom_clone_deep(geom);
1289 }
1290}
1291
char * s
Definition cu_in_wkt.c:23
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
Definition gbox.c:438
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
int ptarray_remove_point(POINTARRAY *pa, uint32_t where)
Remove a point from an existing POINTARRAY.
Definition ptarray.c:259
LW_LINEARIZE_TOLERANCE_TYPE
Semantic of the tolerance argument passed to lwcurve_linearize.
Definition liblwgeom.h:2377
@ LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE
Tolerance expresses the maximum angle between the radii generating approximation line vertices,...
Definition liblwgeom.h:2398
@ LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD
Tolerance expresses the number of segments to use for each quarter of circle (quadrant).
Definition liblwgeom.h:2383
@ LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION
Tolerance expresses the maximum distance between an arbitrary point on the curve and the closest poin...
Definition liblwgeom.h:2390
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
#define LW_FALSE
Definition liblwgeom.h:94
#define COLLECTIONTYPE
Definition liblwgeom.h:108
#define COMPOUNDTYPE
Definition liblwgeom.h:110
double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
Definition measures.c:2344
#define CURVEPOLYTYPE
Definition liblwgeom.h:111
#define MULTILINETYPE
Definition liblwgeom.h:106
#define MULTISURFACETYPE
Definition liblwgeom.h:113
#define LINETYPE
Definition liblwgeom.h:103
#define MULTIPOINTTYPE
Definition liblwgeom.h:105
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition ptarray.c:59
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:165
void * lwalloc(size_t size)
Definition lwutil.c:227
#define TINTYPE
Definition liblwgeom.h:116
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
@ LW_LINEARIZE_FLAG_SYMMETRIC
Symmetric linearization means that the output vertices would be the same no matter the order of the p...
Definition liblwgeom.h:2407
@ LW_LINEARIZE_FLAG_RETAIN_ANGLE
Retain angle instructs the engine to try its best to retain the requested angle between generating ra...
Definition liblwgeom.h:2427
void lwfree(void *mem)
Definition lwutil.c:248
#define FLAGS_NDIMS(flags)
Definition liblwgeom.h:179
#define POLYGONTYPE
Definition liblwgeom.h:104
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:367
#define POLYHEDRALSURFACETYPE
Definition liblwgeom.h:114
#define CIRCSTRINGTYPE
Definition liblwgeom.h:109
#define FLAGS_GET_M(flags)
Definition liblwgeom.h:166
void lwcollection_free(LWCOLLECTION *col)
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
void ptarray_free(POINTARRAY *pa)
Definition ptarray.c:327
LWGEOM * lwcircstring_as_lwgeom(const LWCIRCSTRING *obj)
Definition lwgeom.c:342
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition lwpoly.c:43
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
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,...
Definition ptarray.c:147
#define MULTICURVETYPE
Definition liblwgeom.h:112
#define TRIANGLETYPE
Definition liblwgeom.h:115
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:93
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
LWCIRCSTRING * lwcircstring_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:369
void lwline_free(LWLINE *line)
Definition lwline.c:67
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition lwgeom.c:337
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...
Definition ptarray.c:51
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition ptarray.c:643
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition lwgeom.c:557
#define EPSILON_SQLMM
Tolerance used to determine equality.
void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points)
Definition ptarray.c:1674
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)
Definition ptarray.c:37
LWLINE * lwline_clone_deep(const LWLINE *lwgeom)
Definition lwline.c:109
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition lwalgorithm.c:70
int ptarray_has_m(const POINTARRAY *pa)
Definition ptarray.c:44
#define LWDEBUG(level, msg)
Definition lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static LWCOLLECTION * lwcollection_linearize(const LWCOLLECTION *collection, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition lwstroke.c:796
static double angle_increment_using_max_deviation(double max_deviation, double radius)
Definition lwstroke.c:155
static LWGEOM * geom_from_pa(const POINTARRAY *pa, int32_t srid, int is_arc, int start, int end)
Definition lwstroke.c:977
LWGEOM * lwgeom_unstroke(const LWGEOM *geom)
Convert linearized type into arc type, de-linearizing the strokes where possible.
Definition lwstroke.c:1271
LWPOLY * lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
Definition lwstroke.c:688
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.
Definition lwstroke.c:251
static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
Definition lwstroke.c:108
static double lw_arc_angle(const POINT2D *a, const POINT2D *b, const POINT2D *c)
Return ABC angle in radians TODO: move to lwalgorithm.
Definition lwstroke.c:881
static LWPOLY * lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition lwstroke.c:641
int lwgeom_type_arc(const LWGEOM *geom)
Geometry type is one of the potentially "arc containing" types (circstring, multicurve,...
Definition lwstroke.c:89
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 ...
Definition lwstroke.c:903
static double angle_increment_using_segments_per_quad(double tol)
Definition lwstroke.c:131
static LWLINE * lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition lwstroke.c:524
static LWLINE * lwcompound_linearize(const LWCOMPOUND *icompound, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition lwstroke.c:584
static LWGEOM * linestring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
Definition lwstroke.c:946
LWGEOM * lwmpolygon_unstroke(const LWMPOLY *mpoly)
Definition lwstroke.c:1215
static LWMLINE * lwmcurve_linearize(const LWMCURVE *mcurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition lwstroke.c:703
LWGEOM * lwpolygon_unstroke(const LWPOLY *poly)
Definition lwstroke.c:1158
static double angle_increment_using_max_angle(double tol)
Definition lwstroke.c:221
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
Convert type with arcs into equivalent linearized type.
Definition lwstroke.c:871
LWGEOM * pta_unstroke(const POINTARRAY *points, int32_t srid)
Definition lwstroke.c:987
static LWGEOM * circstring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
Definition lwstroke.c:961
LWGEOM * lwcollection_unstroke(const LWCOLLECTION *c)
Definition lwstroke.c:1243
LWGEOM * lwmline_unstroke(const LWMLINE *mline)
Definition lwstroke.c:1187
LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition lwstroke.c:838
LWGEOM * lwline_unstroke(const LWLINE *line)
Definition lwstroke.c:1149
int lwgeom_has_arc(const LWGEOM *geom)
Geometry includes at least one actual circular arc.
Definition lwstroke.c:55
static LWMPOLY * lwmsurface_linearize(const LWMSURFACE *msurface, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition lwstroke.c:750
int32_t srid
Definition liblwgeom.h:508
POINTARRAY * points
Definition liblwgeom.h:507
uint32_t ngeoms
Definition liblwgeom.h:580
GBOX * bbox
Definition liblwgeom.h:574
LWGEOM ** geoms
Definition liblwgeom.h:575
int32_t srid
Definition liblwgeom.h:576
lwflags_t flags
Definition liblwgeom.h:591
int32_t srid
Definition liblwgeom.h:590
uint32_t ngeoms
Definition liblwgeom.h:594
LWGEOM ** geoms
Definition liblwgeom.h:589
int32_t srid
Definition liblwgeom.h:604
LWGEOM ** rings
Definition liblwgeom.h:603
uint32_t nrings
Definition liblwgeom.h:608
uint8_t type
Definition liblwgeom.h:462
POINTARRAY * points
Definition liblwgeom.h:483
int32_t srid
Definition liblwgeom.h:484
LWGEOM ** geoms
Definition liblwgeom.h:617
lwflags_t flags
Definition liblwgeom.h:619
uint32_t ngeoms
Definition liblwgeom.h:622
int32_t srid
Definition liblwgeom.h:618
int32_t srid
Definition liblwgeom.h:548
LWLINE ** geoms
Definition liblwgeom.h:547
uint32_t ngeoms
Definition liblwgeom.h:552
uint32_t ngeoms
Definition liblwgeom.h:566
LWPOLY ** geoms
Definition liblwgeom.h:561
int32_t srid
Definition liblwgeom.h:562
int32_t srid
Definition liblwgeom.h:632
uint32_t ngeoms
Definition liblwgeom.h:636
LWGEOM ** geoms
Definition liblwgeom.h:631
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
int32_t srid
Definition liblwgeom.h:520
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
double m
Definition liblwgeom.h:414
double x
Definition liblwgeom.h:414
double z
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414
lwflags_t flags
Definition liblwgeom.h:431
uint32_t npoints
Definition liblwgeom.h:427