PostGIS  3.0.6dev-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 #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);
68  GSERIALIZED *result;
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);
99  GSERIALIZED *result;
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);
133  GSERIALIZED *result;
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);
185  GSERIALIZED *result;
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);
237  GSERIALIZED *result;
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  /* empty is not within anything */
940  if (lwpoint_is_empty(point)) return -1;
941 
942  getPoint2d_p(point->point, 0, &pt);
943  /* assume bbox short-circuit has already been attempted */
944 
945  i = 0; /* the current index into the root array */
946 
947  /* is the point inside any of the sub-polygons? */
948  for ( p = 0; p < polyCount; p++ )
949  {
950  /* Skip empty polygons */
951  if( ringCounts[p] == 0 ) continue;
952 
953  in_ring = point_in_ring_rtree(root[i], &pt);
954  POSTGIS_DEBUGF(4, "point_in_multipolygon_rtree: exterior ring (%d), point_in_ring returned %d", p, in_ring);
955  if ( in_ring == -1 ) /* outside the exterior ring */
956  {
957  POSTGIS_DEBUG(3, "point_in_multipolygon_rtree: outside exterior ring.");
958  }
959  else if ( in_ring == 0 ) /* on the boundary */
960  {
961  POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: on edge of exterior ring %d", p);
962  return 0;
963  } else {
964  result = in_ring;
965 
966  for(r=1; r<ringCounts[p]; r++)
967  {
968  in_ring = point_in_ring_rtree(root[i+r], &pt);
969  POSTGIS_DEBUGF(4, "point_in_multipolygon_rtree: interior ring (%d), point_in_ring returned %d", r, in_ring);
970  if (in_ring == 1) /* inside a hole => outside the polygon */
971  {
972  POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: within hole %d of exterior ring %d", r, p);
973  result = -1;
974  break;
975  }
976  if (in_ring == 0) /* on the edge of a hole */
977  {
978  POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: on edge of hole %d of exterior ring %d", r, p);
979  return 0;
980  }
981  }
982  /* if we have a positive result, we can short-circuit and return it */
983  if ( result != -1)
984  {
985  return result;
986  }
987  }
988  /* increment the index by the total number of rings in the sub-poly */
989  /* we do this here in case we short-cutted out of the poly before looking at all the rings */
990  i += ringCounts[p];
991  }
992 
993  return result; /* -1 = outside, 0 = boundary, 1 = inside */
994 
995 }
996 
997 /*
998  * return -1 iff point outside polygon
999  * return 0 iff point on boundary
1000  * return 1 iff point inside polygon
1001  */
1002 int point_in_polygon(LWPOLY *polygon, LWPOINT *point)
1003 {
1004  uint32_t i;
1005  int result, in_ring;
1006  POINT2D pt;
1007 
1008  POSTGIS_DEBUG(2, "point_in_polygon called.");
1009 
1010  getPoint2d_p(point->point, 0, &pt);
1011  /* assume bbox short-circuit has already been attempted */
1012 
1013  /* everything is outside of an empty polygon */
1014  if ( polygon->nrings == 0 ) return -1;
1015 
1016  in_ring = point_in_ring(polygon->rings[0], &pt);
1017  if ( in_ring == -1) /* outside the exterior ring */
1018  {
1019  POSTGIS_DEBUG(3, "point_in_polygon: outside exterior ring.");
1020  return -1;
1021  }
1022  result = in_ring;
1023 
1024  for (i=1; i<polygon->nrings; i++)
1025  {
1026  in_ring = point_in_ring(polygon->rings[i], &pt);
1027  if (in_ring == 1) /* inside a hole => outside the polygon */
1028  {
1029  POSTGIS_DEBUGF(3, "point_in_polygon: within hole %d.", i);
1030  return -1;
1031  }
1032  if (in_ring == 0) /* on the edge of a hole */
1033  {
1034  POSTGIS_DEBUGF(3, "point_in_polygon: on edge of hole %d.", i);
1035  return 0;
1036  }
1037  }
1038  return result; /* -1 = outside, 0 = boundary, 1 = inside */
1039 }
1040 
1041 /*
1042  * return -1 iff point outside multipolygon
1043  * return 0 iff point on multipolygon boundary
1044  * return 1 iff point inside multipolygon
1045  */
1046 int point_in_multipolygon(LWMPOLY *mpolygon, LWPOINT *point)
1047 {
1048  uint32_t i, j;
1049  int result, in_ring;
1050  POINT2D pt;
1051 
1052  POSTGIS_DEBUG(2, "point_in_polygon called.");
1053 
1054  /* empty is not within anything */
1055  if (lwpoint_is_empty(point)) return -1;
1056 
1057  getPoint2d_p(point->point, 0, &pt);
1058  /* assume bbox short-circuit has already been attempted */
1059 
1060  result = -1;
1061 
1062  for (j = 0; j < mpolygon->ngeoms; j++ )
1063  {
1064 
1065  LWPOLY *polygon = mpolygon->geoms[j];
1066 
1067  /* everything is outside of an empty polygon */
1068  if ( polygon->nrings == 0 ) continue;
1069 
1070  in_ring = point_in_ring(polygon->rings[0], &pt);
1071  if ( in_ring == -1) /* outside the exterior ring */
1072  {
1073  POSTGIS_DEBUG(3, "point_in_polygon: outside exterior ring.");
1074  continue;
1075  }
1076  if ( in_ring == 0 )
1077  {
1078  return 0;
1079  }
1080 
1081  result = in_ring;
1082 
1083  for (i=1; i<polygon->nrings; i++)
1084  {
1085  in_ring = point_in_ring(polygon->rings[i], &pt);
1086  if (in_ring == 1) /* inside a hole => outside the polygon */
1087  {
1088  POSTGIS_DEBUGF(3, "point_in_polygon: within hole %d.", i);
1089  result = -1;
1090  break;
1091  }
1092  if (in_ring == 0) /* on the edge of a hole */
1093  {
1094  POSTGIS_DEBUGF(3, "point_in_polygon: on edge of hole %d.", i);
1095  return 0;
1096  }
1097  }
1098  if ( result != -1)
1099  {
1100  return result;
1101  }
1102  }
1103  return result;
1104 }
1105 
1106 
1107 /*******************************************************************************
1108  * End of "Fast Winding Number Inclusion of a Point in a Polygon" derivative.
1109  ******************************************************************************/
1110 
1111 /**********************************************************************
1112  *
1113  * ST_MinimumBoundingRadius
1114  *
1115  **********************************************************************/
1116 
1118 Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS)
1119 {
1120  GSERIALIZED* geom;
1121  LWGEOM* input;
1122  LWBOUNDINGCIRCLE* mbc = NULL;
1123  LWGEOM* lwcenter;
1124  GSERIALIZED* center;
1125  TupleDesc resultTupleDesc;
1126  HeapTuple resultTuple;
1127  Datum result;
1128  Datum result_values[2];
1129  bool result_is_null[2];
1130  double radius = 0;
1131 
1132  if (PG_ARGISNULL(0))
1133  PG_RETURN_NULL();
1134 
1135  geom = PG_GETARG_GSERIALIZED_P(0);
1136 
1137  /* Empty geometry? Return POINT EMPTY with zero radius */
1138  if (gserialized_is_empty(geom))
1139  {
1141  }
1142  else
1143  {
1144  input = lwgeom_from_gserialized(geom);
1145  mbc = lwgeom_calculate_mbc(input);
1146 
1147  if (!(mbc && mbc->center))
1148  {
1149  lwpgerror("Error calculating minimum bounding circle.");
1150  lwgeom_free(input);
1151  PG_RETURN_NULL();
1152  }
1153 
1154  lwcenter = (LWGEOM*) lwpoint_make2d(input->srid, mbc->center->x, mbc->center->y);
1155  radius = mbc->radius;
1156 
1158  lwgeom_free(input);
1159  }
1160 
1161  center = geometry_serialize(lwcenter);
1162  lwgeom_free(lwcenter);
1163 
1164  get_call_result_type(fcinfo, NULL, &resultTupleDesc);
1165  BlessTupleDesc(resultTupleDesc);
1166 
1167  result_values[0] = PointerGetDatum(center);
1168  result_is_null[0] = false;
1169  result_values[1] = Float8GetDatum(radius);
1170  result_is_null[1] = false;
1171 
1172  resultTuple = heap_form_tuple(resultTupleDesc, result_values, result_is_null);
1173 
1174  result = HeapTupleGetDatum(resultTuple);
1175 
1176  PG_RETURN_DATUM(result);
1177 }
1178 
1179 /**********************************************************************
1180  *
1181  * ST_MinimumBoundingCircle
1182  *
1183  **********************************************************************/
1184 
1186 Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS)
1187 {
1188  GSERIALIZED* geom;
1189  LWGEOM* input;
1190  LWBOUNDINGCIRCLE* mbc = NULL;
1191  LWGEOM* lwcircle;
1192  GSERIALIZED* center;
1193  int segs_per_quarter;
1194 
1195  if (PG_ARGISNULL(0))
1196  PG_RETURN_NULL();
1197 
1198  geom = PG_GETARG_GSERIALIZED_P(0);
1199  segs_per_quarter = PG_GETARG_INT32(1);
1200 
1201  /* Empty geometry? Return POINT EMPTY */
1202  if (gserialized_is_empty(geom))
1203  {
1205  }
1206  else
1207  {
1208  input = lwgeom_from_gserialized(geom);
1209  mbc = lwgeom_calculate_mbc(input);
1210 
1211  if (!(mbc && mbc->center))
1212  {
1213  lwpgerror("Error calculating minimum bounding circle.");
1214  lwgeom_free(input);
1215  PG_RETURN_NULL();
1216  }
1217 
1218  /* Zero radius? Return a point. */
1219  if (mbc->radius == 0)
1220  lwcircle = lwpoint_as_lwgeom(lwpoint_make2d(input->srid, mbc->center->x, mbc->center->y));
1221  else
1222  lwcircle = lwpoly_as_lwgeom(lwpoly_construct_circle(input->srid, mbc->center->x, mbc->center->y, mbc->radius, segs_per_quarter, LW_TRUE));
1223 
1225  lwgeom_free(input);
1226  }
1227 
1228  center = geometry_serialize(lwcircle);
1229  lwgeom_free(lwcircle);
1230 
1231  PG_RETURN_POINTER(center);
1232 }
1233 
1234 /**********************************************************************
1235  *
1236  * ST_GeometricMedian
1237  *
1238  **********************************************************************/
1239 
1241 Datum ST_GeometricMedian(PG_FUNCTION_ARGS)
1242 {
1243  GSERIALIZED* geom;
1244  GSERIALIZED* result;
1245  LWGEOM* input;
1246  LWPOINT* lwresult;
1247  static const double min_default_tolerance = 1e-8;
1248  double tolerance = min_default_tolerance;
1249  bool compute_tolerance_from_box;
1250  bool fail_if_not_converged;
1251  int max_iter;
1252 
1253  /* Read and validate our input arguments */
1254  if (PG_ARGISNULL(0))
1255  PG_RETURN_NULL();
1256 
1257  compute_tolerance_from_box = PG_ARGISNULL(1);
1258 
1259  if (!compute_tolerance_from_box)
1260  {
1261  tolerance = PG_GETARG_FLOAT8(1);
1262  if (tolerance < 0)
1263  {
1264  lwpgerror("Tolerance must be positive.");
1265  PG_RETURN_NULL();
1266  }
1267  }
1268 
1269  max_iter = PG_ARGISNULL(2) ? -1 : PG_GETARG_INT32(2);
1270  fail_if_not_converged = PG_ARGISNULL(3) ? LW_FALSE : PG_GETARG_BOOL(3);
1271 
1272  if (max_iter < 0)
1273  {
1274  lwpgerror("Maximum iterations must be positive.");
1275  PG_RETURN_NULL();
1276  }
1277 
1278  /* OK, inputs are valid. */
1279  geom = PG_GETARG_GSERIALIZED_P(0);
1280  input = lwgeom_from_gserialized(geom);
1281 
1282  if (compute_tolerance_from_box)
1283  {
1284  /* Compute a default tolerance based on the smallest dimension
1285  * of the geometry's bounding box.
1286  */
1287  static const double tolerance_coefficient = 1e-6;
1288  const GBOX* box = lwgeom_get_bbox(input);
1289 
1290  if (box)
1291  {
1292  double min_dim = FP_MIN(box->xmax - box->xmin, box->ymax - box->ymin);
1293  if (lwgeom_has_z(input))
1294  min_dim = FP_MIN(min_dim, box->zmax - box->zmin);
1295 
1296  /* Apply a lower bound to the computed default tolerance to
1297  * avoid a tolerance of zero in the case of collinear
1298  * points.
1299  */
1300  tolerance = FP_MAX(min_default_tolerance, tolerance_coefficient * min_dim);
1301  }
1302  }
1303 
1304  lwresult = lwgeom_median(input, tolerance, max_iter, fail_if_not_converged);
1305  lwgeom_free(input);
1306 
1307  if(!lwresult)
1308  {
1309  lwpgerror("Error computing geometric median.");
1310  PG_RETURN_NULL();
1311  }
1312 
1313  result = geometry_serialize(lwpoint_as_lwgeom(lwresult));
1314 
1315  PG_RETURN_POINTER(result);
1316 }
1317 
1318 /**********************************************************************
1319  *
1320  * ST_IsPolygonCW
1321  *
1322  **********************************************************************/
1323 
1325 Datum ST_IsPolygonCW(PG_FUNCTION_ARGS)
1326 {
1327  GSERIALIZED* geom;
1328  LWGEOM* input;
1329  bool is_clockwise;
1330 
1331  if (PG_ARGISNULL(0))
1332  PG_RETURN_NULL();
1333 
1334  geom = PG_GETARG_GSERIALIZED_P(0);
1335  input = lwgeom_from_gserialized(geom);
1336 
1338 
1339  lwgeom_free(input);
1340  PG_FREE_IF_COPY(geom, 0);
1341 
1342  PG_RETURN_BOOL(is_clockwise);
1343 }
1344 
1345 /**********************************************************************
1346  *
1347  * ST_IsPolygonCCW
1348  *
1349  **********************************************************************/
1350 
1352 Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS)
1353 {
1354  GSERIALIZED* geom;
1355  LWGEOM* input;
1356  bool is_ccw;
1357 
1358  if (PG_ARGISNULL(0))
1359  PG_RETURN_NULL();
1360 
1361  geom = PG_GETARG_GSERIALIZED_P_COPY(0);
1362  input = lwgeom_from_gserialized(geom);
1363  lwgeom_reverse_in_place(input);
1364  is_ccw = lwgeom_is_clockwise(input);
1365  lwgeom_free(input);
1366  PG_FREE_IF_COPY(geom, 0);
1367 
1368  PG_RETURN_BOOL(is_ccw);
1369 }
1370 
char * r
Definition: cu_in_wkt.c:24
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:404
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:689
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:161
LWPOINT * lwline_interpolate_point_3d(const LWLINE *line, double distance)
Interpolate one point along a line in 3D.
Definition: lwline.c:602
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:321
#define LW_FALSE
Definition: liblwgeom.h:108
#define COLLECTIONTYPE
Definition: liblwgeom.h:122
int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition: lwgeom.c:1715
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:1058
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1138
#define MULTILINETYPE
Definition: liblwgeom.h:120
#define LINETYPE
Definition: liblwgeom.h:117
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition: lwgeom.c:286
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:311
void lwline_release(LWLINE *lwline)
Definition: lwline.c:125
#define MULTIPOINTTYPE
Definition: liblwgeom.h:119
int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
Given two lines, characterize how (and if) they cross each other.
Definition: lwalgorithm.c:462
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition: lwgeom_api.c:349
double ptarray_length_2d(const POINTARRAY *pts)
Find the 2d length of the given POINTARRAY (even if it's 3d)
Definition: ptarray.c:1700
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:916
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:116
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:233
POINTARRAY * lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat)
Interpolate one or more points along a line.
Definition: lwline.c:525
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:326
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:725
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:107
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:229
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:923
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:2245
int lwgeom_is_clockwise(LWGEOM *lwgeom)
Ensure the outer ring is clockwise oriented and all inner rings are counter-clockwise.
Definition: lwgeom.c:65
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:677
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)
int lwpoint_is_empty(const LWPOINT *point)
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:91
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:193
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:121
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)
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
double ymax
Definition: liblwgeom.h:343
double zmax
Definition: liblwgeom.h:345
double xmax
Definition: liblwgeom.h:341
double zmin
Definition: liblwgeom.h:344
double ymin
Definition: liblwgeom.h:342
double xmin
Definition: liblwgeom.h:340
POINT2D * center
Definition: liblwgeom.h:1756
uint8_t type
Definition: liblwgeom.h:448
GBOX * bbox
Definition: liblwgeom.h:444
int32_t srid
Definition: liblwgeom.h:446
POINTARRAY * points
Definition: liblwgeom.h:469
int32_t srid
Definition: liblwgeom.h:470
int32_t srid
Definition: liblwgeom.h:534
LWLINE ** geoms
Definition: liblwgeom.h:533
uint32_t ngeoms
Definition: liblwgeom.h:538
uint32_t ngeoms
Definition: liblwgeom.h:552
LWPOLY ** geoms
Definition: liblwgeom.h:547
POINTARRAY * point
Definition: liblwgeom.h:457
POINTARRAY ** rings
Definition: liblwgeom.h:505
uint32_t nrings
Definition: liblwgeom.h:510
double y
Definition: liblwgeom.h:376
double x
Definition: liblwgeom.h:376
double m
Definition: liblwgeom.h:400
double x
Definition: liblwgeom.h:400
double z
Definition: liblwgeom.h:400
double y
Definition: liblwgeom.h:400
uint32_t npoints
Definition: liblwgeom.h:413
double ipm
Definition: liblwgeom.h:1345
double zsize
Definition: liblwgeom.h:1348
double ysize
Definition: liblwgeom.h:1347
double xsize
Definition: liblwgeom.h:1346
double ipx
Definition: liblwgeom.h:1342
double msize
Definition: liblwgeom.h:1349
double ipy
Definition: liblwgeom.h:1343
double ipz
Definition: liblwgeom.h:1344
Snap-to-grid.
Definition: liblwgeom.h:1341
The following struct and methods are used for a 1D RTree implementation, described at: http://lin-ear...
Definition: lwgeom_rtree.h:47