PostGIS  2.4.9dev-r@@SVN_REVISION@@
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  *
24  **********************************************************************/
25 
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 
32 #include "../postgis_config.h"
33 
34 /*#define POSTGIS_DEBUG_LEVEL 3*/
35 
36 #include "lwgeom_log.h"
37 
38 #include "liblwgeom_internal.h"
39 
40 
41 LWGEOM* pta_unstroke(const POINTARRAY *points, int type, int 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);
46 LWGEOM* lwgeom_unstroke(const LWGEOM *geom);
47 
48 
49 /*
50  * Determines (recursively in the case of collections) whether the geometry
51  * contains at least on arc geometry or segment.
52  */
53 int
54 lwgeom_has_arc(const LWGEOM *geom)
55 {
56  LWCOLLECTION *col;
57  int i;
58 
59  LWDEBUG(2, "lwgeom_has_arc called.");
60 
61  switch (geom->type)
62  {
63  case POINTTYPE:
64  case LINETYPE:
65  case POLYGONTYPE:
66  case TRIANGLETYPE:
67  case MULTIPOINTTYPE:
68  case MULTILINETYPE:
69  case MULTIPOLYGONTYPE:
71  case TINTYPE:
72  return LW_FALSE;
73  case CIRCSTRINGTYPE:
74  case CURVEPOLYTYPE:
75  case COMPOUNDTYPE:
76  return LW_TRUE;
77  /* It's a collection that MAY contain an arc */
78  default:
79  col = (LWCOLLECTION *)geom;
80  for (i=0; i<col->ngeoms; i++)
81  {
82  if (lwgeom_has_arc(col->geoms[i]) == LW_TRUE)
83  return LW_TRUE;
84  }
85  return LW_FALSE;
86  }
87 }
88 
89 
90 
91 /*******************************************************************************
92  * Begin curve segmentize functions
93  ******************************************************************************/
94 
95 static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
96 {
97  LWDEBUGF(4,"angle %.05g a1 %.05g a2 %.05g a3 %.05g zm1 %.05g zm2 %.05g zm3 %.05g",angle,a1,a2,a3,zm1,zm2,zm3);
98  /* Counter-clockwise sweep */
99  if ( a1 < a2 )
100  {
101  if ( angle <= a2 )
102  return zm1 + (zm2-zm1) * (angle-a1) / (a2-a1);
103  else
104  return zm2 + (zm3-zm2) * (angle-a2) / (a3-a2);
105  }
106  /* Clockwise sweep */
107  else
108  {
109  if ( angle >= a2 )
110  return zm1 + (zm2-zm1) * (a1-angle) / (a1-a2);
111  else
112  return zm2 + (zm3-zm2) * (a2-angle) / (a2-a3);
113  }
114 }
115 
133 static int
135  const POINT4D *p1, const POINT4D *p2, const POINT4D *p3,
136  double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
137  int flags)
138 {
139  POINT2D center;
140  POINT2D *t1 = (POINT2D*)p1;
141  POINT2D *t2 = (POINT2D*)p2;
142  POINT2D *t3 = (POINT2D*)p3;
143  POINT4D pt;
144  int p2_side = 0;
145  int clockwise = LW_TRUE;
146  double radius; /* Arc radius */
147  double increment; /* Angle per segment */
148  double angle_shift = 0;
149  double a1, a2, a3, angle;
150  POINTARRAY *pa = to;
151  int is_circle = LW_FALSE;
152  int points_added = 0;
153  int reverse = 0;
154 
155  LWDEBUG(2, "lwarc_linearize called.");
156 
157  LWDEBUGF(2, " curve is CIRCULARSTRING(%.15g %.15f, %.15f %.15f, %.15f %15f)",
158  t1->x, t1->y, t2->x, t2->y, t3->x, t3->y);
159 
160  p2_side = lw_segment_side(t1, t3, t2);
161 
162  LWDEBUGF(2, " p2 side is %d", p2_side);
163 
164  /* Force counterclockwise scan if SYMMETRIC operation is requsested */
165  if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC )
166  {
167  /* swap p1-p3 */
168  t1 = (POINT2D*)p3;
169  t3 = (POINT2D*)p1;
170  p1 = (POINT4D*)t1;
171  p3 = (POINT4D*)t3;
172  p2_side = 1;
173  reverse = 1;
174  }
175 
176  radius = lw_arc_center(t1, t2, t3, &center);
177  LWDEBUGF(2, " center is POINT(%.15g %.15g) - radius:%g", center.x, center.y, radius);
178 
179  /* Matched start/end points imply circle */
180  if ( p1->x == p3->x && p1->y == p3->y )
181  is_circle = LW_TRUE;
182 
183  /* Negative radius signals straight line, p1/p2/p3 are colinear */
184  if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
185  return 0;
186 
187  /* The side of the p1/p3 line that p2 falls on dictates the sweep
188  direction from p1 to p3. */
189  if ( p2_side == -1 )
190  clockwise = LW_TRUE;
191  else
192  clockwise = LW_FALSE;
193 
194  if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD )
195  {{
196  int perQuad = rint(tol);
197  // error out if tol != perQuad ? (not-round)
198  if ( perQuad != tol )
199  {
200  lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
201  return -1;
202  }
203  if ( perQuad < 1 )
204  {
205  lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
206  return -1;
207  }
208  increment = fabs(M_PI_2 / perQuad);
209  LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);
210 
211  }}
212  else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION )
213  {{
214  double halfAngle, maxErr;
215  if ( tol <= 0 )
216  {
217  lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", tol);
218  return -1;
219  }
220 
221  /*
222  * Ref: https://en.wikipedia.org/wiki/Sagitta_(geometry)
223  *
224  * An arc "sagitta" (distance between middle point of arc and
225  * middle point of corresponding chord) is defined as:
226  *
227  * sagitta = radius * ( 1 - cos( angle ) );
228  *
229  * We want our sagitta to be at most "tolerance" long,
230  * and we want to find out angle, so we use the inverse
231  * formula:
232  *
233  * tol = radius * ( 1 - cos( angle ) );
234  * 1 - cos( angle ) = tol/radius
235  * - cos( angle ) = tol/radius - 1
236  * cos( angle ) = - tol/radius + 1
237  * angle = acos( 1 - tol/radius )
238  *
239  * Constraints: 1.0 - tol/radius must be between -1 and 1
240  * which means tol must be between 0 and 2 times
241  * the radius, which makes sense as you cannot have a
242  * sagitta bigger than twice the radius!
243  *
244  */
245  maxErr = tol;
246  if ( maxErr > radius * 2 )
247  {
248  maxErr = radius * 2;
249  LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, "
250  "using arc-max 2 * radius == %g", tol, maxErr);
251  }
252  do {
253  halfAngle = acos( 1.0 - maxErr / radius );
254  /* TODO: avoid a loop here, going rather straight to
255  * a minimum angle value */
256  if ( halfAngle != 0 ) break;
257  LWDEBUGF(2, "lwarc_linearize: tolerance %g is too small for this arc"
258  " to compute approximation angle, doubling it", maxErr);
259  maxErr *= 2;
260  } while(1);
261  increment = 2 * halfAngle;
262  LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
263  }}
264  else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE )
265  {
266  increment = tol;
267  if ( increment <= 0 )
268  {
269  lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
270  return -1;
271  }
272  }
273  else
274  {
275  lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type);
276  return LW_FALSE;
277  }
278 
279  /* Angles of each point that defines the arc section */
280  a1 = atan2(p1->y - center.y, p1->x - center.x);
281  a2 = atan2(p2->y - center.y, p2->x - center.x);
282  a3 = atan2(p3->y - center.y, p3->x - center.x);
283 
284  LWDEBUGF(2, "lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)",
285  a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI);
286 
287  if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC )
288  {{
289  /* Calculate total arc angle, in radians */
290  double angle = clockwise ? a1 - a3 : a3 - a1;
291  if ( angle < 0 ) angle += M_PI * 2;
292  LWDEBUGF(2, "lwarc_linearize SYMMETRIC requested - total angle %g deg",
293  angle * 180 / M_PI);
294  if ( flags & LW_LINEARIZE_FLAG_RETAIN_ANGLE )
295  {{
296  /* Number of steps */
297  int steps = trunc(angle / increment);
298  /* Angle reminder */
299  double angle_reminder = angle - ( increment * steps );
300  angle_shift = angle_reminder / 2.0;
301 
302  LWDEBUGF(2, "lwarc_linearize RETAIN_ANGLE operation requested - "
303  "total angle %g, steps %d, increment %g, reminder %g",
304  angle * 180 / M_PI, steps, increment * 180 / M_PI,
305  angle_reminder * 180 / M_PI);
306  }}
307  else
308  {{
309  /* Number of segments in output */
310  int segs = ceil(angle / increment);
311  /* Tweak increment to be regular for all the arc */
312  increment = angle/segs;
313 
314  LWDEBUGF(2, "lwarc_linearize SYMMETRIC operation requested - "
315  "total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d - I:%g",
316  angle*180/M_PI, p1->x, p1->y, center.x, center.y, p3->x, p3->y,
317  segs, increment*180/M_PI);
318  }}
319  }}
320 
321  /* p2 on left side => clockwise sweep */
322  if ( clockwise )
323  {
324  LWDEBUG(2, " Clockwise sweep");
325  increment *= -1;
326  angle_shift *= -1;
327  /* Adjust a3 down so we can decrement from a1 to a3 cleanly */
328  if ( a3 > a1 )
329  a3 -= 2.0 * M_PI;
330  if ( a2 > a1 )
331  a2 -= 2.0 * M_PI;
332  }
333  /* p2 on right side => counter-clockwise sweep */
334  else
335  {
336  LWDEBUG(2, " Counterclockwise sweep");
337  /* Adjust a3 up so we can increment from a1 to a3 cleanly */
338  if ( a3 < a1 )
339  a3 += 2.0 * M_PI;
340  if ( a2 < a1 )
341  a2 += 2.0 * M_PI;
342  }
343 
344  /* Override angles for circle case */
345  if( is_circle )
346  {
347  increment = fabs(increment);
348  a3 = a1 + 2.0 * M_PI;
349  a2 = a1 + M_PI;
350  clockwise = LW_FALSE;
351  }
352 
353  LWDEBUGF(2, "lwarc_linearize angle_shift:%g, increment:%g",
354  angle_shift * 180/M_PI, increment * 180/M_PI);
355 
356  if ( reverse ) {{
357  const int capacity = 8; /* TODO: compute exactly ? */
358  pa = ptarray_construct_empty(ptarray_has_z(to), ptarray_has_m(to), capacity);
359  }}
360 
361  /* Sweep from a1 to a3 */
362  if ( ! reverse )
363  {
365  }
366  ++points_added;
367  if ( angle_shift ) angle_shift -= increment;
368  LWDEBUGF(2, "a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d",
369  a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise);
370  for ( angle = a1 + increment + angle_shift; clockwise ? angle > a3 : angle < a3; angle += increment )
371  {
372  LWDEBUGF(2, " SA: %g ( %g deg )", angle, angle*180/M_PI);
373  pt.x = center.x + radius * cos(angle);
374  pt.y = center.y + radius * sin(angle);
375  pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z);
376  pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m);
377  ptarray_append_point(pa, &pt, LW_FALSE);
378  ++points_added;
379  angle_shift = 0;
380  }
381 
382  /* Ensure the final point is EXACTLY the same as the first for the circular case */
383  if ( is_circle )
384  {
385  ptarray_remove_point(pa, pa->npoints - 1);
387  }
388 
389  if ( reverse )
390  {
391  int i;
393  for ( i=pa->npoints; i>0; i-- ) {
394  getPoint4d_p(pa, i-1, &pt);
395  ptarray_append_point(to, &pt, LW_FALSE);
396  }
397  ptarray_free(pa);
398  }
399 
400  return points_added;
401 }
402 
403 /*
404  * @param icurve input curve
405  * @param tol tolerance, semantic driven by tolerance_type
406  * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
407  * @param flags see flags in lwarc_linearize
408  *
409  * @return a newly allocated LWLINE
410  */
411 static LWLINE *
412 lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol,
413  LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
414  int flags)
415 {
416  LWLINE *oline;
417  POINTARRAY *ptarray;
418  uint32_t i, j;
419  POINT4D p1, p2, p3, p4;
420  int ret;
421 
422  LWDEBUGF(2, "lwcircstring_linearize called., dim = %d", icurve->points->flags);
423 
424  ptarray = ptarray_construct_empty(FLAGS_GET_Z(icurve->points->flags), FLAGS_GET_M(icurve->points->flags), 64);
425 
426  for (i = 2; i < icurve->points->npoints; i+=2)
427  {
428  LWDEBUGF(3, "lwcircstring_linearize: arc ending at point %d", i);
429 
430  getPoint4d_p(icurve->points, i - 2, &p1);
431  getPoint4d_p(icurve->points, i - 1, &p2);
432  getPoint4d_p(icurve->points, i, &p3);
433 
434  ret = lwarc_linearize(ptarray, &p1, &p2, &p3, tol, tolerance_type, flags);
435  if ( ret > 0 )
436  {
437  LWDEBUGF(3, "lwcircstring_linearize: generated %d points", ptarray->npoints);
438  }
439  else if ( ret == 0 )
440  {
441  LWDEBUG(3, "lwcircstring_linearize: points are colinear, returning curve points as line");
442 
443  for (j = i - 2 ; j < i ; j++)
444  {
445  getPoint4d_p(icurve->points, j, &p4);
446  ptarray_append_point(ptarray, &p4, LW_TRUE);
447  }
448  }
449  else
450  {
451  /* An error occurred, lwerror should have been called by now */
452  ptarray_free(ptarray);
453  return NULL;
454  }
455  }
456  getPoint4d_p(icurve->points, icurve->points->npoints-1, &p1);
457  ptarray_append_point(ptarray, &p1, LW_FALSE);
458 
459  oline = lwline_construct(icurve->srid, NULL, ptarray);
460  return oline;
461 }
462 
463 /*
464  * @param icompound input compound curve
465  * @param tol tolerance, semantic driven by tolerance_type
466  * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
467  * @param flags see flags in lwarc_linearize
468  *
469  * @return a newly allocated LWLINE
470  */
471 static LWLINE *
472 lwcompound_linearize(const LWCOMPOUND *icompound, double tol,
473  LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
474  int flags)
475 {
476  LWGEOM *geom;
477  POINTARRAY *ptarray = NULL, *ptarray_out = NULL;
478  LWLINE *tmp = NULL;
479  uint32_t i, j;
480  POINT4D p;
481 
482  LWDEBUG(2, "lwcompound_stroke called.");
483 
484  ptarray = ptarray_construct_empty(FLAGS_GET_Z(icompound->flags), FLAGS_GET_M(icompound->flags), 64);
485 
486  for (i = 0; i < icompound->ngeoms; i++)
487  {
488  geom = icompound->geoms[i];
489  if (geom->type == CIRCSTRINGTYPE)
490  {
491  tmp = lwcircstring_linearize((LWCIRCSTRING *)geom, tol, tolerance_type, flags);
492  for (j = 0; j < tmp->points->npoints; j++)
493  {
494  getPoint4d_p(tmp->points, j, &p);
495  ptarray_append_point(ptarray, &p, LW_TRUE);
496  }
497  lwline_free(tmp);
498  }
499  else if (geom->type == LINETYPE)
500  {
501  tmp = (LWLINE *)geom;
502  for (j = 0; j < tmp->points->npoints; j++)
503  {
504  getPoint4d_p(tmp->points, j, &p);
505  ptarray_append_point(ptarray, &p, LW_TRUE);
506  }
507  }
508  else
509  {
510  lwerror("Unsupported geometry type %d found.",
511  geom->type, lwtype_name(geom->type));
512  return NULL;
513  }
514  }
515  ptarray_out = ptarray_remove_repeated_points(ptarray, 0.0);
516  ptarray_free(ptarray);
517  return lwline_construct(icompound->srid, NULL, ptarray_out);
518 }
519 
520 /* Kept for backward compatibility - TODO: drop */
521 LWLINE *
522 lwcompound_stroke(const LWCOMPOUND *icompound, uint32_t perQuad)
523 {
525 }
526 
527 
528 /*
529  * @param icompound input curve polygon
530  * @param tol tolerance, semantic driven by tolerance_type
531  * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
532  * @param flags see flags in lwarc_linearize
533  *
534  * @return a newly allocated LWPOLY
535  */
536 static LWPOLY *
537 lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol,
538  LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
539  int flags)
540 {
541  LWPOLY *ogeom;
542  LWGEOM *tmp;
543  LWLINE *line;
544  POINTARRAY **ptarray;
545  int i;
546 
547  LWDEBUG(2, "lwcurvepoly_linearize called.");
548 
549  ptarray = lwalloc(sizeof(POINTARRAY *)*curvepoly->nrings);
550 
551  for (i = 0; i < curvepoly->nrings; i++)
552  {
553  tmp = curvepoly->rings[i];
554  if (tmp->type == CIRCSTRINGTYPE)
555  {
556  line = lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, tolerance_type, flags);
557  ptarray[i] = ptarray_clone_deep(line->points);
558  lwline_free(line);
559  }
560  else if (tmp->type == LINETYPE)
561  {
562  line = (LWLINE *)tmp;
563  ptarray[i] = ptarray_clone_deep(line->points);
564  }
565  else if (tmp->type == COMPOUNDTYPE)
566  {
567  line = lwcompound_linearize((LWCOMPOUND *)tmp, tol, tolerance_type, flags);
568  ptarray[i] = ptarray_clone_deep(line->points);
569  lwline_free(line);
570  }
571  else
572  {
573  lwerror("Invalid ring type found in CurvePoly.");
574  return NULL;
575  }
576  }
577 
578  ogeom = lwpoly_construct(curvepoly->srid, NULL, curvepoly->nrings, ptarray);
579  return ogeom;
580 }
581 
582 /* Kept for backward compatibility - TODO: drop */
583 LWPOLY *
584 lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
585 {
587 }
588 
589 
598 static LWMLINE *
599 lwmcurve_linearize(const LWMCURVE *mcurve, double tol,
601  int flags)
602 {
603  LWMLINE *ogeom;
604  LWGEOM **lines;
605  int i;
606 
607  LWDEBUGF(2, "lwmcurve_linearize called, geoms=%d, dim=%d.", mcurve->ngeoms, FLAGS_NDIMS(mcurve->flags));
608 
609  lines = lwalloc(sizeof(LWGEOM *)*mcurve->ngeoms);
610 
611  for (i = 0; i < mcurve->ngeoms; i++)
612  {
613  const LWGEOM *tmp = mcurve->geoms[i];
614  if (tmp->type == CIRCSTRINGTYPE)
615  {
616  lines[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags);
617  }
618  else if (tmp->type == LINETYPE)
619  {
620  lines[i] = (LWGEOM *)lwline_construct(mcurve->srid, NULL, ptarray_clone_deep(((LWLINE *)tmp)->points));
621  }
622  else if (tmp->type == COMPOUNDTYPE)
623  {
624  lines[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags);
625  }
626  else
627  {
628  lwerror("Unsupported geometry found in MultiCurve.");
629  return NULL;
630  }
631  }
632 
633  ogeom = (LWMLINE *)lwcollection_construct(MULTILINETYPE, mcurve->srid, NULL, mcurve->ngeoms, lines);
634  return ogeom;
635 }
636 
645 static LWMPOLY *
646 lwmsurface_linearize(const LWMSURFACE *msurface, double tol,
648  int flags)
649 {
650  LWMPOLY *ogeom;
651  LWGEOM *tmp;
652  LWPOLY *poly;
653  LWGEOM **polys;
654  POINTARRAY **ptarray;
655  int i, j;
656 
657  LWDEBUG(2, "lwmsurface_linearize called.");
658 
659  polys = lwalloc(sizeof(LWGEOM *)*msurface->ngeoms);
660 
661  for (i = 0; i < msurface->ngeoms; i++)
662  {
663  tmp = msurface->geoms[i];
664  if (tmp->type == CURVEPOLYTYPE)
665  {
666  polys[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags);
667  }
668  else if (tmp->type == POLYGONTYPE)
669  {
670  poly = (LWPOLY *)tmp;
671  ptarray = lwalloc(sizeof(POINTARRAY *)*poly->nrings);
672  for (j = 0; j < poly->nrings; j++)
673  {
674  ptarray[j] = ptarray_clone_deep(poly->rings[j]);
675  }
676  polys[i] = (LWGEOM *)lwpoly_construct(msurface->srid, NULL, poly->nrings, ptarray);
677  }
678  }
679  ogeom = (LWMPOLY *)lwcollection_construct(MULTIPOLYGONTYPE, msurface->srid, NULL, msurface->ngeoms, polys);
680  return ogeom;
681 }
682 
691 static LWCOLLECTION *
692 lwcollection_linearize(const LWCOLLECTION *collection, double tol,
694  int flags)
695 {
696  LWCOLLECTION *ocol;
697  LWGEOM *tmp;
698  LWGEOM **geoms;
699  int i;
700 
701  LWDEBUG(2, "lwcollection_linearize called.");
702 
703  geoms = lwalloc(sizeof(LWGEOM *)*collection->ngeoms);
704 
705  for (i=0; i<collection->ngeoms; i++)
706  {
707  tmp = collection->geoms[i];
708  switch (tmp->type)
709  {
710  case CIRCSTRINGTYPE:
711  geoms[i] = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)tmp, tol, type, flags);
712  break;
713  case COMPOUNDTYPE:
714  geoms[i] = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)tmp, tol, type, flags);
715  break;
716  case CURVEPOLYTYPE:
717  geoms[i] = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)tmp, tol, type, flags);
718  break;
719  case MULTICURVETYPE:
720  case MULTISURFACETYPE:
721  case COLLECTIONTYPE:
722  geoms[i] = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)tmp, tol, type, flags);
723  break;
724  default:
725  geoms[i] = lwgeom_clone(tmp);
726  break;
727  }
728  }
729  ocol = lwcollection_construct(COLLECTIONTYPE, collection->srid, NULL, collection->ngeoms, geoms);
730  return ocol;
731 }
732 
733 LWGEOM *
734 lwcurve_linearize(const LWGEOM *geom, double tol,
736  int flags)
737 {
738  LWGEOM * ogeom = NULL;
739  switch (geom->type)
740  {
741  case CIRCSTRINGTYPE:
742  ogeom = (LWGEOM *)lwcircstring_linearize((LWCIRCSTRING *)geom, tol, type, flags);
743  break;
744  case COMPOUNDTYPE:
745  ogeom = (LWGEOM *)lwcompound_linearize((LWCOMPOUND *)geom, tol, type, flags);
746  break;
747  case CURVEPOLYTYPE:
748  ogeom = (LWGEOM *)lwcurvepoly_linearize((LWCURVEPOLY *)geom, tol, type, flags);
749  break;
750  case MULTICURVETYPE:
751  ogeom = (LWGEOM *)lwmcurve_linearize((LWMCURVE *)geom, tol, type, flags);
752  break;
753  case MULTISURFACETYPE:
754  ogeom = (LWGEOM *)lwmsurface_linearize((LWMSURFACE *)geom, tol, type, flags);
755  break;
756  case COLLECTIONTYPE:
757  ogeom = (LWGEOM *)lwcollection_linearize((LWCOLLECTION *)geom, tol, type, flags);
758  break;
759  default:
760  ogeom = lwgeom_clone(geom);
761  }
762  return ogeom;
763 }
764 
765 /* Kept for backward compatibility - TODO: drop */
766 LWGEOM *
767 lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
768 {
770 }
771 
776 static double
777 lw_arc_angle(const POINT2D *a, const POINT2D *b, const POINT2D *c)
778 {
779  POINT2D ab, cb;
780 
781  ab.x = b->x - a->x;
782  ab.y = b->y - a->y;
783 
784  cb.x = b->x - c->x;
785  cb.y = b->y - c->y;
786 
787  double dot = (ab.x * cb.x + ab.y * cb.y); /* dot product */
788  double cross = (ab.x * cb.y - ab.y * cb.x); /* cross product */
789 
790  double alpha = atan2(cross, dot);
791 
792  return alpha;
793 }
794 
799 static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b)
800 {
801  POINT2D center;
802  POINT2D *t1 = (POINT2D*)a1;
803  POINT2D *t2 = (POINT2D*)a2;
804  POINT2D *t3 = (POINT2D*)a3;
805  POINT2D *tb = (POINT2D*)b;
806  double radius = lw_arc_center(t1, t2, t3, &center);
807  double b_distance, diff;
808 
809  /* Co-linear a1/a2/a3 */
810  if ( radius < 0.0 )
811  return LW_FALSE;
812 
813  b_distance = distance2d_pt_pt(tb, &center);
814  diff = fabs(radius - b_distance);
815  LWDEBUGF(4, "circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius);
816 
817  /* Is the point b on the circle? */
818  if ( diff < EPSILON_SQLMM )
819  {
820  int a2_side = lw_segment_side(t1, t3, t2);
821  int b_side = lw_segment_side(t1, t3, tb);
822  double angle1 = lw_arc_angle(t1, t2, t3);
823  double angle2 = lw_arc_angle(t2, t3, tb);
824 
825  /* Is the angle similar to the previous one ? */
826  diff = fabs(angle1 - angle2);
827  LWDEBUGF(4, " angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff);
828  if ( diff > EPSILON_SQLMM )
829  {
830  return LW_FALSE;
831  }
832 
833  /* Is the point b on the same side of a1/a3 as the mid-point a2 is? */
834  /* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */
835  if ( b_side != a2_side )
836  return LW_TRUE;
837  }
838  return LW_FALSE;
839 }
840 
841 static LWGEOM*
842 linestring_from_pa(const POINTARRAY *pa, int srid, int start, int end)
843 {
844  int i = 0, j = 0;
845  POINT4D p;
846  POINTARRAY *pao = ptarray_construct(ptarray_has_z(pa), ptarray_has_m(pa), end-start+2);
847  LWDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end);
848  for( i = start; i < end + 2; i++ )
849  {
850  getPoint4d_p(pa, i, &p);
851  ptarray_set_point4d(pao, j++, &p);
852  }
853  return lwline_as_lwgeom(lwline_construct(srid, NULL, pao));
854 }
855 
856 static LWGEOM*
857 circstring_from_pa(const POINTARRAY *pa, int srid, int start, int end)
858 {
859 
860  POINT4D p0, p1, p2;
862  LWDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end);
863  getPoint4d_p(pa, start, &p0);
864  ptarray_set_point4d(pao, 0, &p0);
865  getPoint4d_p(pa, (start+end+1)/2, &p1);
866  ptarray_set_point4d(pao, 1, &p1);
867  getPoint4d_p(pa, end+1, &p2);
868  ptarray_set_point4d(pao, 2, &p2);
869  return lwcircstring_as_lwgeom(lwcircstring_construct(srid, NULL, pao));
870 }
871 
872 static LWGEOM*
873 geom_from_pa(const POINTARRAY *pa, int srid, int is_arc, int start, int end)
874 {
875  LWDEBUGF(4, "srid=%d, is_arc=%d, start=%d, end=%d", srid, is_arc, start, end);
876  if ( is_arc )
877  return circstring_from_pa(pa, srid, start, end);
878  else
879  return linestring_from_pa(pa, srid, start, end);
880 }
881 
882 LWGEOM*
883 pta_unstroke(const POINTARRAY *points, int type, int srid)
884 {
885  int i = 0, j, k;
886  POINT4D a1, a2, a3, b;
887  POINT4D first, center;
888  char *edges_in_arcs;
889  int found_arc = LW_FALSE;
890  int current_arc = 1;
891  int num_edges;
892  int edge_type; /* non-zero if edge is part of an arc */
893  int start, end;
894  LWCOLLECTION *outcol;
895  /* Minimum number of edges, per quadrant, required to define an arc */
896  const unsigned int min_quad_edges = 2;
897 
898  /* Die on null input */
899  if ( ! points )
900  lwerror("pta_unstroke called with null pointarray");
901 
902  /* Null on empty input? */
903  if ( points->npoints == 0 )
904  return NULL;
905 
906  /* We can't desegmentize anything shorter than four points */
907  if ( points->npoints < 4 )
908  {
909  /* Return a linestring here*/
910  lwerror("pta_unstroke needs implementation for npoints < 4");
911  }
912 
913  /* Allocate our result array of vertices that are part of arcs */
914  num_edges = points->npoints - 1;
915  edges_in_arcs = lwalloc(num_edges + 1);
916  memset(edges_in_arcs, 0, num_edges + 1);
917 
918  /* We make a candidate arc of the first two edges, */
919  /* And then see if the next edge follows it */
920  while( i < num_edges-2 )
921  {
922  unsigned int arc_edges;
923  double num_quadrants;
924  double angle;
925 
926  found_arc = LW_FALSE;
927  /* Make candidate arc */
928  getPoint4d_p(points, i , &a1);
929  getPoint4d_p(points, i+1, &a2);
930  getPoint4d_p(points, i+2, &a3);
931  memcpy(&first, &a1, sizeof(POINT4D));
932 
933  for( j = i+3; j < num_edges+1; j++ )
934  {
935  LWDEBUGF(4, "i=%d, j=%d", i, j);
936  getPoint4d_p(points, j, &b);
937  /* Does this point fall on our candidate arc? */
938  if ( pt_continues_arc(&a1, &a2, &a3, &b) )
939  {
940  /* Yes. Mark this edge and the two preceding it as arc components */
941  LWDEBUGF(4, "pt_continues_arc #%d", current_arc);
942  found_arc = LW_TRUE;
943  for ( k = j-1; k > j-4; k-- )
944  edges_in_arcs[k] = current_arc;
945  }
946  else
947  {
948  /* No. So we're done with this candidate arc */
949  LWDEBUG(4, "pt_continues_arc = false");
950  current_arc++;
951  break;
952  }
953 
954  memcpy(&a1, &a2, sizeof(POINT4D));
955  memcpy(&a2, &a3, sizeof(POINT4D));
956  memcpy(&a3, &b, sizeof(POINT4D));
957  }
958  /* Jump past all the edges that were added to the arc */
959  if ( found_arc )
960  {
961  /* Check if an arc was composed by enough edges to be
962  * really considered an arc
963  * See http://trac.osgeo.org/postgis/ticket/2420
964  */
965  arc_edges = j - 1 - i;
966  LWDEBUGF(4, "arc defined by %d edges found", arc_edges);
967  if ( first.x == b.x && first.y == b.y ) {
968  LWDEBUG(4, "arc is a circle");
969  num_quadrants = 4;
970  }
971  else {
972  lw_arc_center((POINT2D*)&first, (POINT2D*)&b, (POINT2D*)&a1, (POINT2D*)&center);
973  angle = lw_arc_angle((POINT2D*)&first, (POINT2D*)&center, (POINT2D*)&b);
974  int p2_side = lw_segment_side((POINT2D*)&first, (POINT2D*)&a1, (POINT2D*)&b);
975  if ( p2_side >= 0 ) angle = -angle;
976 
977  if ( angle < 0 ) angle = 2 * M_PI + angle;
978  num_quadrants = ( 4 * angle ) / ( 2 * M_PI );
979  LWDEBUGF(4, "arc angle (%g %g, %g %g, %g %g) is %g (side is %d), quandrants:%g", first.x, first.y, center.x, center.y, b.x, b.y, angle, p2_side, num_quadrants);
980  }
981  /* a1 is first point, b is last point */
982  if ( arc_edges < min_quad_edges * num_quadrants ) {
983  LWDEBUGF(4, "Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants);
984  for ( k = j-1; k >= i; k-- )
985  edges_in_arcs[k] = 0;
986  }
987 
988  i = j-1;
989  }
990  else
991  {
992  /* Mark this edge as a linear edge */
993  edges_in_arcs[i] = 0;
994  i = i+1;
995  }
996  }
997 
998 #if POSTGIS_DEBUG_LEVEL > 3
999  {
1000  char *edgestr = lwalloc(num_edges+1);
1001  for ( i = 0; i < num_edges; i++ )
1002  {
1003  if ( edges_in_arcs[i] )
1004  edgestr[i] = 48 + edges_in_arcs[i];
1005  else
1006  edgestr[i] = '.';
1007  }
1008  edgestr[num_edges] = 0;
1009  LWDEBUGF(3, "edge pattern %s", edgestr);
1010  lwfree(edgestr);
1011  }
1012 #endif
1013 
1014  start = 0;
1015  edge_type = edges_in_arcs[0];
1016  outcol = lwcollection_construct_empty(COMPOUNDTYPE, srid, ptarray_has_z(points), ptarray_has_m(points));
1017  for( i = 1; i < num_edges; i++ )
1018  {
1019  if( edge_type != edges_in_arcs[i] )
1020  {
1021  end = i - 1;
1022  lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end));
1023  start = i;
1024  edge_type = edges_in_arcs[i];
1025  }
1026  }
1027  lwfree(edges_in_arcs); /* not needed anymore */
1028 
1029  /* Roll out last item */
1030  end = num_edges - 1;
1031  lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end));
1032 
1033  /* Strip down to singleton if only one entry */
1034  if ( outcol->ngeoms == 1 )
1035  {
1036  LWGEOM *outgeom = outcol->geoms[0];
1037  outcol->ngeoms = 0; lwcollection_free(outcol);
1038  return outgeom;
1039  }
1040  return lwcollection_as_lwgeom(outcol);
1041 }
1042 
1043 
1044 LWGEOM *
1046 {
1047  LWDEBUG(2, "lwline_unstroke called.");
1048 
1049  if ( line->points->npoints < 4 ) return lwline_as_lwgeom(lwline_clone(line));
1050  else return pta_unstroke(line->points, line->flags, line->srid);
1051 }
1052 
1053 LWGEOM *
1055 {
1056  LWGEOM **geoms;
1057  int i, hascurve = 0;
1058 
1059  LWDEBUG(2, "lwpolygon_unstroke called.");
1060 
1061  geoms = lwalloc(sizeof(LWGEOM *)*poly->nrings);
1062  for (i=0; i<poly->nrings; i++)
1063  {
1064  geoms[i] = pta_unstroke(poly->rings[i], poly->flags, poly->srid);
1065  if (geoms[i]->type == CIRCSTRINGTYPE || geoms[i]->type == COMPOUNDTYPE)
1066  {
1067  hascurve = 1;
1068  }
1069  }
1070  if (hascurve == 0)
1071  {
1072  for (i=0; i<poly->nrings; i++)
1073  {
1074  lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */
1075  }
1076  return lwgeom_clone((LWGEOM *)poly);
1077  }
1078 
1079  return (LWGEOM *)lwcollection_construct(CURVEPOLYTYPE, poly->srid, NULL, poly->nrings, geoms);
1080 }
1081 
1082 LWGEOM *
1084 {
1085  LWGEOM **geoms;
1086  int i, hascurve = 0;
1087 
1088  LWDEBUG(2, "lwmline_unstroke called.");
1089 
1090  geoms = lwalloc(sizeof(LWGEOM *)*mline->ngeoms);
1091  for (i=0; i<mline->ngeoms; i++)
1092  {
1093  geoms[i] = lwline_unstroke((LWLINE *)mline->geoms[i]);
1094  if (geoms[i]->type == CIRCSTRINGTYPE || geoms[i]->type == COMPOUNDTYPE)
1095  {
1096  hascurve = 1;
1097  }
1098  }
1099  if (hascurve == 0)
1100  {
1101  for (i=0; i<mline->ngeoms; i++)
1102  {
1103  lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */
1104  }
1105  return lwgeom_clone((LWGEOM *)mline);
1106  }
1107  return (LWGEOM *)lwcollection_construct(MULTICURVETYPE, mline->srid, NULL, mline->ngeoms, geoms);
1108 }
1109 
1110 LWGEOM *
1112 {
1113  LWGEOM **geoms;
1114  int i, hascurve = 0;
1115 
1116  LWDEBUG(2, "lwmpoly_unstroke called.");
1117 
1118  geoms = lwalloc(sizeof(LWGEOM *)*mpoly->ngeoms);
1119  for (i=0; i<mpoly->ngeoms; i++)
1120  {
1121  geoms[i] = lwpolygon_unstroke((LWPOLY *)mpoly->geoms[i]);
1122  if (geoms[i]->type == CURVEPOLYTYPE)
1123  {
1124  hascurve = 1;
1125  }
1126  }
1127  if (hascurve == 0)
1128  {
1129  for (i=0; i<mpoly->ngeoms; i++)
1130  {
1131  lwfree(geoms[i]); /* TODO: should this be lwgeom_free instead ? */
1132  }
1133  return lwgeom_clone((LWGEOM *)mpoly);
1134  }
1135  return (LWGEOM *)lwcollection_construct(MULTISURFACETYPE, mpoly->srid, NULL, mpoly->ngeoms, geoms);
1136 }
1137 
1138 LWGEOM *
1140 {
1141  LWDEBUG(2, "lwgeom_unstroke called.");
1142 
1143  switch (geom->type)
1144  {
1145  case LINETYPE:
1146  return lwline_unstroke((LWLINE *)geom);
1147  case POLYGONTYPE:
1148  return lwpolygon_unstroke((LWPOLY *)geom);
1149  case MULTILINETYPE:
1150  return lwmline_unstroke((LWMLINE *)geom);
1151  case MULTIPOLYGONTYPE:
1152  return lwmpolygon_unstroke((LWMPOLY *)geom);
1153  default:
1154  return lwgeom_clone(geom);
1155  }
1156 }
1157 
void ptarray_set_point4d(POINTARRAY *pa, int n, const POINT4D *p4d)
Definition: lwgeom_api.c:437
int ngeoms
Definition: liblwgeom.h:546
int32_t srid
Definition: liblwgeom.h:519
double x
Definition: liblwgeom.h:352
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:227
#define LINETYPE
Definition: liblwgeom.h:86
static LWMLINE * lwmcurve_linearize(const LWMCURVE *mcurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:599
LWGEOM * lwgeom_unstroke(const LWGEOM *geom)
Definition: lwstroke.c:1139
uint8_t flags
Definition: liblwgeom.h:543
POINTARRAY * ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance)
Definition: ptarray.c:1546
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:777
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:62
double m
Definition: liblwgeom.h:352
#define MULTICURVETYPE
Definition: liblwgeom.h:95
LWCOLLECTION * lwcollection_construct(uint8_t type, int srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:43
Tolerance expresses the maximum angle between the radii generating approximation line vertices...
Definition: liblwgeom.h:2217
void lwfree(void *mem)
Definition: lwutil.c:244
LWGEOM ** rings
Definition: liblwgeom.h:535
LWLINE * lwline_clone(const LWLINE *lwgeom)
Definition: lwline.c:102
int npoints
Definition: liblwgeom.h:371
int32_t srid
Definition: liblwgeom.h:443
#define POLYGONTYPE
Definition: liblwgeom.h:87
LWGEOM * lwcircstring_as_lwgeom(const LWCIRCSTRING *obj)
Definition: lwgeom.c:273
#define CURVEPOLYTYPE
Definition: liblwgeom.h:94
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:70
void ptarray_free(POINTARRAY *pa)
Definition: ptarray.c:330
#define COMPOUNDTYPE
Definition: liblwgeom.h:93
#define MULTIPOINTTYPE
Definition: liblwgeom.h:88
static LWLINE * lwcompound_linearize(const LWCOMPOUND *icompound, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:472
void lwline_free(LWLINE *line)
Definition: lwline.c:76
int32_t srid
Definition: liblwgeom.h:545
static LWPOLY * lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:537
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define TRIANGLETYPE
Definition: liblwgeom.h:98
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:97
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:799
static LWMPOLY * lwmsurface_linearize(const LWMSURFACE *msurface, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:646
LWGEOM * pta_unstroke(const POINTARRAY *points, int type, int srid)
Definition: lwstroke.c:883
LW_LINEARIZE_TOLERANCE_TYPE
Semantic of the tolerance argument passed to lwcurve_linearize.
Definition: liblwgeom.h:2196
static LWGEOM * circstring_from_pa(const POINTARRAY *pa, int srid, int start, int end)
Definition: lwstroke.c:857
double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
The old function nessecary for ptarray_segmentize2d in ptarray.c.
Definition: measures.c:2317
Tolerance expresses the maximum distance between an arbitrary point on the curve and the closest poin...
Definition: liblwgeom.h:2209
int32_t srid
Definition: liblwgeom.h:421
LWGEOM ** geoms
Definition: liblwgeom.h:522
uint8_t flags
Definition: liblwgeom.h:517
int ngeoms
Definition: liblwgeom.h:481
LWPOLY * lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
Definition: lwstroke.c:584
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:134
unsigned int uint32_t
Definition: uthash.h:78
double x
Definition: liblwgeom.h:328
LWGEOM * lwpolygon_unstroke(const LWPOLY *poly)
Definition: lwstroke.c:1054
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:218
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:298
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE, then a duplicate point will not be added.
Definition: ptarray.c:156
int32_t srid
Definition: liblwgeom.h:532
#define LW_FALSE
Definition: liblwgeom.h:77
uint8_t flags
Definition: liblwgeom.h:369
LWPOLY * lwpoly_construct(int srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:43
LWPOLY ** geoms
Definition: liblwgeom.h:496
#define EPSILON_SQLMM
Tolerance used to determine equality.
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:76
LWLINE * lwline_construct(int srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
LWGEOM ** geoms
Definition: liblwgeom.h:509
LWLINE * lwcompound_stroke(const LWCOMPOUND *icompound, uint32_t perQuad)
Definition: lwstroke.c:522
static LWGEOM * geom_from_pa(const POINTARRAY *pa, int srid, int is_arc, int start, int end)
Definition: lwstroke.c:873
#define TINTYPE
Definition: liblwgeom.h:99
POINTARRAY ** rings
Definition: liblwgeom.h:457
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:630
static LWLINE * lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:412
int nrings
Definition: liblwgeom.h:455
int32_t srid
Definition: liblwgeom.h:506
double y
Definition: liblwgeom.h:328
#define FLAGS_GET_Z(flags)
Macros for manipulating the &#39;flags&#39; byte.
Definition: liblwgeom.h:140
static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
Definition: lwstroke.c:95
double z
Definition: liblwgeom.h:352
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition: lwgeom.c:444
int ngeoms
Definition: liblwgeom.h:494
LWCIRCSTRING * lwcircstring_construct(int srid, GBOX *bbox, POINTARRAY *points)
Definition: lwcircstring.c:51
Symmetric linearization means that the output vertices would be the same no matter the order of the p...
Definition: liblwgeom.h:2226
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:90
LWLINE ** geoms
Definition: liblwgeom.h:483
int ngeoms
Definition: liblwgeom.h:559
Tolerance expresses the number of segments to use for each quarter of circle (quadrant).
Definition: liblwgeom.h:2202
int ptarray_has_m(const POINTARRAY *pa)
Definition: ptarray.c:43
#define MULTISURFACETYPE
Definition: liblwgeom.h:96
int lwgeom_has_arc(const LWGEOM *geom)
Definition: lwstroke.c:54
int32_t srid
Definition: liblwgeom.h:454
LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:734
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
Definition: lwstroke.c:767
LWGEOM ** geoms
Definition: liblwgeom.h:561
int ptarray_remove_point(POINTARRAY *pa, int where)
Remove a point from an existing POINTARRAY.
Definition: ptarray.c:261
int ngeoms
Definition: liblwgeom.h:520
LWGEOM * lwline_unstroke(const LWLINE *line)
Definition: lwstroke.c:1045
LWGEOM ** geoms
Definition: liblwgeom.h:548
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:85
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:141
Retain angle instructs the engine to try its best to retain the requested angle between generating ra...
Definition: liblwgeom.h:2246
int32_t srid
Definition: liblwgeom.h:558
uint8_t type
Definition: liblwgeom.h:396
type
Definition: ovdump.py:41
static LWGEOM * linestring_from_pa(const POINTARRAY *pa, int srid, int start, int end)
Definition: lwstroke.c:842
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:340
POINTARRAY * points
Definition: liblwgeom.h:444
static LWCOLLECTION * lwcollection_linearize(const LWCOLLECTION *collection, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:692
uint8_t flags
Definition: liblwgeom.h:452
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:92
int ptarray_has_z(const POINTARRAY *pa)
Definition: ptarray.c:36
void * lwalloc(size_t size)
Definition: lwutil.c:229
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:64
double y
Definition: liblwgeom.h:352
#define MULTILINETYPE
Definition: liblwgeom.h:89
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
Definition: lwcollection.c:94
uint8_t flags
Definition: liblwgeom.h:419
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
#define FLAGS_NDIMS(flags)
Definition: liblwgeom.h:152
LWGEOM * lwmpolygon_unstroke(const LWMPOLY *mpoly)
Definition: lwstroke.c:1111
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:187
int32_t srid
Definition: liblwgeom.h:493
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
int getPoint4d_p(const POINTARRAY *pa, int n, POINT4D *point)
Definition: lwgeom_api.c:122
#define COLLECTIONTYPE
Definition: liblwgeom.h:91
LWGEOM * lwmline_unstroke(const LWMLINE *mline)
Definition: lwstroke.c:1083
int32_t srid
Definition: liblwgeom.h:480
POINTARRAY * points
Definition: liblwgeom.h:422
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:268