PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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 */
39Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS);
40Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS);
41Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS);
42Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS);
43Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS);
44Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS);
45Datum ST_GeometricMedian(PG_FUNCTION_ARGS);
46Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS);
47Datum ST_IsPolygonCW(PG_FUNCTION_ARGS);
48
49
50static double determineSide(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point);
51static int isOnSegment(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point);
52static int point_in_ring(POINTARRAY *pts, const POINT2D *point);
53static 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
64Datum 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
96Datum 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
130Datum 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 ***********************************************************************/
182Datum 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
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
231Datum ST_3DLineInterpolatePoint(PG_FUNCTION_ARGS);
232
234Datum 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
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 */
332Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS);
333Datum LWGEOM_snaptogrid_pointoff(PG_FUNCTION_ARGS);
334
335
336
338Datum 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 */
388static void
389grid_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
399Datum 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*/
475Datum 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
512Datum LWGEOM_line_substring(PG_FUNCTION_ARGS);
513
515Datum 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 )
643 type = COLLECTIONTYPE;
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 */
671static 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 */
685static 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 */
735static 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 */
818static 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 */
895int 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 */
931int 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 */
1002int 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 */
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
1118Datum 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
1186Datum 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
1241Datum 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
1325Datum 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
1352Datum 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);
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)
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)...
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.
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
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition lwgeom.c:286
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition lwgeom.c:689
LWPOINT * lwline_interpolate_point_3d(const LWLINE *line, double distance)
Interpolate one point along a line in 3D.
Definition lwline.c:602
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:326
#define LW_FALSE
Definition liblwgeom.h:108
#define COLLECTIONTYPE
Definition liblwgeom.h:122
POINTARRAY * lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat)
Interpolate one or more points along a line.
Definition lwline.c:525
int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition lwgeom.c:1715
LWMPOINT * lwmpoint_construct(int32_t srid, const POINTARRAY *pa)
Definition lwmpoint.c:52
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 lwpoint_free(LWPOINT *pt)
Definition lwpoint.c:213
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
#define MULTILINETYPE
Definition liblwgeom.h:120
#define LINETYPE
Definition liblwgeom.h:117
LWBOUNDINGCIRCLE * lwgeom_calculate_mbc(const LWGEOM *g)
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:1066
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition lwpoint.c:129
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.
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:1708
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
void lwmline_release(LWMLINE *lwline)
Definition lwmline.c:32
void * lwalloc(size_t size)
Definition lwutil.c:227
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition lwpoint.c:163
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:321
LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom)
Definition lwgeom.c:233
void lwboundingcircle_destroy(LWBOUNDINGCIRCLE *c)
LWPOINT * lwgeom_median(const LWGEOM *g, double tol, uint32_t maxiter, char fail_if_not_converged)
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
Definition lwgeom.c:2245
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwpoint.c:151
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:161
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:107
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
#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_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint)
Definition lwchaikins.c:182
int lwgeom_is_clockwise(LWGEOM *lwgeom)
Ensure the outer ring is clockwise oriented and all inner rings are counter-clockwise.
Definition lwgeom.c:65
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition lwgeom.c:311
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.
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition lwinline.h:121
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 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 double distance(double x1, double y1, double x2, double y2)
Definition lwtree.c:1032
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...