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