PostGIS  3.4.0dev-r@@SVN_REVISION@@
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
lwgeom_functions_analytic.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-2005 Refractions Research Inc.
22  *
23  **********************************************************************/
24 
25 
26 #include "postgres.h"
27 #include "funcapi.h"
28 #include "fmgr.h"
29 #include "liblwgeom.h"
30 #include "liblwgeom_internal.h" /* For FP comparators. */
31 #include "lwgeom_pg.h"
32 #include "math.h"
33 #include "lwgeom_rtree.h"
35 
36 #include "access/htup_details.h"
37 
38 /* Prototypes */
39 Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS);
40 Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS);
41 Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS);
42 Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS);
43 Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS);
44 Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS);
45 Datum ST_GeometricMedian(PG_FUNCTION_ARGS);
46 Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS);
47 Datum ST_IsPolygonCW(PG_FUNCTION_ARGS);
48 
49 
50 static double determineSide(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point);
51 static int isOnSegment(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point);
52 static int point_in_ring(POINTARRAY *pts, const POINT2D *point);
53 static int point_in_ring_rtree(RTREE_NODE *root, const POINT2D *point);
54 
55 /***********************************************************************
56  * Simple Douglas-Peucker line simplification.
57  * No checks are done to avoid introduction of self-intersections.
58  * No topology relations are considered.
59  *
60  * --strk@kbt.io;
61  ***********************************************************************/
62 
64 Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS)
65 {
66  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P_COPY(0);
67  double dist = PG_GETARG_FLOAT8(1);
69  int type = gserialized_get_type(geom);
70  LWGEOM *in;
71  bool preserve_collapsed = false;
72  int modified = LW_FALSE;
73 
74  /* Can't simplify points! */
75  if ( type == POINTTYPE || type == MULTIPOINTTYPE )
76  PG_RETURN_POINTER(geom);
77 
78  /* Handle optional argument to preserve collapsed features */
79  if ((PG_NARGS() > 2) && (!PG_ARGISNULL(2)))
80  preserve_collapsed = PG_GETARG_BOOL(2);
81 
82  in = lwgeom_from_gserialized(geom);
83 
84  modified = lwgeom_simplify_in_place(in, dist, preserve_collapsed);
85  if (!modified)
86  PG_RETURN_POINTER(geom);
87 
88  if (!in || lwgeom_is_empty(in))
89  PG_RETURN_NULL();
90 
91  result = geometry_serialize(in);
92  PG_RETURN_POINTER(result);
93 }
94 
96 Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS)
97 {
98  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
100  int type = gserialized_get_type(geom);
101  LWGEOM *in;
102  LWGEOM *out;
103  double area=0;
104  int set_area=0;
105 
106  if ( type == POINTTYPE || type == MULTIPOINTTYPE )
107  PG_RETURN_POINTER(geom);
108 
109  if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
110  area = PG_GETARG_FLOAT8(1);
111 
112  if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) )
113  set_area = PG_GETARG_INT32(2);
114 
115  in = lwgeom_from_gserialized(geom);
116 
117  out = lwgeom_set_effective_area(in,set_area, area);
118  if ( ! out ) PG_RETURN_NULL();
119 
120  /* COMPUTE_BBOX TAINTING */
121  if ( in->bbox ) lwgeom_add_bbox(out);
122 
123  result = geometry_serialize(out);
124  lwgeom_free(out);
125  PG_FREE_IF_COPY(geom, 0);
126  PG_RETURN_POINTER(result);
127 }
128 
130 Datum LWGEOM_ChaikinSmoothing(PG_FUNCTION_ARGS)
131 {
132  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
134  int type = gserialized_get_type(geom);
135  LWGEOM *in;
136  LWGEOM *out;
137  int preserve_endpoints=1;
138  int n_iterations=1;
139 
140  if ( type == POINTTYPE || type == MULTIPOINTTYPE )
141  PG_RETURN_POINTER(geom);
142 
143  if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
144  n_iterations = PG_GETARG_INT32(1);
145 
146  if (n_iterations< 1 || n_iterations>5)
147  elog(ERROR,"Number of iterations must be between 1 and 5 : %s", __func__);
148 
149  if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) )
150  {
151  if(PG_GETARG_BOOL(2))
152  preserve_endpoints = 1;
153  else
154  preserve_endpoints = 0;
155  }
156 
157  in = lwgeom_from_gserialized(geom);
158 
159  out = lwgeom_chaikin(in, n_iterations, preserve_endpoints);
160  if ( ! out ) PG_RETURN_NULL();
161 
162  /* COMPUTE_BBOX TAINTING */
163  if ( in->bbox ) lwgeom_add_bbox(out);
164 
165  result = geometry_serialize(out);
166  lwgeom_free(out);
167  PG_FREE_IF_COPY(geom, 0);
168  PG_RETURN_POINTER(result);
169 }
170 
171 
172 /***********************************************************************
173  * --strk@kbt.io;
174  ***********************************************************************/
175 
176 /***********************************************************************
177  * Interpolate a point along a line, useful for Geocoding applications
178  * SELECT line_interpolate_point( 'LINESTRING( 0 0, 2 2'::geometry, .5 )
179  * returns POINT( 1 1 ).
180  ***********************************************************************/
182 Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS)
183 {
184  GSERIALIZED *gser = PG_GETARG_GSERIALIZED_P(0);
186  double distance_fraction = PG_GETARG_FLOAT8(1);
187  int repeat = PG_NARGS() > 2 && PG_GETARG_BOOL(2);
188  int32_t srid = gserialized_get_srid(gser);
189  LWLINE* lwline;
190  LWGEOM* lwresult;
191  POINTARRAY* opa;
192 
193  if ( distance_fraction < 0 || distance_fraction > 1 )
194  {
195  elog(ERROR,"line_interpolate_point: 2nd arg isn't within [0,1]");
196  PG_FREE_IF_COPY(gser, 0);
197  PG_RETURN_NULL();
198  }
199 
200  if ( gserialized_get_type(gser) != LINETYPE )
201  {
202  elog(ERROR,"line_interpolate_point: 1st arg isn't a line");
203  PG_FREE_IF_COPY(gser, 0);
204  PG_RETURN_NULL();
205  }
206 
208  opa = lwline_interpolate_points(lwline, distance_fraction, repeat);
209 
210  lwgeom_free(lwline_as_lwgeom(lwline));
211  PG_FREE_IF_COPY(gser, 0);
212 
213  if (opa->npoints <= 1)
214  {
215  lwresult = lwpoint_as_lwgeom(lwpoint_construct(srid, NULL, opa));
216  } else {
217  lwresult = lwmpoint_as_lwgeom(lwmpoint_construct(srid, opa));
218  }
219 
220  result = geometry_serialize(lwresult);
221  lwgeom_free(lwresult);
222 
223  PG_RETURN_POINTER(result);
224 }
225 
226 /***********************************************************************
227  * Interpolate a point along a line 3D version
228  * --vincent.mora@oslandia.com;
229  ***********************************************************************/
230 
231 Datum ST_3DLineInterpolatePoint(PG_FUNCTION_ARGS);
232 
234 Datum ST_3DLineInterpolatePoint(PG_FUNCTION_ARGS)
235 {
236  GSERIALIZED *gser = PG_GETARG_GSERIALIZED_P(0);
238  double distance = PG_GETARG_FLOAT8(1);
239  LWLINE *line;
240  LWGEOM *geom;
241  LWPOINT *point;
242 
243  if (distance < 0 || distance > 1)
244  {
245  elog(ERROR, "line_interpolate_point: 2nd arg isn't within [0,1]");
246  PG_RETURN_NULL();
247  }
248 
249  if (gserialized_get_type(gser) != LINETYPE)
250  {
251  elog(ERROR, "line_interpolate_point: 1st arg isn't a line");
252  PG_RETURN_NULL();
253  }
254 
255  geom = lwgeom_from_gserialized(gser);
256  line = lwgeom_as_lwline(geom);
257 
258  point = lwline_interpolate_point_3d(line, distance);
259 
260  lwgeom_free(geom);
261  PG_FREE_IF_COPY(gser, 0);
262 
263  result = geometry_serialize(lwpoint_as_lwgeom(point));
264  lwpoint_free(point);
265 
266  PG_RETURN_POINTER(result);
267 }
268 
269 /***********************************************************************
270  * --jsunday@rochgrp.com;
271  ***********************************************************************/
272 
273 /***********************************************************************
274  *
275  * Grid application function for postgis.
276  *
277  * WHAT IS
278  * -------
279  *
280  * This is a grid application function for postgis.
281  * You use it to stick all of a geometry points to
282  * a custom grid defined by its origin and cell size
283  * in geometry units.
284  *
285  * Points reduction is obtained by collapsing all
286  * consecutive points falling on the same grid node and
287  * removing all consecutive segments S1,S2 having
288  * S2.startpoint = S1.endpoint and S2.endpoint = S1.startpoint.
289  *
290  * ISSUES
291  * ------
292  *
293  * Only 2D is contemplated in grid application.
294  *
295  * Consecutive coincident segments removal does not work
296  * on first/last segments (They are not considered consecutive).
297  *
298  * Grid application occurs on a geometry subobject basis, thus no
299  * points reduction occurs for multipoint geometries.
300  *
301  * USAGE TIPS
302  * ----------
303  *
304  * Run like this:
305  *
306  * SELECT SnapToGrid(<geometry>, <originX>, <originY>, <sizeX>, <sizeY>);
307  *
308  * Grid cells are not visible on a screen as long as the screen
309  * point size is equal or bigger then the grid cell size.
310  * This assumption may be used to reduce the number of points in
311  * a map for a given display scale.
312  *
313  * Keeping multiple resolution versions of the same data may be used
314  * in conjunction with MINSCALE/MAXSCALE keywords of mapserv to speed
315  * up rendering.
316  *
317  * Check also the use of DP simplification to reduce grid visibility.
318  * I'm still researching about the relationship between grid size and
319  * DP epsilon values - please tell me if you know more about this.
320  *
321  *
322  * --strk@kbt.io;
323  *
324  ***********************************************************************/
325 
326 #define CHECK_RING_IS_CLOSE
327 #define SAMEPOINT(a,b) ((a)->x==(b)->x&&(a)->y==(b)->y)
328 
329 
330 
331 /* Forward declarations */
332 Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS);
333 Datum LWGEOM_snaptogrid_pointoff(PG_FUNCTION_ARGS);
334 
335 
336 
338 Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS)
339 {
340  LWGEOM *in_lwgeom;
341  GSERIALIZED *out_geom = NULL;
342  LWGEOM *out_lwgeom;
343  gridspec grid;
344 
345  GSERIALIZED *in_geom = PG_GETARG_GSERIALIZED_P(0);
346 
347  /* Set grid values to zero to start */
348  memset(&grid, 0, sizeof(gridspec));
349 
350  grid.ipx = PG_GETARG_FLOAT8(1);
351  grid.ipy = PG_GETARG_FLOAT8(2);
352  grid.xsize = PG_GETARG_FLOAT8(3);
353  grid.ysize = PG_GETARG_FLOAT8(4);
354 
355  /* Return input geometry if input geometry is empty */
356  if ( gserialized_is_empty(in_geom) )
357  {
358  PG_RETURN_POINTER(in_geom);
359  }
360 
361  /* Return input geometry if input grid is meaningless */
362  if ( grid.xsize==0 && grid.ysize==0 && grid.zsize==0 && grid.msize==0 )
363  {
364  PG_RETURN_POINTER(in_geom);
365  }
366 
367  in_lwgeom = lwgeom_from_gserialized(in_geom);
368 
369  POSTGIS_DEBUGF(3, "SnapToGrid got a %s", lwtype_name(in_lwgeom->type));
370 
371  out_lwgeom = lwgeom_grid(in_lwgeom, &grid);
372  if ( out_lwgeom == NULL ) PG_RETURN_NULL();
373 
374  /* COMPUTE_BBOX TAINTING */
375  if ( in_lwgeom->bbox )
376  lwgeom_refresh_bbox(out_lwgeom);
377 
378  POSTGIS_DEBUGF(3, "SnapToGrid made a %s", lwtype_name(out_lwgeom->type));
379 
380  out_geom = geometry_serialize(out_lwgeom);
381 
382  PG_RETURN_POINTER(out_geom);
383 }
384 
385 
386 #if POSTGIS_DEBUG_LEVEL >= 4
387 /* Print grid using given reporter */
388 static void
389 grid_print(const gridspec *grid)
390 {
391  lwpgnotice("GRID(%g %g %g %g, %g %g %g %g)",
392  grid->ipx, grid->ipy, grid->ipz, grid->ipm,
393  grid->xsize, grid->ysize, grid->zsize, grid->msize);
394 }
395 #endif
396 
397 
399 Datum LWGEOM_snaptogrid_pointoff(PG_FUNCTION_ARGS)
400 {
401  GSERIALIZED *in_geom, *in_point;
402  LWGEOM *in_lwgeom;
403  LWPOINT *in_lwpoint;
404  GSERIALIZED *out_geom = NULL;
405  LWGEOM *out_lwgeom;
406  gridspec grid;
407  /* BOX3D box3d; */
408  POINT4D offsetpoint;
409 
410  in_geom = PG_GETARG_GSERIALIZED_P(0);
411 
412  /* Return input geometry if input geometry is empty */
413  if ( gserialized_is_empty(in_geom) )
414  {
415  PG_RETURN_POINTER(in_geom);
416  }
417 
418  in_point = PG_GETARG_GSERIALIZED_P(1);
419  in_lwpoint = lwgeom_as_lwpoint(lwgeom_from_gserialized(in_point));
420  if ( in_lwpoint == NULL )
421  {
422  lwpgerror("Offset geometry must be a point");
423  }
424 
425  grid.xsize = PG_GETARG_FLOAT8(2);
426  grid.ysize = PG_GETARG_FLOAT8(3);
427  grid.zsize = PG_GETARG_FLOAT8(4);
428  grid.msize = PG_GETARG_FLOAT8(5);
429 
430  /* Take offsets from point geometry */
431  getPoint4d_p(in_lwpoint->point, 0, &offsetpoint);
432  grid.ipx = offsetpoint.x;
433  grid.ipy = offsetpoint.y;
434  grid.ipz = lwgeom_has_z((LWGEOM*)in_lwpoint) ? offsetpoint.z : 0;
435  grid.ipm = lwgeom_has_m((LWGEOM*)in_lwpoint) ? offsetpoint.m : 0;
436 
437 #if POSTGIS_DEBUG_LEVEL >= 4
438  grid_print(&grid);
439 #endif
440 
441  /* Return input geometry if input grid is meaningless */
442  if ( grid.xsize==0 && grid.ysize==0 && grid.zsize==0 && grid.msize==0 )
443  {
444  PG_RETURN_POINTER(in_geom);
445  }
446 
447  in_lwgeom = lwgeom_from_gserialized(in_geom);
448 
449  POSTGIS_DEBUGF(3, "SnapToGrid got a %s", lwtype_name(in_lwgeom->type));
450 
451  out_lwgeom = lwgeom_grid(in_lwgeom, &grid);
452  if ( out_lwgeom == NULL ) PG_RETURN_NULL();
453 
454  /* COMPUTE_BBOX TAINTING */
455  if (in_lwgeom->bbox)
456  {
457  lwgeom_refresh_bbox(out_lwgeom);
458  }
459 
460  POSTGIS_DEBUGF(3, "SnapToGrid made a %s", lwtype_name(out_lwgeom->type));
461 
462  out_geom = geometry_serialize(out_lwgeom);
463 
464  PG_RETURN_POINTER(out_geom);
465 }
466 
467 
468 /*
469 ** crossingDirection(line1, line2)
470 **
471 ** Determines crossing direction of line2 relative to line1.
472 ** Only accepts LINESTRING as parameters!
473 */
475 Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS)
476 {
477  int type1, type2, rv;
478  LWLINE *l1 = NULL;
479  LWLINE *l2 = NULL;
480  GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
481  GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
482 
483  gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
484 
485  type1 = gserialized_get_type(geom1);
486  type2 = gserialized_get_type(geom2);
487 
488  if ( type1 != LINETYPE || type2 != LINETYPE )
489  {
490  elog(ERROR,"This function only accepts LINESTRING as arguments.");
491  PG_RETURN_NULL();
492  }
493 
496 
497  rv = lwline_crossing_direction(l1, l2);
498 
499  PG_FREE_IF_COPY(geom1, 0);
500  PG_FREE_IF_COPY(geom2, 1);
501 
502  PG_RETURN_INT32(rv);
503 
504 }
505 
506 
507 
508 /***********************************************************************
509  * --strk@kbt.io
510  ***********************************************************************/
511 
512 Datum LWGEOM_line_substring(PG_FUNCTION_ARGS);
513 
515 Datum LWGEOM_line_substring(PG_FUNCTION_ARGS)
516 {
517  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
518  double from = PG_GETARG_FLOAT8(1);
519  double to = PG_GETARG_FLOAT8(2);
520  LWGEOM *olwgeom;
521  POINTARRAY *ipa, *opa;
522  GSERIALIZED *ret;
523  int type = gserialized_get_type(geom);
524 
525  if ( from < 0 || from > 1 )
526  {
527  elog(ERROR,"line_interpolate_point: 2nd arg isn't within [0,1]");
528  PG_RETURN_NULL();
529  }
530 
531  if ( to < 0 || to > 1 )
532  {
533  elog(ERROR,"line_interpolate_point: 3rd arg isn't within [0,1]");
534  PG_RETURN_NULL();
535  }
536 
537  if ( from > to )
538  {
539  elog(ERROR, "2nd arg must be smaller then 3rd arg");
540  PG_RETURN_NULL();
541  }
542 
543  if ( type == LINETYPE )
544  {
546 
547  if ( lwgeom_is_empty((LWGEOM*)iline) )
548  {
549  /* TODO return empty line */
550  lwline_release(iline);
551  PG_FREE_IF_COPY(geom, 0);
552  PG_RETURN_NULL();
553  }
554 
555  ipa = iline->points;
556 
557  opa = ptarray_substring(ipa, from, to, 0);
558 
559  if ( opa->npoints == 1 ) /* Point returned */
560  olwgeom = (LWGEOM *)lwpoint_construct(iline->srid, NULL, opa);
561  else
562  olwgeom = (LWGEOM *)lwline_construct(iline->srid, NULL, opa);
563 
564  }
565  else if ( type == MULTILINETYPE )
566  {
567  LWMLINE *iline;
568  uint32_t i = 0, g = 0;
569  int homogeneous = LW_TRUE;
570  LWGEOM **geoms = NULL;
571  double length = 0.0, sublength = 0.0, minprop = 0.0, maxprop = 0.0;
572 
574 
575  if ( lwgeom_is_empty((LWGEOM*)iline) )
576  {
577  /* TODO return empty collection */
578  lwmline_release(iline);
579  PG_FREE_IF_COPY(geom, 0);
580  PG_RETURN_NULL();
581  }
582 
583  /* Calculate the total length of the mline */
584  for ( i = 0; i < iline->ngeoms; i++ )
585  {
586  LWLINE *subline = (LWLINE*)iline->geoms[i];
587  if ( subline->points && subline->points->npoints > 1 )
588  length += ptarray_length_2d(subline->points);
589  }
590 
591  geoms = lwalloc(sizeof(LWGEOM*) * iline->ngeoms);
592 
593  /* Slice each sub-geometry of the multiline */
594  for ( i = 0; i < iline->ngeoms; i++ )
595  {
596  LWLINE *subline = (LWLINE*)iline->geoms[i];
597  double subfrom = 0.0, subto = 0.0;
598 
599  if ( subline->points && subline->points->npoints > 1 )
600  sublength += ptarray_length_2d(subline->points);
601 
602  /* Calculate proportions for this subline */
603  minprop = maxprop;
604  maxprop = sublength / length;
605 
606  /* This subline doesn't reach the lowest proportion requested
607  or is beyond the highest proporton */
608  if ( from > maxprop || to < minprop )
609  continue;
610 
611  if ( from <= minprop )
612  subfrom = 0.0;
613  if ( to >= maxprop )
614  subto = 1.0;
615 
616  if ( from > minprop && from <= maxprop )
617  subfrom = (from - minprop) / (maxprop - minprop);
618 
619  if ( to < maxprop && to >= minprop )
620  subto = (to - minprop) / (maxprop - minprop);
621 
622 
623  opa = ptarray_substring(subline->points, subfrom, subto, 0);
624  if ( opa && opa->npoints > 0 )
625  {
626  if ( opa->npoints == 1 ) /* Point returned */
627  {
628  geoms[g] = (LWGEOM *)lwpoint_construct(SRID_UNKNOWN, NULL, opa);
629  homogeneous = LW_FALSE;
630  }
631  else
632  {
633  geoms[g] = (LWGEOM *)lwline_construct(SRID_UNKNOWN, NULL, opa);
634  }
635  g++;
636  }
637 
638 
639 
640  }
641  /* If we got any points, we need to return a GEOMETRYCOLLECTION */
642  if ( ! homogeneous )
644 
645  olwgeom = (LWGEOM*)lwcollection_construct(type, iline->srid, NULL, g, geoms);
646  }
647  else
648  {
649  elog(ERROR,"line_substring: 1st arg isn't a line");
650  PG_RETURN_NULL();
651  }
652 
653  ret = geometry_serialize(olwgeom);
654  lwgeom_free(olwgeom);
655  PG_FREE_IF_COPY(geom, 0);
656  PG_RETURN_POINTER(ret);
657 
658 }
659 
660 /*******************************************************************************
661  * The following is based on the "Fast Winding Number Inclusion of a Point
662  * in a Polygon" algorithm by Dan Sunday.
663  * http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm#Winding%20Number
664  ******************************************************************************/
665 
666 /*
667  * returns: >0 for a point to the left of the segment,
668  * <0 for a point to the right of the segment,
669  * 0 for a point on the segment
670  */
671 static double determineSide(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
672 {
673  return ((seg2->x-seg1->x)*(point->y-seg1->y)-(point->x-seg1->x)*(seg2->y-seg1->y));
674 }
675 
676 /*
677  * This function doesn't test that the point falls on the line defined by
678  * the two points. It assumes that that has already been determined
679  * by having determineSide return within the tolerance. It simply checks
680  * that if the point is on the line, it is within the endpoints.
681  *
682  * returns: 1 if the point is not outside the bounds of the segment
683  * 0 if it is
684  */
685 static int isOnSegment(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
686 {
687  double maxX;
688  double maxY;
689  double minX;
690  double minY;
691 
692  if (seg1->x > seg2->x)
693  {
694  maxX = seg1->x;
695  minX = seg2->x;
696  }
697  else
698  {
699  maxX = seg2->x;
700  minX = seg1->x;
701  }
702  if (seg1->y > seg2->y)
703  {
704  maxY = seg1->y;
705  minY = seg2->y;
706  }
707  else
708  {
709  maxY = seg2->y;
710  minY = seg1->y;
711  }
712 
713  POSTGIS_DEBUGF(3, "maxX minX/maxY minY: %.8f %.8f/%.8f %.8f", maxX, minX, maxY, minY);
714 
715  if (maxX < point->x || minX > point->x)
716  {
717  POSTGIS_DEBUGF(3, "X value %.8f falls outside the range %.8f-%.8f", point->x, minX, maxX);
718 
719  return 0;
720  }
721  else if (maxY < point->y || minY > point->y)
722  {
723  POSTGIS_DEBUGF(3, "Y value %.8f falls outside the range %.8f-%.8f", point->y, minY, maxY);
724 
725  return 0;
726  }
727  return 1;
728 }
729 
730 /*
731  * return -1 iff point is outside ring pts
732  * return 1 iff point is inside ring pts
733  * return 0 iff point is on ring pts
734  */
735 static int point_in_ring_rtree(RTREE_NODE *root, const POINT2D *point)
736 {
737  int wn = 0;
738  uint32_t i;
739  double side;
740  const POINT2D *seg1;
741  const POINT2D *seg2;
742  LWMLINE *lines;
743 
744  POSTGIS_DEBUG(2, "point_in_ring called.");
745 
746  lines = RTreeFindLineSegments(root, point->y);
747  if (!lines)
748  return -1;
749 
750  for (i=0; i<lines->ngeoms; i++)
751  {
752  seg1 = getPoint2d_cp(lines->geoms[i]->points, 0);
753  seg2 = getPoint2d_cp(lines->geoms[i]->points, 1);
754 
755  side = determineSide(seg1, seg2, point);
756 
757  POSTGIS_DEBUGF(3, "segment: (%.8f, %.8f),(%.8f, %.8f)", seg1->x, seg1->y, seg2->x, seg2->y);
758  POSTGIS_DEBUGF(3, "side result: %.8f", side);
759  POSTGIS_DEBUGF(3, "counterclockwise wrap %d, clockwise wrap %d", FP_CONTAINS_BOTTOM(seg1->y, point->y, seg2->y), FP_CONTAINS_BOTTOM(seg2->y, point->y, seg1->y));
760 
761  /* zero length segments are ignored. */
762  if (((seg2->x - seg1->x)*(seg2->x - seg1->x) + (seg2->y - seg1->y)*(seg2->y - seg1->y)) < 1e-12*1e-12)
763  {
764  POSTGIS_DEBUG(3, "segment is zero length... ignoring.");
765 
766  continue;
767  }
768 
769  /* a point on the boundary of a ring is not contained. */
770  /* WAS: if (fabs(side) < 1e-12), see #852 */
771  if (side == 0.0)
772  {
773  if (isOnSegment(seg1, seg2, point) == 1)
774  {
775  POSTGIS_DEBUGF(3, "point on ring boundary between points %d, %d", i, i+1);
776 
777  return 0;
778  }
779  }
780 
781  /*
782  * If the point is to the left of the line, and it's rising,
783  * then the line is to the right of the point and
784  * circling counter-clockwise, so increment.
785  */
786  if ((seg1->y <= point->y) && (point->y < seg2->y) && (side > 0))
787  {
788  POSTGIS_DEBUG(3, "incrementing winding number.");
789 
790  ++wn;
791  }
792  /*
793  * If the point is to the right of the line, and it's falling,
794  * then the line is to the right of the point and circling
795  * clockwise, so decrement.
796  */
797  else if ((seg2->y <= point->y) && (point->y < seg1->y) && (side < 0))
798  {
799  POSTGIS_DEBUG(3, "decrementing winding number.");
800 
801  --wn;
802  }
803  }
804 
805  POSTGIS_DEBUGF(3, "winding number %d", wn);
806 
807  if (wn == 0)
808  return -1;
809  return 1;
810 }
811 
812 
813 /*
814  * return -1 iff point is outside ring pts
815  * return 1 iff point is inside ring pts
816  * return 0 iff point is on ring pts
817  */
818 static int point_in_ring(POINTARRAY *pts, const POINT2D *point)
819 {
820  int wn = 0;
821  uint32_t i;
822  double side;
823  const POINT2D* seg1;
824  const POINT2D* seg2;
825 
826  POSTGIS_DEBUG(2, "point_in_ring called.");
827 
828  seg2 = getPoint2d_cp(pts, 0);
829  for (i=0; i<pts->npoints-1; i++)
830  {
831  seg1 = seg2;
832  seg2 = getPoint2d_cp(pts, i+1);
833 
834  side = determineSide(seg1, seg2, point);
835 
836  POSTGIS_DEBUGF(3, "segment: (%.8f, %.8f),(%.8f, %.8f)", seg1->x, seg1->y, seg2->x, seg2->y);
837  POSTGIS_DEBUGF(3, "side result: %.8f", side);
838  POSTGIS_DEBUGF(3, "counterclockwise wrap %d, clockwise wrap %d", FP_CONTAINS_BOTTOM(seg1->y, point->y, seg2->y), FP_CONTAINS_BOTTOM(seg2->y, point->y, seg1->y));
839 
840  /* zero length segments are ignored. */
841  if ((seg2->x == seg1->x) && (seg2->y == seg1->y))
842  {
843  POSTGIS_DEBUG(3, "segment is zero length... ignoring.");
844 
845  continue;
846  }
847 
848  /* a point on the boundary of a ring is not contained. */
849  /* WAS: if (fabs(side) < 1e-12), see #852 */
850  if (side == 0.0)
851  {
852  if (isOnSegment(seg1, seg2, point) == 1)
853  {
854  POSTGIS_DEBUGF(3, "point on ring boundary between points %d, %d", i, i+1);
855 
856  return 0;
857  }
858  }
859 
860  /*
861  * If the point is to the left of the line, and it's rising,
862  * then the line is to the right of the point and
863  * circling counter-clockwise, so increment.
864  */
865  if ((seg1->y <= point->y) && (point->y < seg2->y) && (side > 0))
866  {
867  POSTGIS_DEBUG(3, "incrementing winding number.");
868 
869  ++wn;
870  }
871  /*
872  * If the point is to the right of the line, and it's falling,
873  * then the line is to the right of the point and circling
874  * clockwise, so decrement.
875  */
876  else if ((seg2->y <= point->y) && (point->y < seg1->y) && (side < 0))
877  {
878  POSTGIS_DEBUG(3, "decrementing winding number.");
879 
880  --wn;
881  }
882  }
883 
884  POSTGIS_DEBUGF(3, "winding number %d", wn);
885 
886  if (wn == 0)
887  return -1;
888  return 1;
889 }
890 
891 /*
892  * return 0 iff point outside polygon or on boundary
893  * return 1 iff point inside polygon
894  */
895 int point_in_polygon_rtree(RTREE_NODE **root, int ringCount, LWPOINT *point)
896 {
897  int i;
898  POINT2D pt;
899 
900  POSTGIS_DEBUGF(2, "point_in_polygon called for %p %d %p.", root, ringCount, point);
901 
902  getPoint2d_p(point->point, 0, &pt);
903  /* assume bbox short-circuit has already been attempted */
904 
905  if (point_in_ring_rtree(root[0], &pt) != 1)
906  {
907  POSTGIS_DEBUG(3, "point_in_polygon_rtree: outside exterior ring.");
908 
909  return 0;
910  }
911 
912  for (i=1; i<ringCount; i++)
913  {
914  if (point_in_ring_rtree(root[i], &pt) != -1)
915  {
916  POSTGIS_DEBUGF(3, "point_in_polygon_rtree: within hole %d.", i);
917 
918  return 0;
919  }
920  }
921  return 1;
922 }
923 
924 /*
925  * return -1 if point outside polygon
926  * return 0 if point on boundary
927  * return 1 if point inside polygon
928  *
929  * Expected **root order is each exterior ring followed by its holes, eg. EIIEIIEI
930  */
931 int point_in_multipolygon_rtree(RTREE_NODE **root, int polyCount, int *ringCounts, LWPOINT *point)
932 {
933  int i, p, r, in_ring;
934  POINT2D pt;
935  int result = -1;
936 
937  POSTGIS_DEBUGF(2, "point_in_multipolygon_rtree called for %p %d %p.", root, polyCount, point);
938 
939  getPoint2d_p(point->point, 0, &pt);
940  /* assume bbox short-circuit has already been attempted */
941 
942  i = 0; /* the current index into the root array */
943 
944  /* is the point inside any of the sub-polygons? */
945  for ( p = 0; p < polyCount; p++ )
946  {
947  in_ring = point_in_ring_rtree(root[i], &pt);
948  POSTGIS_DEBUGF(4, "point_in_multipolygon_rtree: exterior ring (%d), point_in_ring returned %d", p, in_ring);
949  if ( in_ring == -1 ) /* outside the exterior ring */
950  {
951  POSTGIS_DEBUG(3, "point_in_multipolygon_rtree: outside exterior ring.");
952  }
953  else if ( in_ring == 0 ) /* on the boundary */
954  {
955  POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: on edge of exterior ring %d", p);
956  return 0;
957  } else {
958  result = in_ring;
959 
960  for(r=1; r<ringCounts[p]; r++)
961  {
962  in_ring = point_in_ring_rtree(root[i+r], &pt);
963  POSTGIS_DEBUGF(4, "point_in_multipolygon_rtree: interior ring (%d), point_in_ring returned %d", r, in_ring);
964  if (in_ring == 1) /* inside a hole => outside the polygon */
965  {
966  POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: within hole %d of exterior ring %d", r, p);
967  result = -1;
968  break;
969  }
970  if (in_ring == 0) /* on the edge of a hole */
971  {
972  POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: on edge of hole %d of exterior ring %d", r, p);
973  return 0;
974  }
975  }
976  /* if we have a positive result, we can short-circuit and return it */
977  if ( result != -1)
978  {
979  return result;
980  }
981  }
982  /* increment the index by the total number of rings in the sub-poly */
983  /* we do this here in case we short-cutted out of the poly before looking at all the rings */
984  i += ringCounts[p];
985  }
986 
987  return result; /* -1 = outside, 0 = boundary, 1 = inside */
988 
989 }
990 
991 /*
992  * return -1 iff point outside polygon
993  * return 0 iff point on boundary
994  * return 1 iff point inside polygon
995  */
996 int point_in_polygon(LWPOLY *polygon, LWPOINT *point)
997 {
998  uint32_t i;
999  int result, in_ring;
1000  POINT2D pt;
1001 
1002  POSTGIS_DEBUG(2, "point_in_polygon called.");
1003 
1004  getPoint2d_p(point->point, 0, &pt);
1005  /* assume bbox short-circuit has already been attempted */
1006 
1007  /* everything is outside of an empty polygon */
1008  if ( polygon->nrings == 0 ) return -1;
1009 
1010  in_ring = point_in_ring(polygon->rings[0], &pt);
1011  if ( in_ring == -1) /* outside the exterior ring */
1012  {
1013  POSTGIS_DEBUG(3, "point_in_polygon: outside exterior ring.");
1014  return -1;
1015  }
1016  result = in_ring;
1017 
1018  for (i=1; i<polygon->nrings; i++)
1019  {
1020  in_ring = point_in_ring(polygon->rings[i], &pt);
1021  if (in_ring == 1) /* inside a hole => outside the polygon */
1022  {
1023  POSTGIS_DEBUGF(3, "point_in_polygon: within hole %d.", i);
1024  return -1;
1025  }
1026  if (in_ring == 0) /* on the edge of a hole */
1027  {
1028  POSTGIS_DEBUGF(3, "point_in_polygon: on edge of hole %d.", i);
1029  return 0;
1030  }
1031  }
1032  return result; /* -1 = outside, 0 = boundary, 1 = inside */
1033 }
1034 
1035 /*
1036  * return -1 iff point outside multipolygon
1037  * return 0 iff point on multipolygon boundary
1038  * return 1 iff point inside multipolygon
1039  */
1040 int point_in_multipolygon(LWMPOLY *mpolygon, LWPOINT *point)
1041 {
1042  uint32_t i, j;
1043  int result, in_ring;
1044  POINT2D pt;
1045 
1046  POSTGIS_DEBUG(2, "point_in_polygon called.");
1047 
1048  getPoint2d_p(point->point, 0, &pt);
1049  /* assume bbox short-circuit has already been attempted */
1050 
1051  result = -1;
1052 
1053  for (j = 0; j < mpolygon->ngeoms; j++ )
1054  {
1055 
1056  LWPOLY *polygon = mpolygon->geoms[j];
1057 
1058  /* everything is outside of an empty polygon */
1059  if ( polygon->nrings == 0 ) continue;
1060 
1061  in_ring = point_in_ring(polygon->rings[0], &pt);
1062  if ( in_ring == -1) /* outside the exterior ring */
1063  {
1064  POSTGIS_DEBUG(3, "point_in_polygon: outside exterior ring.");
1065  continue;
1066  }
1067  if ( in_ring == 0 )
1068  {
1069  return 0;
1070  }
1071 
1072  result = in_ring;
1073 
1074  for (i=1; i<polygon->nrings; i++)
1075  {
1076  in_ring = point_in_ring(polygon->rings[i], &pt);
1077  if (in_ring == 1) /* inside a hole => outside the polygon */
1078  {
1079  POSTGIS_DEBUGF(3, "point_in_polygon: within hole %d.", i);
1080  result = -1;
1081  break;
1082  }
1083  if (in_ring == 0) /* on the edge of a hole */
1084  {
1085  POSTGIS_DEBUGF(3, "point_in_polygon: on edge of hole %d.", i);
1086  return 0;
1087  }
1088  }
1089  if ( result != -1)
1090  {
1091  return result;
1092  }
1093  }
1094  return result;
1095 }
1096 
1097 
1098 /*******************************************************************************
1099  * End of "Fast Winding Number Inclusion of a Point in a Polygon" derivative.
1100  ******************************************************************************/
1101 
1102 /**********************************************************************
1103  *
1104  * ST_MinimumBoundingRadius
1105  *
1106  **********************************************************************/
1107 
1109 Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS)
1110 {
1111  GSERIALIZED* geom;
1112  LWGEOM* input;
1113  LWBOUNDINGCIRCLE* mbc = NULL;
1114  LWGEOM* lwcenter;
1115  GSERIALIZED* center;
1116  TupleDesc resultTupleDesc;
1117  HeapTuple resultTuple;
1118  Datum result;
1119  Datum result_values[2];
1120  bool result_is_null[2];
1121  double radius = 0;
1122 
1123  if (PG_ARGISNULL(0))
1124  PG_RETURN_NULL();
1125 
1126  geom = PG_GETARG_GSERIALIZED_P(0);
1127 
1128  /* Empty geometry? Return POINT EMPTY with zero radius */
1129  if (gserialized_is_empty(geom))
1130  {
1132  }
1133  else
1134  {
1135  input = lwgeom_from_gserialized(geom);
1136  mbc = lwgeom_calculate_mbc(input);
1137 
1138  if (!(mbc && mbc->center))
1139  {
1140  lwpgerror("Error calculating minimum bounding circle.");
1141  lwgeom_free(input);
1142  PG_RETURN_NULL();
1143  }
1144 
1145  lwcenter = (LWGEOM*) lwpoint_make2d(input->srid, mbc->center->x, mbc->center->y);
1146  radius = mbc->radius;
1147 
1149  lwgeom_free(input);
1150  }
1151 
1152  center = geometry_serialize(lwcenter);
1153  lwgeom_free(lwcenter);
1154 
1155  get_call_result_type(fcinfo, NULL, &resultTupleDesc);
1156  BlessTupleDesc(resultTupleDesc);
1157 
1158  result_values[0] = PointerGetDatum(center);
1159  result_is_null[0] = false;
1160  result_values[1] = Float8GetDatum(radius);
1161  result_is_null[1] = false;
1162 
1163  resultTuple = heap_form_tuple(resultTupleDesc, result_values, result_is_null);
1164 
1165  result = HeapTupleGetDatum(resultTuple);
1166 
1167  PG_RETURN_DATUM(result);
1168 }
1169 
1170 /**********************************************************************
1171  *
1172  * ST_MinimumBoundingCircle
1173  *
1174  **********************************************************************/
1175 
1177 Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS)
1178 {
1179  GSERIALIZED* geom;
1180  LWGEOM* input;
1181  LWBOUNDINGCIRCLE* mbc = NULL;
1182  LWGEOM* lwcircle;
1183  GSERIALIZED* center;
1184  int segs_per_quarter;
1185 
1186  if (PG_ARGISNULL(0))
1187  PG_RETURN_NULL();
1188 
1189  geom = PG_GETARG_GSERIALIZED_P(0);
1190  segs_per_quarter = PG_GETARG_INT32(1);
1191 
1192  /* Empty geometry? Return POINT EMPTY */
1193  if (gserialized_is_empty(geom))
1194  {
1196  }
1197  else
1198  {
1199  input = lwgeom_from_gserialized(geom);
1200  mbc = lwgeom_calculate_mbc(input);
1201 
1202  if (!(mbc && mbc->center))
1203  {
1204  lwpgerror("Error calculating minimum bounding circle.");
1205  lwgeom_free(input);
1206  PG_RETURN_NULL();
1207  }
1208 
1209  /* Zero radius? Return a point. */
1210  if (mbc->radius == 0)
1211  lwcircle = lwpoint_as_lwgeom(lwpoint_make2d(input->srid, mbc->center->x, mbc->center->y));
1212  else
1213  lwcircle = lwpoly_as_lwgeom(lwpoly_construct_circle(input->srid, mbc->center->x, mbc->center->y, mbc->radius, segs_per_quarter, LW_TRUE));
1214 
1216  lwgeom_free(input);
1217  }
1218 
1219  center = geometry_serialize(lwcircle);
1220  lwgeom_free(lwcircle);
1221 
1222  PG_RETURN_POINTER(center);
1223 }
1224 
1225 /**********************************************************************
1226  *
1227  * ST_GeometricMedian
1228  *
1229  **********************************************************************/
1230 
1232 Datum ST_GeometricMedian(PG_FUNCTION_ARGS)
1233 {
1234  GSERIALIZED* geom;
1236  LWGEOM* input;
1237  LWPOINT* lwresult;
1238  static const double min_default_tolerance = 1e-8;
1239  double tolerance = min_default_tolerance;
1240  bool compute_tolerance_from_box;
1241  bool fail_if_not_converged;
1242  int max_iter;
1243 
1244  /* Read and validate our input arguments */
1245  if (PG_ARGISNULL(0))
1246  PG_RETURN_NULL();
1247 
1248  compute_tolerance_from_box = PG_ARGISNULL(1);
1249 
1250  if (!compute_tolerance_from_box)
1251  {
1252  tolerance = PG_GETARG_FLOAT8(1);
1253  if (tolerance < 0)
1254  {
1255  lwpgerror("Tolerance must be positive.");
1256  PG_RETURN_NULL();
1257  }
1258  }
1259 
1260  max_iter = PG_ARGISNULL(2) ? -1 : PG_GETARG_INT32(2);
1261  fail_if_not_converged = PG_ARGISNULL(3) ? LW_FALSE : PG_GETARG_BOOL(3);
1262 
1263  if (max_iter < 0)
1264  {
1265  lwpgerror("Maximum iterations must be positive.");
1266  PG_RETURN_NULL();
1267  }
1268 
1269  /* OK, inputs are valid. */
1270  geom = PG_GETARG_GSERIALIZED_P(0);
1271  input = lwgeom_from_gserialized(geom);
1272 
1273  if (compute_tolerance_from_box)
1274  {
1275  /* Compute a default tolerance based on the smallest dimension
1276  * of the geometry's bounding box.
1277  */
1278  static const double tolerance_coefficient = 1e-6;
1279  const GBOX* box = lwgeom_get_bbox(input);
1280 
1281  if (box)
1282  {
1283  double min_dim = FP_MIN(box->xmax - box->xmin, box->ymax - box->ymin);
1284  if (lwgeom_has_z(input))
1285  min_dim = FP_MIN(min_dim, box->zmax - box->zmin);
1286 
1287  /* Apply a lower bound to the computed default tolerance to
1288  * avoid a tolerance of zero in the case of collinear
1289  * points.
1290  */
1291  tolerance = FP_MAX(min_default_tolerance, tolerance_coefficient * min_dim);
1292  }
1293  }
1294 
1295  lwresult = lwgeom_median(input, tolerance, max_iter, fail_if_not_converged);
1296  lwgeom_free(input);
1297 
1298  if(!lwresult)
1299  {
1300  lwpgerror("Error computing geometric median.");
1301  PG_RETURN_NULL();
1302  }
1303 
1304  result = geometry_serialize(lwpoint_as_lwgeom(lwresult));
1305 
1306  PG_RETURN_POINTER(result);
1307 }
1308 
1309 /**********************************************************************
1310  *
1311  * ST_IsPolygonCW
1312  *
1313  **********************************************************************/
1314 
1316 Datum ST_IsPolygonCW(PG_FUNCTION_ARGS)
1317 {
1318  GSERIALIZED* geom;
1319  LWGEOM* input;
1320  bool is_clockwise;
1321 
1322  if (PG_ARGISNULL(0))
1323  PG_RETURN_NULL();
1324 
1325  geom = PG_GETARG_GSERIALIZED_P(0);
1326  input = lwgeom_from_gserialized(geom);
1327 
1329 
1330  lwgeom_free(input);
1331  PG_FREE_IF_COPY(geom, 0);
1332 
1333  PG_RETURN_BOOL(is_clockwise);
1334 }
1335 
1336 /**********************************************************************
1337  *
1338  * ST_IsPolygonCCW
1339  *
1340  **********************************************************************/
1341 
1343 Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS)
1344 {
1345  GSERIALIZED* geom;
1346  LWGEOM* input;
1347  bool is_ccw;
1348 
1349  if (PG_ARGISNULL(0))
1350  PG_RETURN_NULL();
1351 
1352  geom = PG_GETARG_GSERIALIZED_P_COPY(0);
1353  input = lwgeom_from_gserialized(geom);
1354  lwgeom_reverse_in_place(input);
1355  is_ccw = lwgeom_is_clockwise(input);
1356  lwgeom_free(input);
1357  PG_FREE_IF_COPY(geom, 0);
1358 
1359  PG_RETURN_BOOL(is_ccw);
1360 }
1361 
char * r
Definition: cu_in_wkt.c:24
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
LWGEOM * lwgeom_set_effective_area(const LWGEOM *igeom, int set_area, double trshld)
void gserialized_error_if_srid_mismatch(const GSERIALIZED *g1, const GSERIALIZED *g2, const char *funcname)
Definition: gserialized.c:403
int32_t gserialized_get_srid(const GSERIALIZED *g)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: gserialized.c:126
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:239
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:152
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:89
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoint.c:151
LWPOLY * lwpoly_construct_circle(int32_t srid, double x, double y, double radius, uint32_t segments_per_quarter, char exterior)
Definition: lwpoly.c:120
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition: lwgeom.c:707
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
LWPOINT * lwline_interpolate_point_3d(const LWLINE *line, double distance)
Interpolate one point along a line in 3D.
Definition: lwline.c:605
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
#define LW_FALSE
Definition: liblwgeom.h:94
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition: lwgeom.c:1737
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition: lwpoint.c:163
void lwpoint_free(LWPOINT *pt)
Definition: lwpoint.c:213
POINTARRAY * ptarray_substring(POINTARRAY *pa, double d1, double d2, double tolerance)
@d1 start location (distance from start / total distance) @d2 end location (distance from start / tot...
Definition: ptarray.c:1079
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition: lwgeom.c:304
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:329
void lwline_release(LWLINE *lwline)
Definition: lwline.c:125
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
Given two lines, characterize how (and if) they cross each other.
Definition: lwalgorithm.c:459
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition: lwgeom_api.c:342
double ptarray_length_2d(const POINTARRAY *pts)
Find the 2d length of the given POINTARRAY (even if it's 3d)
Definition: ptarray.c:1832
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
void lwmline_release(LWMLINE *lwline)
Definition: lwmline.c:32
LWBOUNDINGCIRCLE * lwgeom_calculate_mbc(const LWGEOM *g)
LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom)
Definition: lwgeom.c:251
POINTARRAY * lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat)
Interpolate one or more points along a line.
Definition: lwline.c:528
LWGEOM * lwgeom_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint)
Definition: lwchaikins.c:182
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:129
void lwboundingcircle_destroy(LWBOUNDINGCIRCLE *c)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:125
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:743
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
LWPOINT * lwgeom_median(const LWGEOM *g, double tol, uint32_t maxiter, char fail_if_not_converged)
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:2267
int lwgeom_is_clockwise(LWGEOM *lwgeom)
Ensure the outer ring is clockwise oriented and all inner rings are counter-clockwise.
Definition: lwgeom.c:66
LWMPOINT * lwmpoint_construct(int32_t srid, const POINTARRAY *pa)
Definition: lwmpoint.c:52
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:695
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:103
This library is the generic geometry handling section of PostGIS.
#define FP_CONTAINS_BOTTOM(A, X, B)
#define FP_MAX(A, B)
#define FP_MIN(A, B)
int point_in_multipolygon(LWMPOLY *mpolygon, LWPOINT *point)
int point_in_multipolygon_rtree(RTREE_NODE **root, int polyCount, int *ringCounts, LWPOINT *point)
Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS)
static int isOnSegment(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
static int point_in_ring_rtree(RTREE_NODE *root, const POINT2D *point)
Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS)
static int point_in_ring(POINTARRAY *pts, const POINT2D *point)
Datum LWGEOM_ChaikinSmoothing(PG_FUNCTION_ARGS)
Datum ST_GeometricMedian(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(LWGEOM_simplify2d)
Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS)
int point_in_polygon_rtree(RTREE_NODE **root, int ringCount, LWPOINT *point)
Datum ST_IsPolygonCW(PG_FUNCTION_ARGS)
Datum LWGEOM_snaptogrid_pointoff(PG_FUNCTION_ARGS)
Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS)
Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS)
int point_in_polygon(LWPOLY *polygon, LWPOINT *point)
Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS)
Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS)
Datum ST_3DLineInterpolatePoint(PG_FUNCTION_ARGS)
Datum LWGEOM_line_substring(PG_FUNCTION_ARGS)
Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS)
static double determineSide(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
LWMLINE * RTreeFindLineSegments(RTREE_NODE *root, double value)
Retrieves a collection of line segments given the root and crossing value.
Definition: lwgeom_rtree.c:450
if(!(yy_init))
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:101
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:203
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:131
static double distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
type
Definition: ovdump.py:42
static int is_clockwise(int num_points, double *x, double *y, double *z)
double ymax
Definition: liblwgeom.h:357
double zmax
Definition: liblwgeom.h:359
double xmax
Definition: liblwgeom.h:355
double zmin
Definition: liblwgeom.h:358
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
POINT2D * center
Definition: liblwgeom.h:1816
uint8_t type
Definition: liblwgeom.h:462
GBOX * bbox
Definition: liblwgeom.h:458
int32_t srid
Definition: liblwgeom.h:460
POINTARRAY * points
Definition: liblwgeom.h:483
int32_t srid
Definition: liblwgeom.h:484
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
POINTARRAY * point
Definition: liblwgeom.h:471
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524
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
uint32_t npoints
Definition: liblwgeom.h:427
double ipm
Definition: liblwgeom.h:1379
double zsize
Definition: liblwgeom.h:1382
double ysize
Definition: liblwgeom.h:1381
double xsize
Definition: liblwgeom.h:1380
double ipx
Definition: liblwgeom.h:1376
double msize
Definition: liblwgeom.h:1383
double ipy
Definition: liblwgeom.h:1377
double ipz
Definition: liblwgeom.h:1378
Snap-to-grid.
Definition: liblwgeom.h:1375
The following struct and methods are used for a 1D RTree implementation, described at: http://lin-ear...
Definition: lwgeom_rtree.h:47