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