PostGIS 3.7.0dev-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_itree.h"
34
35#include "access/htup_details.h"
36
37/* Prototypes */
38Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS);
39Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS);
40Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS);
41Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS);
42Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS);
43Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS);
44Datum ST_GeometricMedian(PG_FUNCTION_ARGS);
45Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS);
46Datum ST_IsPolygonCW(PG_FUNCTION_ARGS);
47
48
49/***********************************************************************
50 * Simple Douglas-Peucker line simplification.
51 * No checks are done to avoid introduction of self-intersections.
52 * No topology relations are considered.
53 *
54 * --strk@kbt.io;
55 ***********************************************************************/
56
58Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS)
59{
60 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P_COPY(0);
61 double dist = PG_GETARG_FLOAT8(1);
63 int type = gserialized_get_type(geom);
64 LWGEOM *in;
65 bool preserve_collapsed = false;
66 int modified = LW_FALSE;
67
68 /* Can't simplify points! */
69 if ( type == POINTTYPE || type == MULTIPOINTTYPE )
70 PG_RETURN_POINTER(geom);
71
72 /* Handle optional argument to preserve collapsed features */
73 if ((PG_NARGS() > 2) && (!PG_ARGISNULL(2)))
74 preserve_collapsed = PG_GETARG_BOOL(2);
75
76 in = lwgeom_from_gserialized(geom);
77
78 modified = lwgeom_simplify_in_place(in, dist, preserve_collapsed);
79 if (!modified)
80 PG_RETURN_POINTER(geom);
81
82 if (!in || lwgeom_is_empty(in))
83 PG_RETURN_NULL();
84
85 result = geometry_serialize(in);
86 PG_RETURN_POINTER(result);
87}
88
90Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS)
91{
92 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
94 int type = gserialized_get_type(geom);
95 LWGEOM *in;
96 LWGEOM *out;
97 double area=0;
98 int set_area=0;
99
100 if ( type == POINTTYPE || type == MULTIPOINTTYPE )
101 PG_RETURN_POINTER(geom);
102
103 if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
104 area = PG_GETARG_FLOAT8(1);
105
106 if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) )
107 set_area = PG_GETARG_INT32(2);
108
109 in = lwgeom_from_gserialized(geom);
110
111 out = lwgeom_set_effective_area(in,set_area, area);
112 if ( ! out ) PG_RETURN_NULL();
113
114 /* COMPUTE_BBOX TAINTING */
115 if ( in->bbox ) lwgeom_add_bbox(out);
116
117 result = geometry_serialize(out);
118 lwgeom_free(out);
119 PG_FREE_IF_COPY(geom, 0);
120 PG_RETURN_POINTER(result);
121}
122
124Datum LWGEOM_ChaikinSmoothing(PG_FUNCTION_ARGS)
125{
126 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
128 int type = gserialized_get_type(geom);
129 LWGEOM *in;
130 LWGEOM *out;
131 int preserve_endpoints=1;
132 int n_iterations=1;
133
134 if ( type == POINTTYPE || type == MULTIPOINTTYPE )
135 PG_RETURN_POINTER(geom);
136
137 if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
138 n_iterations = PG_GETARG_INT32(1);
139
140 if (n_iterations< 1 || n_iterations>5)
141 elog(ERROR,"Number of iterations must be between 1 and 5 : %s", __func__);
142
143 if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) )
144 {
145 if(PG_GETARG_BOOL(2))
146 preserve_endpoints = 1;
147 else
148 preserve_endpoints = 0;
149 }
150
151 in = lwgeom_from_gserialized(geom);
152
153 out = lwgeom_chaikin(in, n_iterations, preserve_endpoints);
154 if ( ! out ) PG_RETURN_NULL();
155
156 /* COMPUTE_BBOX TAINTING */
157 if ( in->bbox ) lwgeom_add_bbox(out);
158
159 result = geometry_serialize(out);
160 lwgeom_free(out);
161 PG_FREE_IF_COPY(geom, 0);
162 PG_RETURN_POINTER(result);
163}
164
165
166/***********************************************************************
167 * --strk@kbt.io;
168 ***********************************************************************/
169
170/***********************************************************************
171 * Interpolate a point along a line, useful for Geocoding applications
172 * SELECT line_interpolate_point( 'LINESTRING( 0 0, 2 2'::geometry, .5 )
173 * returns POINT( 1 1 ).
174 ***********************************************************************/
176Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS)
177{
178 GSERIALIZED *gser = PG_GETARG_GSERIALIZED_P(0);
180 double distance_fraction = PG_GETARG_FLOAT8(1);
181 int repeat = PG_NARGS() > 2 && PG_GETARG_BOOL(2);
182 int32_t srid = gserialized_get_srid(gser);
183 LWLINE* lwline;
184 LWGEOM* lwresult;
185 POINTARRAY* opa;
186
187 if ( distance_fraction < 0 || distance_fraction > 1 )
188 {
189 elog(ERROR,"line_interpolate_point: 2nd arg isn't within [0,1]");
190 PG_FREE_IF_COPY(gser, 0);
191 PG_RETURN_NULL();
192 }
193
194 if ( gserialized_get_type(gser) != LINETYPE )
195 {
196 elog(ERROR,"line_interpolate_point: 1st arg isn't a line");
197 PG_FREE_IF_COPY(gser, 0);
198 PG_RETURN_NULL();
199 }
200
202 opa = lwline_interpolate_points(lwline, distance_fraction, repeat);
203
205 PG_FREE_IF_COPY(gser, 0);
206
207 if (opa->npoints <= 1)
208 {
209 lwresult = lwpoint_as_lwgeom(lwpoint_construct(srid, NULL, opa));
210 } else {
211 lwresult = lwmpoint_as_lwgeom(lwmpoint_construct(srid, opa));
212 }
213
214 result = geometry_serialize(lwresult);
215 lwgeom_free(lwresult);
216
217 PG_RETURN_POINTER(result);
218}
219
220/***********************************************************************
221 * Interpolate a point along a line 3D version
222 * --vincent.mora@oslandia.com;
223 ***********************************************************************/
224
225Datum ST_3DLineInterpolatePoint(PG_FUNCTION_ARGS);
226
228Datum ST_3DLineInterpolatePoint(PG_FUNCTION_ARGS)
229{
230 GSERIALIZED *gser = PG_GETARG_GSERIALIZED_P(0);
232 double distance = PG_GETARG_FLOAT8(1);
233 LWLINE *line;
234 LWGEOM *geom;
235 LWPOINT *point;
236
237 if (distance < 0 || distance > 1)
238 {
239 elog(ERROR, "line_interpolate_point: 2nd arg isn't within [0,1]");
240 PG_RETURN_NULL();
241 }
242
243 if (gserialized_get_type(gser) != LINETYPE)
244 {
245 elog(ERROR, "line_interpolate_point: 1st arg isn't a line");
246 PG_RETURN_NULL();
247 }
248
249 geom = lwgeom_from_gserialized(gser);
250 line = lwgeom_as_lwline(geom);
251
253
254 lwgeom_free(geom);
255 PG_FREE_IF_COPY(gser, 0);
256
257 result = geometry_serialize(lwpoint_as_lwgeom(point));
258 lwpoint_free(point);
259
260 PG_RETURN_POINTER(result);
261}
262
263/***********************************************************************
264 * --jsunday@rochgrp.com;
265 ***********************************************************************/
266
267/***********************************************************************
268 *
269 * Grid application function for postgis.
270 *
271 * WHAT IS
272 * -------
273 *
274 * This is a grid application function for postgis.
275 * You use it to stick all of a geometry points to
276 * a custom grid defined by its origin and cell size
277 * in geometry units.
278 *
279 * Points reduction is obtained by collapsing all
280 * consecutive points falling on the same grid node and
281 * removing all consecutive segments S1,S2 having
282 * S2.startpoint = S1.endpoint and S2.endpoint = S1.startpoint.
283 *
284 * ISSUES
285 * ------
286 *
287 * Only 2D is contemplated in grid application.
288 *
289 * Consecutive coincident segments removal does not work
290 * on first/last segments (They are not considered consecutive).
291 *
292 * Grid application occurs on a geometry subobject basis, thus no
293 * points reduction occurs for multipoint geometries.
294 *
295 * USAGE TIPS
296 * ----------
297 *
298 * Run like this:
299 *
300 * SELECT SnapToGrid(<geometry>, <originX>, <originY>, <sizeX>, <sizeY>);
301 *
302 * Grid cells are not visible on a screen as long as the screen
303 * point size is equal or bigger then the grid cell size.
304 * This assumption may be used to reduce the number of points in
305 * a map for a given display scale.
306 *
307 * Keeping multiple resolution versions of the same data may be used
308 * in conjunction with MINSCALE/MAXSCALE keywords of mapserv to speed
309 * up rendering.
310 *
311 * Check also the use of DP simplification to reduce grid visibility.
312 * I'm still researching about the relationship between grid size and
313 * DP epsilon values - please tell me if you know more about this.
314 *
315 *
316 * --strk@kbt.io;
317 *
318 ***********************************************************************/
319
320#define CHECK_RING_IS_CLOSE
321#define SAMEPOINT(a,b) ((a)->x==(b)->x&&(a)->y==(b)->y)
322
323
324
325/* Forward declarations */
326Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS);
327Datum LWGEOM_snaptogrid_pointoff(PG_FUNCTION_ARGS);
328
329
330
332Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS)
333{
334 LWGEOM *in_lwgeom;
335 GSERIALIZED *out_geom = NULL;
336 LWGEOM *out_lwgeom;
337 gridspec grid;
338
339 GSERIALIZED *in_geom = PG_GETARG_GSERIALIZED_P(0);
340
341 /* Set grid values to zero to start */
342 memset(&grid, 0, sizeof(gridspec));
343
344 grid.ipx = PG_GETARG_FLOAT8(1);
345 grid.ipy = PG_GETARG_FLOAT8(2);
346 grid.xsize = PG_GETARG_FLOAT8(3);
347 grid.ysize = PG_GETARG_FLOAT8(4);
348
349 /* Return input geometry if input geometry is empty */
350 if ( gserialized_is_empty(in_geom) )
351 {
352 PG_RETURN_POINTER(in_geom);
353 }
354
355 /* Return input geometry if input grid is meaningless */
356 if ( grid.xsize==0 && grid.ysize==0 && grid.zsize==0 && grid.msize==0 )
357 {
358 PG_RETURN_POINTER(in_geom);
359 }
360
361 in_lwgeom = lwgeom_from_gserialized(in_geom);
362
363 POSTGIS_DEBUGF(3, "SnapToGrid got a %s", lwtype_name(in_lwgeom->type));
364
365 out_lwgeom = lwgeom_grid(in_lwgeom, &grid);
366 if ( out_lwgeom == NULL ) PG_RETURN_NULL();
367
368 /* COMPUTE_BBOX TAINTING */
369 if ( in_lwgeom->bbox )
370 lwgeom_refresh_bbox(out_lwgeom);
371
372 POSTGIS_DEBUGF(3, "SnapToGrid made a %s", lwtype_name(out_lwgeom->type));
373
374 out_geom = geometry_serialize(out_lwgeom);
375
376 PG_RETURN_POINTER(out_geom);
377}
378
379
380#if POSTGIS_DEBUG_LEVEL >= 4
381/* Print grid using given reporter */
382static void
383grid_print(const gridspec *grid)
384{
385 lwpgnotice("GRID(%g %g %g %g, %g %g %g %g)",
386 grid->ipx, grid->ipy, grid->ipz, grid->ipm,
387 grid->xsize, grid->ysize, grid->zsize, grid->msize);
388}
389#endif
390
391
393Datum LWGEOM_snaptogrid_pointoff(PG_FUNCTION_ARGS)
394{
395 GSERIALIZED *in_geom, *in_point;
396 LWGEOM *in_lwgeom;
397 LWPOINT *in_lwpoint;
398 GSERIALIZED *out_geom = NULL;
399 LWGEOM *out_lwgeom;
400 gridspec grid;
401 /* BOX3D box3d; */
402 POINT4D offsetpoint;
403
404 in_geom = PG_GETARG_GSERIALIZED_P(0);
405
406 /* Return input geometry if input geometry is empty */
407 if ( gserialized_is_empty(in_geom) )
408 {
409 PG_RETURN_POINTER(in_geom);
410 }
411
412 in_point = PG_GETARG_GSERIALIZED_P(1);
413 in_lwpoint = lwgeom_as_lwpoint(lwgeom_from_gserialized(in_point));
414 if ( in_lwpoint == NULL )
415 {
416 lwpgerror("Offset geometry must be a point");
417 }
418
419 grid.xsize = PG_GETARG_FLOAT8(2);
420 grid.ysize = PG_GETARG_FLOAT8(3);
421 grid.zsize = PG_GETARG_FLOAT8(4);
422 grid.msize = PG_GETARG_FLOAT8(5);
423
424 /* Take offsets from point geometry */
425 getPoint4d_p(in_lwpoint->point, 0, &offsetpoint);
426 grid.ipx = offsetpoint.x;
427 grid.ipy = offsetpoint.y;
428 grid.ipz = lwgeom_has_z((LWGEOM*)in_lwpoint) ? offsetpoint.z : 0;
429 grid.ipm = lwgeom_has_m((LWGEOM*)in_lwpoint) ? offsetpoint.m : 0;
430
431#if POSTGIS_DEBUG_LEVEL >= 4
432 grid_print(&grid);
433#endif
434
435 /* Return input geometry if input grid is meaningless */
436 if ( grid.xsize==0 && grid.ysize==0 && grid.zsize==0 && grid.msize==0 )
437 {
438 PG_RETURN_POINTER(in_geom);
439 }
440
441 in_lwgeom = lwgeom_from_gserialized(in_geom);
442
443 POSTGIS_DEBUGF(3, "SnapToGrid got a %s", lwtype_name(in_lwgeom->type));
444
445 out_lwgeom = lwgeom_grid(in_lwgeom, &grid);
446 if ( out_lwgeom == NULL ) PG_RETURN_NULL();
447
448 /* COMPUTE_BBOX TAINTING */
449 if (in_lwgeom->bbox)
450 {
451 lwgeom_refresh_bbox(out_lwgeom);
452 }
453
454 POSTGIS_DEBUGF(3, "SnapToGrid made a %s", lwtype_name(out_lwgeom->type));
455
456 out_geom = geometry_serialize(out_lwgeom);
457
458 PG_RETURN_POINTER(out_geom);
459}
460
461
462/*
463** crossingDirection(line1, line2)
464**
465** Determines crossing direction of line2 relative to line1.
466** Only accepts LINESTRING as parameters!
467*/
469Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS)
470{
471 int type1, type2, rv;
472 LWLINE *l1 = NULL;
473 LWLINE *l2 = NULL;
474 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
475 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
476
477 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
478
479 type1 = gserialized_get_type(geom1);
480 type2 = gserialized_get_type(geom2);
481
482 if ( type1 != LINETYPE || type2 != LINETYPE )
483 {
484 elog(ERROR,"This function only accepts LINESTRING as arguments.");
485 PG_RETURN_NULL();
486 }
487
490
491 rv = lwline_crossing_direction(l1, l2);
492
493 PG_FREE_IF_COPY(geom1, 0);
494 PG_FREE_IF_COPY(geom2, 1);
495
496 PG_RETURN_INT32(rv);
497
498}
499
500
501
502/***********************************************************************
503 * --strk@kbt.io
504 ***********************************************************************/
505
506Datum LWGEOM_line_substring(PG_FUNCTION_ARGS);
507
509Datum LWGEOM_line_substring(PG_FUNCTION_ARGS)
510{
511 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
512 double from = PG_GETARG_FLOAT8(1);
513 double to = PG_GETARG_FLOAT8(2);
514 LWGEOM *olwgeom;
515 POINTARRAY *ipa, *opa;
516 GSERIALIZED *ret;
517 int type = gserialized_get_type(geom);
518
519 if ( from < 0 || from > 1 )
520 {
521 elog(ERROR,"line_interpolate_point: 2nd arg isn't within [0,1]");
522 PG_RETURN_NULL();
523 }
524
525 if ( to < 0 || to > 1 )
526 {
527 elog(ERROR,"line_interpolate_point: 3rd arg isn't within [0,1]");
528 PG_RETURN_NULL();
529 }
530
531 if ( from > to )
532 {
533 elog(ERROR, "2nd arg must be smaller then 3rd arg");
534 PG_RETURN_NULL();
535 }
536
537 if ( type == LINETYPE )
538 {
540
541 if ( lwgeom_is_empty((LWGEOM*)iline) )
542 {
543 /* TODO return empty line */
544 lwline_release(iline);
545 PG_FREE_IF_COPY(geom, 0);
546 PG_RETURN_NULL();
547 }
548
549 ipa = iline->points;
550
551 opa = ptarray_substring(ipa, from, to, 0);
552
553 if ( opa->npoints == 1 ) /* Point returned */
554 olwgeom = (LWGEOM *)lwpoint_construct(iline->srid, NULL, opa);
555 else
556 olwgeom = (LWGEOM *)lwline_construct(iline->srid, NULL, opa);
557
558 }
559 else if ( type == MULTILINETYPE )
560 {
561 LWMLINE *iline;
562 uint32_t i = 0, g = 0;
563 int homogeneous = LW_TRUE;
564 LWGEOM **geoms = NULL;
565 double length = 0.0, sublength = 0.0, minprop = 0.0, maxprop = 0.0;
566
568
569 if ( lwgeom_is_empty((LWGEOM*)iline) )
570 {
571 /* TODO return empty collection */
572 lwmline_release(iline);
573 PG_FREE_IF_COPY(geom, 0);
574 PG_RETURN_NULL();
575 }
576
577 /* Calculate the total length of the mline */
578 for ( i = 0; i < iline->ngeoms; i++ )
579 {
580 LWLINE *subline = (LWLINE*)iline->geoms[i];
581 if ( subline->points && subline->points->npoints > 1 )
582 length += ptarray_length_2d(subline->points);
583 }
584
585 geoms = lwalloc(sizeof(LWGEOM*) * iline->ngeoms);
586
587 /* Slice each sub-geometry of the multiline */
588 for ( i = 0; i < iline->ngeoms; i++ )
589 {
590 LWLINE *subline = (LWLINE*)iline->geoms[i];
591 double subfrom = 0.0, subto = 0.0;
592
593 if ( subline->points && subline->points->npoints > 1 )
594 sublength += ptarray_length_2d(subline->points);
595
596 /* Calculate proportions for this subline */
597 minprop = maxprop;
598 maxprop = sublength / length;
599
600 /* This subline doesn't reach the lowest proportion requested
601 or is beyond the highest proporton */
602 if ( from > maxprop || to < minprop )
603 continue;
604
605 if ( from <= minprop )
606 subfrom = 0.0;
607 if ( to >= maxprop )
608 subto = 1.0;
609
610 if ( from > minprop && from <= maxprop )
611 subfrom = (from - minprop) / (maxprop - minprop);
612
613 if ( to < maxprop && to >= minprop )
614 subto = (to - minprop) / (maxprop - minprop);
615
616
617 opa = ptarray_substring(subline->points, subfrom, subto, 0);
618 if ( opa && opa->npoints > 0 )
619 {
620 if ( opa->npoints == 1 ) /* Point returned */
621 {
622 geoms[g] = (LWGEOM *)lwpoint_construct(SRID_UNKNOWN, NULL, opa);
623 homogeneous = LW_FALSE;
624 }
625 else
626 {
627 geoms[g] = (LWGEOM *)lwline_construct(SRID_UNKNOWN, NULL, opa);
628 }
629 g++;
630 }
631
632
633
634 }
635 /* If we got any points, we need to return a GEOMETRYCOLLECTION */
636 if ( ! homogeneous )
637 type = COLLECTIONTYPE;
638
639 olwgeom = (LWGEOM*)lwcollection_construct(type, iline->srid, NULL, g, geoms);
640 }
641 else
642 {
643 elog(ERROR,"line_substring: 1st arg isn't a line");
644 PG_RETURN_NULL();
645 }
646
647 ret = geometry_serialize(olwgeom);
648 lwgeom_free(olwgeom);
649 PG_FREE_IF_COPY(geom, 0);
650 PG_RETURN_POINTER(ret);
651
652}
653
654
655/**********************************************************************
656 *
657 * ST_MinimumBoundingRadius
658 *
659 **********************************************************************/
660
662Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS)
663{
664 GSERIALIZED* geom;
665 LWGEOM* input;
666 LWBOUNDINGCIRCLE* mbc = NULL;
667 LWGEOM* lwcenter;
668 GSERIALIZED* center;
669 TupleDesc resultTupleDesc;
670 HeapTuple resultTuple;
671 Datum result;
672 Datum result_values[2];
673 bool result_is_null[2];
674 double radius = 0;
675
676 if (PG_ARGISNULL(0))
677 PG_RETURN_NULL();
678
679 geom = PG_GETARG_GSERIALIZED_P(0);
680
681 /* Empty geometry? Return POINT EMPTY with zero radius */
682 if (gserialized_is_empty(geom))
683 {
685 }
686 else
687 {
688 input = lwgeom_from_gserialized(geom);
689 mbc = lwgeom_calculate_mbc(input);
690
691 if (!(mbc && mbc->center))
692 {
693 lwpgerror("Error calculating minimum bounding circle.");
694 lwgeom_free(input);
695 PG_RETURN_NULL();
696 }
697
698 lwcenter = (LWGEOM*) lwpoint_make2d(input->srid, mbc->center->x, mbc->center->y);
699 radius = mbc->radius;
700
702 lwgeom_free(input);
703 }
704
705 center = geometry_serialize(lwcenter);
706 lwgeom_free(lwcenter);
707
708 get_call_result_type(fcinfo, NULL, &resultTupleDesc);
709 BlessTupleDesc(resultTupleDesc);
710
711 result_values[0] = PointerGetDatum(center);
712 result_is_null[0] = false;
713 result_values[1] = Float8GetDatum(radius);
714 result_is_null[1] = false;
715
716 resultTuple = heap_form_tuple(resultTupleDesc, result_values, result_is_null);
717
718 result = HeapTupleGetDatum(resultTuple);
719
720 PG_RETURN_DATUM(result);
721}
722
723/**********************************************************************
724 *
725 * ST_MinimumBoundingCircle
726 *
727 **********************************************************************/
728
730Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS)
731{
732 GSERIALIZED* geom;
733 LWGEOM* input;
734 LWBOUNDINGCIRCLE* mbc = NULL;
735 LWGEOM* lwcircle;
736 GSERIALIZED* center;
737 int segs_per_quarter;
738
739 if (PG_ARGISNULL(0))
740 PG_RETURN_NULL();
741
742 geom = PG_GETARG_GSERIALIZED_P(0);
743 segs_per_quarter = PG_GETARG_INT32(1);
744
745 /* Empty geometry? Return POINT EMPTY */
746 if (gserialized_is_empty(geom))
747 {
749 }
750 else
751 {
752 input = lwgeom_from_gserialized(geom);
753 mbc = lwgeom_calculate_mbc(input);
754
755 if (!(mbc && mbc->center))
756 {
757 lwpgerror("Error calculating minimum bounding circle.");
758 lwgeom_free(input);
759 PG_RETURN_NULL();
760 }
761
762 /* Zero radius? Return a point. */
763 if (mbc->radius == 0)
764 lwcircle = lwpoint_as_lwgeom(lwpoint_make2d(input->srid, mbc->center->x, mbc->center->y));
765 else
766 lwcircle = lwpoly_as_lwgeom(lwpoly_construct_circle(input->srid, mbc->center->x, mbc->center->y, mbc->radius, segs_per_quarter, LW_TRUE));
767
769 lwgeom_free(input);
770 }
771
772 center = geometry_serialize(lwcircle);
773 lwgeom_free(lwcircle);
774
775 PG_RETURN_POINTER(center);
776}
777
778/**********************************************************************
779 *
780 * ST_GeometricMedian
781 *
782 **********************************************************************/
783
785Datum ST_GeometricMedian(PG_FUNCTION_ARGS)
786{
787 GSERIALIZED* geom;
789 LWGEOM* input;
790 LWPOINT* lwresult;
791 static const double min_default_tolerance = 1e-8;
792 double tolerance = min_default_tolerance;
793 bool compute_tolerance_from_box;
794 bool fail_if_not_converged;
795 int max_iter;
796
797 /* Read and validate our input arguments */
798 if (PG_ARGISNULL(0))
799 PG_RETURN_NULL();
800
801 compute_tolerance_from_box = PG_ARGISNULL(1);
802
803 if (!compute_tolerance_from_box)
804 {
805 tolerance = PG_GETARG_FLOAT8(1);
806 if (tolerance < 0)
807 {
808 lwpgerror("Tolerance must be positive.");
809 PG_RETURN_NULL();
810 }
811 }
812
813 max_iter = PG_ARGISNULL(2) ? -1 : PG_GETARG_INT32(2);
814 fail_if_not_converged = PG_ARGISNULL(3) ? LW_FALSE : PG_GETARG_BOOL(3);
815
816 if (max_iter < 0)
817 {
818 lwpgerror("Maximum iterations must be positive.");
819 PG_RETURN_NULL();
820 }
821
822 /* OK, inputs are valid. */
823 geom = PG_GETARG_GSERIALIZED_P(0);
824 input = lwgeom_from_gserialized(geom);
825
826 if (compute_tolerance_from_box)
827 {
828 /* Compute a default tolerance based on the smallest dimension
829 * of the geometry's bounding box.
830 */
831 static const double tolerance_coefficient = 1e-6;
832 const GBOX* box = lwgeom_get_bbox(input);
833
834 if (box)
835 {
836 double min_dim = FP_MIN(box->xmax - box->xmin, box->ymax - box->ymin);
837 if (lwgeom_has_z(input))
838 min_dim = FP_MIN(min_dim, box->zmax - box->zmin);
839
840 /* Apply a lower bound to the computed default tolerance to
841 * avoid a tolerance of zero in the case of collinear
842 * points.
843 */
844 tolerance = FP_MAX(min_default_tolerance, tolerance_coefficient * min_dim);
845 }
846 }
847
848 lwresult = lwgeom_median(input, tolerance, max_iter, fail_if_not_converged);
849 lwgeom_free(input);
850
851 if(!lwresult)
852 {
853 lwpgerror("Error computing geometric median.");
854 PG_RETURN_NULL();
855 }
856
857 result = geometry_serialize(lwpoint_as_lwgeom(lwresult));
858
859 PG_RETURN_POINTER(result);
860}
861
862/**********************************************************************
863 *
864 * ST_IsPolygonCW
865 *
866 **********************************************************************/
867
869Datum ST_IsPolygonCW(PG_FUNCTION_ARGS)
870{
871 GSERIALIZED* geom;
872
873 if (PG_ARGISNULL(0))
874 PG_RETURN_NULL();
875
876 geom = PG_GETARG_GSERIALIZED_P(0);
878}
879
880/**********************************************************************
881 *
882 * ST_IsPolygonCCW
883 *
884 **********************************************************************/
885
887Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS)
888{
889 GSERIALIZED* geom;
890
891 if (PG_ARGISNULL(0))
892 PG_RETURN_NULL();
893
894 geom = PG_GETARG_GSERIALIZED_P(0);
895 geom = PG_GETARG_GSERIALIZED_P(0);
897}
898
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
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,...
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:332
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition lwgeom.c:735
LWPOINT * lwline_interpolate_point_3d(const LWLINE *line, double distance)
Interpolate one point along a line in 3D.
Definition lwline.c:615
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:372
#define LW_FALSE
Definition liblwgeom.h:94
#define COLLECTIONTYPE
Definition liblwgeom.h:108
POINTARRAY * lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat)
Interpolate one or more points along a line.
Definition lwline.c:538
int lwgeom_has_orientation(const LWGEOM *lwgeom, int orientation)
Tests that geometry is oriented LW_CLOCKWISE or LW_COUNTERCLOCKWISE.
Definition lwgeom.c:93
int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition lwgeom.c:1851
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
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, gridspec *grid)
Definition lwgeom.c:2421
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#define MULTILINETYPE
Definition liblwgeom.h:106
#define LINETYPE
Definition liblwgeom.h:103
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:1211
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:105
int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
Given two lines, characterize how (and if) they cross each other.
double ptarray_length_2d(const POINTARRAY *pts)
Find the 2d length of the given POINTARRAY (even if it's 3d)
Definition ptarray.c:1975
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition lwgeom.c:962
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
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:367
LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom)
Definition lwgeom.c:279
void lwboundingcircle_destroy(LWBOUNDINGCIRCLE *c)
LWPOINT * lwgeom_median(const LWGEOM *g, double tol, uint32_t maxiter, char fail_if_not_converged)
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:207
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:93
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:771
#define SRID_UNKNOWN
Unknown SRID value.
Definition liblwgeom.h:215
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition lwgeom.c:969
LWGEOM * lwgeom_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint)
Definition lwchaikins.c:182
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition lwgeom.c:357
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition lwgeom.c:723
This library is the generic geometry handling section of PostGIS.
#define LW_CLOCKWISE
Constants for orientation checking and forcing.
#define LW_COUNTERCLOCKWISE
#define FP_MAX(A, B)
#define FP_MIN(A, B)
Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS)
Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS)
Datum LWGEOM_ChaikinSmoothing(PG_FUNCTION_ARGS)
Datum ST_GeometricMedian(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(LWGEOM_simplify2d)
Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS)
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)
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 LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition lwinline.h:127
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:199
static double distance(double x1, double y1, double x2, double y2)
Definition lwtree.c:1032
double ymax
Definition liblwgeom.h:357
double zmax
Definition liblwgeom.h:359
double xmax
Definition liblwgeom.h:355
double zmin
Definition liblwgeom.h:358
double ymin
Definition liblwgeom.h:356
double xmin
Definition liblwgeom.h:354
POINT2D * center
Definition liblwgeom.h:1846
uint8_t type
Definition liblwgeom.h:462
GBOX * bbox
Definition liblwgeom.h:458
int32_t srid
Definition liblwgeom.h:460
POINTARRAY * points
Definition liblwgeom.h:483
int32_t srid
Definition liblwgeom.h:484
int32_t srid
Definition liblwgeom.h:548
LWLINE ** geoms
Definition liblwgeom.h:547
uint32_t ngeoms
Definition liblwgeom.h:552
POINTARRAY * point
Definition liblwgeom.h:471
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
double m
Definition liblwgeom.h:414
double x
Definition liblwgeom.h:414
double z
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414
uint32_t npoints
Definition liblwgeom.h:427
double ipm
Definition liblwgeom.h:1404
double zsize
Definition liblwgeom.h:1407
double ysize
Definition liblwgeom.h:1406
double xsize
Definition liblwgeom.h:1405
double ipx
Definition liblwgeom.h:1401
double msize
Definition liblwgeom.h:1408
double ipy
Definition liblwgeom.h:1402
double ipz
Definition liblwgeom.h:1403
Snap-to-grid.
Definition liblwgeom.h:1400