PostGIS  3.7.0dev-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_itree.h"
34 
35 #include "access/htup_details.h"
36 
37 /* Prototypes */
38 Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS);
39 Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS);
40 Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS);
41 Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS);
42 Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS);
43 Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS);
44 Datum ST_GeometricMedian(PG_FUNCTION_ARGS);
45 Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS);
46 Datum 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 
58 Datum 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 
90 Datum 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 
124 Datum 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  ***********************************************************************/
176 Datum 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 
204  lwgeom_free(lwline_as_lwgeom(lwline));
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 
225 Datum ST_3DLineInterpolatePoint(PG_FUNCTION_ARGS);
226 
228 Datum 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 
252  point = lwline_interpolate_point_3d(line, distance);
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 */
326 Datum LWGEOM_snaptogrid(PG_FUNCTION_ARGS);
327 Datum LWGEOM_snaptogrid_pointoff(PG_FUNCTION_ARGS);
328 
329 
330 
332 Datum 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 */
382 static void
383 grid_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 
393 Datum 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 */
469 Datum 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 
506 Datum LWGEOM_line_substring(PG_FUNCTION_ARGS);
507 
509 Datum 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 )
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 
662 Datum 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 
730 Datum 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 
785 Datum 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 
869 Datum ST_IsPolygonCW(PG_FUNCTION_ARGS)
870 {
871  GSERIALIZED* geom;
872  LWGEOM* input;
873  bool is_clockwise;
874 
875  if (PG_ARGISNULL(0))
876  PG_RETURN_NULL();
877 
878  geom = PG_GETARG_GSERIALIZED_P(0);
879  input = lwgeom_from_gserialized(geom);
880 
882 
883  lwgeom_free(input);
884  PG_FREE_IF_COPY(geom, 0);
885 
886  PG_RETURN_BOOL(is_clockwise);
887 }
888 
889 /**********************************************************************
890  *
891  * ST_IsPolygonCCW
892  *
893  **********************************************************************/
894 
896 Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS)
897 {
898  GSERIALIZED* geom;
899  LWGEOM* input;
900  bool is_ccw;
901 
902  if (PG_ARGISNULL(0))
903  PG_RETURN_NULL();
904 
905  geom = PG_GETARG_GSERIALIZED_P_COPY(0);
906  input = lwgeom_from_gserialized(geom);
908  is_ccw = lwgeom_is_clockwise(input);
909  lwgeom_free(input);
910  PG_FREE_IF_COPY(geom, 0);
911 
912  PG_RETURN_BOOL(is_ccw);
913 }
914 
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)
Definition: gserialized.c:432
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:155
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:268
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:181
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:118
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoint.c:151
LWPOLY * lwpoly_construct_circle(int32_t srid, double x, double y, double radius, uint32_t segments_per_quarter, char exterior)
Definition: lwpoly.c:120
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition: lwgeom.c:707
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
LWPOINT * lwline_interpolate_point_3d(const LWLINE *line, double distance)
Interpolate one point along a line in 3D.
Definition: lwline.c:605
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, gridspec *grid)
Definition: lwgeom.c:2393
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
#define LW_FALSE
Definition: liblwgeom.h:94
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition: lwgeom.c:1823
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:1083
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition: lwgeom.c:304
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:329
void lwline_release(LWLINE *lwline)
Definition: lwline.c:125
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
Given two lines, characterize how (and if) they cross each other.
Definition: lwalgorithm.c:467
double ptarray_length_2d(const POINTARRAY *pts)
Find the 2d length of the given POINTARRAY (even if it's 3d)
Definition: ptarray.c:1840
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
void lwmline_release(LWMLINE *lwline)
Definition: lwmline.c:32
LWBOUNDINGCIRCLE * lwgeom_calculate_mbc(const LWGEOM *g)
LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom)
Definition: lwgeom.c:251
POINTARRAY * lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat)
Interpolate one or more points along a line.
Definition: lwline.c:528
LWGEOM * lwgeom_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint)
Definition: lwchaikins.c:182
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:129
void lwboundingcircle_destroy(LWBOUNDINGCIRCLE *c)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:125
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:743
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:42
LWPOINT * lwgeom_median(const LWGEOM *g, double tol, uint32_t maxiter, char fail_if_not_converged)
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
int lwgeom_is_clockwise(LWGEOM *lwgeom)
Ensure the outer ring is clockwise oriented and all inner rings are counter-clockwise.
Definition: lwgeom.c:66
LWMPOINT * lwmpoint_construct(int32_t srid, const POINTARRAY *pa)
Definition: lwmpoint.c:52
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:695
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:103
This library is the generic geometry handling section of PostGIS.
#define FP_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)
if(!(yy_init))
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 LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:127
static double distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
type
Definition: ovdump.py:42
static int is_clockwise(int num_points, double *x, double *y, double *z)
double ymax
Definition: liblwgeom.h:357
double zmax
Definition: liblwgeom.h:359
double xmax
Definition: liblwgeom.h:355
double zmin
Definition: liblwgeom.h:358
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
POINT2D * center
Definition: liblwgeom.h:1843
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:1402
double zsize
Definition: liblwgeom.h:1405
double ysize
Definition: liblwgeom.h:1404
double xsize
Definition: liblwgeom.h:1403
double ipx
Definition: liblwgeom.h:1399
double msize
Definition: liblwgeom.h:1406
double ipy
Definition: liblwgeom.h:1400
double ipz
Definition: liblwgeom.h:1401
Snap-to-grid.
Definition: liblwgeom.h:1398