PostGIS  3.4.0dev-r@@SVN_REVISION@@
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
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 
41 LWGEOM *pta_unstroke(const POINTARRAY *points, int32_t srid);
42 LWGEOM* lwline_unstroke(const LWLINE *line);
43 LWGEOM* lwpolygon_unstroke(const LWPOLY *poly);
44 LWGEOM* lwmline_unstroke(const LWMLINE *mline);
45 LWGEOM* lwmpolygon_unstroke(const LWMPOLY *mpoly);
47 LWGEOM* 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  */
54 int
55 lwgeom_has_arc(const LWGEOM *geom)
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:
70  case MULTIPOLYGONTYPE:
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 
88 int
89 lwgeom_type_arc(const LWGEOM *geom)
90 {
91  switch (geom->type)
92  {
93  case COMPOUNDTYPE:
94  case CIRCSTRINGTYPE:
95  case CURVEPOLYTYPE:
96  case MULTISURFACETYPE:
97  case MULTICURVETYPE:
98  return LW_TRUE;
99  default:
100  return LW_FALSE;
101  }
102 }
103 
104 /*******************************************************************************
105  * Begin curve segmentize functions
106  ******************************************************************************/
107 
108 static 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, perQuad);
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. */
155 static 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. */
221 static 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 
250 static 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);
490  ptarray_append_point(pa, &pt, LW_FALSE);
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);
507  ptarray_append_point(to, &pt, LW_FALSE);
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  */
523 static LWLINE *
524 lwcircstring_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  */
583 static LWLINE *
584 lwcompound_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  */
640 static LWPOLY *
641 lwcurvepoly_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 */
687 LWPOLY *
688 lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
689 {
691 }
692 
693 
702 static LWMLINE *
703 lwmcurve_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 
749 static LWMPOLY *
750 lwmsurface_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 
795 static LWCOLLECTION *
796 lwcollection_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 
837 LWGEOM *
838 lwcurve_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 */
870 LWGEOM *
871 lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
872 {
874 }
875 
880 static double
881 lw_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 
903 static 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 
945 static LWGEOM *
946 linestring_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 
960 static LWGEOM *
961 circstring_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 
976 static LWGEOM *
977 geom_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 
986 LWGEOM *
987 pta_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 
1148 LWGEOM *
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 
1157 LWGEOM *
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 
1186 LWGEOM *
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 
1214 LWGEOM *
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 
1242 LWGEOM *
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 
1270 LWGEOM *
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:426
int ptarray_remove_point(POINTARRAY *pa, uint32_t where)
Remove a point from an existing POINTARRAY.
Definition: ptarray.c:251
LW_LINEARIZE_TOLERANCE_TYPE
Semantic of the tolerance argument passed to lwcurve_linearize.
Definition: liblwgeom.h:2342
@ LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE
Tolerance expresses the maximum angle between the radii generating approximation line vertices,...
Definition: liblwgeom.h:2363
@ LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD
Tolerance expresses the number of segments to use for each quarter of circle (quadrant).
Definition: liblwgeom.h:2348
@ 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:2355
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
#define LW_FALSE
Definition: liblwgeom.h:94
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
#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:2398
#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_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:647
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:529
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
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
#define TINTYPE
Definition: liblwgeom.h:116
#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:2372
@ 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:2392
void lwfree(void *mem)
Definition: lwutil.c:242
#define FLAGS_NDIMS(flags)
Definition: liblwgeom.h:179
#define POLYGONTYPE
Definition: liblwgeom.h:104
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:114
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:109
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:59
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwcollection.c:92
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:166
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:357
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:125
void ptarray_free(POINTARRAY *pa)
Definition: ptarray.c:319
LWGEOM * lwcircstring_as_lwgeom(const LWCIRCSTRING *obj)
Definition: lwgeom.c:314
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
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:188
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:42
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:43
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
LWCIRCSTRING * lwcircstring_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwcircstring.c:50
#define EPSILON_SQLMM
Tolerance used to determine equality.
LWLINE * lwline_clone_deep(const LWLINE *lwgeom)
Definition: lwline.c:109
void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points)
Definition: ptarray.c:1535
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.
Definition: lwalgorithm.c:226
int ptarray_has_z(const POINTARRAY *pa)
Definition: ptarray.c:37
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:62
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
LWGEOM * lwgeom_unstroke(const LWGEOM *geom)
Convert linearized type into arc type, de-linearizing the strokes where possible.
Definition: lwstroke.c:1271
static LWLINE * lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:524
static double angle_increment_using_max_deviation(double max_deviation, double radius)
Definition: lwstroke.c:155
LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:838
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
Convert type with arcs into equivalent linearized type.
Definition: lwstroke.c:871
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 LWGEOM * linestring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
Definition: lwstroke.c:946
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
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 LWGEOM * circstring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
Definition: lwstroke.c:961
static double angle_increment_using_segments_per_quad(double tol)
Definition: lwstroke.c:131
static LWMPOLY * lwmsurface_linearize(const LWMSURFACE *msurface, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:750
static LWLINE * lwcompound_linearize(const LWCOMPOUND *icompound, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:584
static LWCOLLECTION * lwcollection_linearize(const LWCOLLECTION *collection, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:796
static LWGEOM * geom_from_pa(const POINTARRAY *pa, int32_t srid, int is_arc, int start, int end)
Definition: lwstroke.c:977
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
LWPOLY * lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
Definition: lwstroke.c:688
LWGEOM * lwpolygon_unstroke(const LWPOLY *poly)
Definition: lwstroke.c:1158
static double angle_increment_using_max_angle(double tol)
Definition: lwstroke.c:221
static LWPOLY * lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:641
LWGEOM * pta_unstroke(const POINTARRAY *points, int32_t srid)
Definition: lwstroke.c:987
LWGEOM * lwcollection_unstroke(const LWCOLLECTION *c)
Definition: lwstroke.c:1243
LWGEOM * lwmline_unstroke(const LWMLINE *mline)
Definition: lwstroke.c:1187
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
type
Definition: ovdump.py:42
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