PostGIS  3.0.6dev-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  * 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  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 
96 static 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. */
143 static 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. */
209 static 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 
238 static 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);
478  ptarray_append_point(pa, &pt, LW_FALSE);
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);
495  ptarray_append_point(to, &pt, LW_FALSE);
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  */
511 static LWLINE *
512 lwcircstring_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  */
571 static LWLINE *
572 lwcompound_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  */
628 static LWPOLY *
629 lwcurvepoly_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 */
675 LWPOLY *
676 lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
677 {
679 }
680 
681 
690 static LWMLINE *
691 lwmcurve_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 
737 static LWMPOLY *
738 lwmsurface_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 
783 static LWCOLLECTION *
784 lwcollection_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 
825 LWGEOM *
826 lwcurve_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 */
858 LWGEOM *
859 lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
860 {
862 }
863 
868 static double
869 lw_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 
891 static 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 
933 static LWGEOM *
934 linestring_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 
948 static LWGEOM *
949 circstring_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 
964 static LWGEOM *
965 geom_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 
974 LWGEOM *
975 pta_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 
1136 LWGEOM *
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 
1145 LWGEOM *
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 
1174 LWGEOM *
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 
1202 LWGEOM *
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 
1230 LWGEOM *
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 
1258 LWGEOM *
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
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: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
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:321
#define LW_FALSE
Definition: liblwgeom.h:108
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:291
#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_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:626
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:511
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:116
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:179
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
#define TINTYPE
Definition: liblwgeom.h:130
#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
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:128
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:123
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:180
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:296
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
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:107
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:376
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:1452
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:229
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: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
LWGEOM * lwgeom_unstroke(const LWGEOM *geom)
Definition: lwstroke.c:1259
static LWLINE * lwcircstring_linearize(const LWCIRCSTRING *icurve, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:512
static double angle_increment_using_max_deviation(double max_deviation, double radius)
Definition: lwstroke.c:143
LWGEOM * lwcurve_linearize(const LWGEOM *geom, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:826
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
Definition: lwstroke.c:859
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 LWGEOM * linestring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
Definition: lwstroke.c:934
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 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 LWGEOM * circstring_from_pa(const POINTARRAY *pa, int32_t srid, int start, int end)
Definition: lwstroke.c:949
static double angle_increment_using_segments_per_quad(double tol)
Definition: lwstroke.c:119
static LWMPOLY * lwmsurface_linearize(const LWMSURFACE *msurface, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:738
static LWLINE * lwcompound_linearize(const LWCOMPOUND *icompound, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:572
static LWCOLLECTION * lwcollection_linearize(const LWCOLLECTION *collection, double tol, LW_LINEARIZE_TOLERANCE_TYPE type, int flags)
Definition: lwstroke.c:784
static LWGEOM * geom_from_pa(const POINTARRAY *pa, int32_t srid, int is_arc, int start, int end)
Definition: lwstroke.c:965
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
LWPOLY * lwcurvepoly_stroke(const LWCURVEPOLY *curvepoly, uint32_t perQuad)
Definition: lwstroke.c:676
LWGEOM * lwpolygon_unstroke(const LWPOLY *poly)
Definition: lwstroke.c:1146
static double angle_increment_using_max_angle(double tol)
Definition: lwstroke.c:209
static LWPOLY * lwcurvepoly_linearize(const LWCURVEPOLY *curvepoly, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags)
Definition: lwstroke.c:629
LWGEOM * pta_unstroke(const POINTARRAY *points, int32_t srid)
Definition: lwstroke.c:975
LWGEOM * lwcollection_unstroke(const LWCOLLECTION *c)
Definition: lwstroke.c:1231
LWGEOM * lwmline_unstroke(const LWMLINE *mline)
Definition: lwstroke.c:1175
LWGEOM * lwline_unstroke(const LWLINE *line)
Definition: lwstroke.c:1137
int lwgeom_has_arc(const LWGEOM *geom)
Definition: lwstroke.c:55
type
Definition: ovdump.py:42
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