PostGIS  2.2.8dev-r@@SVN_REVISION@@
geography_measurement.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  *
5  * Copyright (C) 2009 Paul Ramsey <pramsey@cleverelephant.ca>
6  *
7  * This is free software; you can redistribute and/or modify it under
8  * the terms of the GNU General Public Licence. See the COPYING file.
9  *
10  **********************************************************************/
11 
12 #include "postgres.h"
13 
14 #include "../postgis_config.h"
15 
16 #include <math.h>
17 #include <float.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <errno.h>
21 
22 #include "liblwgeom.h" /* For standard geometry types. */
23 #include "liblwgeom_internal.h" /* For FP comparators. */
24 #include "lwgeom_pg.h" /* For debugging macros. */
25 #include "geography.h" /* For utility functions. */
26 #include "geography_measurement_trees.h" /* For circ_tree caching */
27 #include "lwgeom_transform.h" /* For SRID functions */
28 
29 #if PROJ_GEODESIC
30 /* round to 10 nm precision */
31 #define INVMINDIST 1.0e8
32 #else
33 /* round to 100 nm precision */
34 #define INVMINDIST 1.0e9
35 #endif
36 
37 Datum geography_distance(PG_FUNCTION_ARGS);
38 Datum geography_distance_uncached(PG_FUNCTION_ARGS);
39 Datum geography_distance_knn(PG_FUNCTION_ARGS);
40 Datum geography_distance_tree(PG_FUNCTION_ARGS);
41 Datum geography_dwithin(PG_FUNCTION_ARGS);
42 Datum geography_dwithin_uncached(PG_FUNCTION_ARGS);
43 Datum geography_area(PG_FUNCTION_ARGS);
44 Datum geography_length(PG_FUNCTION_ARGS);
45 Datum geography_expand(PG_FUNCTION_ARGS);
46 Datum geography_point_outside(PG_FUNCTION_ARGS);
47 Datum geography_covers(PG_FUNCTION_ARGS);
48 Datum geography_bestsrid(PG_FUNCTION_ARGS);
49 Datum geography_perimeter(PG_FUNCTION_ARGS);
50 Datum geography_project(PG_FUNCTION_ARGS);
51 Datum geography_azimuth(PG_FUNCTION_ARGS);
52 Datum geography_segmentize(PG_FUNCTION_ARGS);
53 
54 
56 Datum geography_distance_knn(PG_FUNCTION_ARGS)
57 {
58  LWGEOM *lwgeom1 = NULL;
59  LWGEOM *lwgeom2 = NULL;
60  GSERIALIZED *g1 = NULL;
61  GSERIALIZED *g2 = NULL;
62  double distance;
63  double tolerance = FP_TOLERANCE;
64  bool use_spheroid = false; /* must use sphere, can't get index to harmonize with spheroid */
65  SPHEROID s;
66 
67  /* Get our geometry objects loaded into memory. */
68  g1 = PG_GETARG_GSERIALIZED_P(0);
69  g2 = PG_GETARG_GSERIALIZED_P(1);
70 
71  /* Initialize spheroid */
72  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
73 
75 
76  /* Set to sphere if requested */
77  if ( ! use_spheroid )
78  s.a = s.b = s.radius;
79 
80  lwgeom1 = lwgeom_from_gserialized(g1);
81  lwgeom2 = lwgeom_from_gserialized(g2);
82 
83  /* Return NULL on empty arguments. */
84  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
85  {
86  PG_FREE_IF_COPY(g1, 0);
87  PG_FREE_IF_COPY(g2, 1);
88  PG_RETURN_NULL();
89  }
90 
91  /* Make sure we have boxes attached */
92  lwgeom_add_bbox_deep(lwgeom1, NULL);
93  lwgeom_add_bbox_deep(lwgeom2, NULL);
94 
95  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
96 
97  POSTGIS_DEBUGF(2, "[GIST] '%s' got distance %g", __func__, distance);
98 
99  /* Clean up */
100  lwgeom_free(lwgeom1);
101  lwgeom_free(lwgeom2);
102  PG_FREE_IF_COPY(g1, 0);
103  PG_FREE_IF_COPY(g2, 1);
104 
105  /* Something went wrong, negative return... should already be eloged, return NULL */
106  if ( distance < 0.0 )
107  {
108  PG_RETURN_NULL();
109  }
110 
111  PG_RETURN_FLOAT8(distance);
112 }
113 
114 /*
115 ** geography_distance_uncached(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
116 ** returns double distance in meters
117 */
119 Datum geography_distance_uncached(PG_FUNCTION_ARGS)
120 {
121  LWGEOM *lwgeom1 = NULL;
122  LWGEOM *lwgeom2 = NULL;
123  GSERIALIZED *g1 = NULL;
124  GSERIALIZED *g2 = NULL;
125  double distance;
126  double tolerance = FP_TOLERANCE;
127  bool use_spheroid = true;
128  SPHEROID s;
129 
130  /* Get our geometry objects loaded into memory. */
131  g1 = PG_GETARG_GSERIALIZED_P(0);
132  g2 = PG_GETARG_GSERIALIZED_P(1);
133 
134  /* Read our tolerance value. */
135  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
136  tolerance = PG_GETARG_FLOAT8(2);
137 
138  /* Read our calculation type. */
139  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
140  use_spheroid = PG_GETARG_BOOL(3);
141 
143 
144  /* Initialize spheroid */
145  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
146 
147  /* Set to sphere if requested */
148  if ( ! use_spheroid )
149  s.a = s.b = s.radius;
150 
151  lwgeom1 = lwgeom_from_gserialized(g1);
152  lwgeom2 = lwgeom_from_gserialized(g2);
153 
154  /* Return NULL on empty arguments. */
155  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
156  {
157  PG_FREE_IF_COPY(g1, 0);
158  PG_FREE_IF_COPY(g2, 1);
159  PG_RETURN_NULL();
160  }
161 
162  /* Make sure we have boxes attached */
163  lwgeom_add_bbox_deep(lwgeom1, NULL);
164  lwgeom_add_bbox_deep(lwgeom2, NULL);
165 
166  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
167 
168  POSTGIS_DEBUGF(2, "[GIST] '%s' got distance %g", __func__, distance);
169 
170  /* Clean up */
171  lwgeom_free(lwgeom1);
172  lwgeom_free(lwgeom2);
173  PG_FREE_IF_COPY(g1, 0);
174  PG_FREE_IF_COPY(g2, 1);
175 
176  /* Something went wrong, negative return... should already be eloged, return NULL */
177  if ( distance < 0.0 )
178  {
179  PG_RETURN_NULL();
180  }
181 
182  PG_RETURN_FLOAT8(distance);
183 }
184 
185 
186 /*
187 ** geography_distance(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
188 ** returns double distance in meters
189 */
191 Datum geography_distance(PG_FUNCTION_ARGS)
192 {
193  GSERIALIZED* g1 = NULL;
194  GSERIALIZED* g2 = NULL;
195  double distance;
196  double tolerance = 0.0;
197  bool use_spheroid = true;
198  SPHEROID s;
199 
200  /* Get our geometry objects loaded into memory. */
201  g1 = PG_GETARG_GSERIALIZED_P(0);
202  g2 = PG_GETARG_GSERIALIZED_P(1);
203 
204  /* Read our tolerance value. */
205  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
206  tolerance = PG_GETARG_FLOAT8(2);
207 
208  /* Read our calculation type. */
209  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
210  use_spheroid = PG_GETARG_BOOL(3);
211 
213 
214  /* Initialize spheroid */
215  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
216 
217  /* Set to sphere if requested */
218  if ( ! use_spheroid )
219  s.a = s.b = s.radius;
220 
221  /* Return NULL on empty arguments. */
223  {
224  PG_FREE_IF_COPY(g1, 0);
225  PG_FREE_IF_COPY(g2, 1);
226  PG_RETURN_NULL();
227  }
228 
229  /* Do the brute force calculation if the cached calculation doesn't tick over */
230  if ( LW_FAILURE == geography_distance_cache(fcinfo, g1, g2, &s, &distance) )
231  {
232  LWGEOM* lwgeom1 = lwgeom_from_gserialized(g1);
233  LWGEOM* lwgeom2 = lwgeom_from_gserialized(g2);
234  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
235  lwgeom_free(lwgeom1);
236  lwgeom_free(lwgeom2);
237  }
238 
239  /* Clean up */
240  PG_FREE_IF_COPY(g1, 0);
241  PG_FREE_IF_COPY(g2, 1);
242 
243  /* Knock off any funny business at the nanometer level, ticket #2168 */
244  distance = round(distance * INVMINDIST) / INVMINDIST;
245 
246  /* Something went wrong, negative return... should already be eloged, return NULL */
247  if ( distance < 0.0 )
248  {
249  elog(ERROR, "distance returned negative!");
250  PG_RETURN_NULL();
251  }
252 
253  PG_RETURN_FLOAT8(distance);
254 }
255 
256 
257 /*
258 ** geography_dwithin(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
259 ** returns double distance in meters
260 */
262 Datum geography_dwithin(PG_FUNCTION_ARGS)
263 {
264  GSERIALIZED *g1 = NULL;
265  GSERIALIZED *g2 = NULL;
266  double tolerance = 0.0;
267  double distance;
268  bool use_spheroid = true;
269  SPHEROID s;
270  int dwithin = LW_FALSE;
271 
272  /* Get our geometry objects loaded into memory. */
273  g1 = PG_GETARG_GSERIALIZED_P(0);
274  g2 = PG_GETARG_GSERIALIZED_P(1);
275 
276  /* Read our tolerance value. */
277  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
278  tolerance = PG_GETARG_FLOAT8(2);
279 
280  /* Read our calculation type. */
281  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
282  use_spheroid = PG_GETARG_BOOL(3);
283 
285 
286  /* Initialize spheroid */
287  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
288 
289  /* Set to sphere if requested */
290  if ( ! use_spheroid )
291  s.a = s.b = s.radius;
292 
293  /* Return FALSE on empty arguments. */
295  {
296  PG_FREE_IF_COPY(g1, 0);
297  PG_FREE_IF_COPY(g2, 1);
298  PG_RETURN_BOOL(FALSE);
299  }
300 
301  /* Do the brute force calculation if the cached calculation doesn't tick over */
302  if ( LW_FAILURE == geography_dwithin_cache(fcinfo, g1, g2, &s, tolerance, &dwithin) )
303  {
304  LWGEOM* lwgeom1 = lwgeom_from_gserialized(g1);
305  LWGEOM* lwgeom2 = lwgeom_from_gserialized(g2);
306  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
307  /* Something went wrong... */
308  if ( distance < 0.0 )
309  elog(ERROR, "lwgeom_distance_spheroid returned negative!");
310  dwithin = (distance <= tolerance);
311  lwgeom_free(lwgeom1);
312  lwgeom_free(lwgeom2);
313  }
314 
315  /* Clean up */
316  PG_FREE_IF_COPY(g1, 0);
317  PG_FREE_IF_COPY(g2, 1);
318 
319  PG_RETURN_BOOL(dwithin);
320 }
321 
322 
323 /*
324 ** geography_distance_tree(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
325 ** returns double distance in meters
326 */
328 Datum geography_distance_tree(PG_FUNCTION_ARGS)
329 {
330  GSERIALIZED *g1 = NULL;
331  GSERIALIZED *g2 = NULL;
332  double tolerance = 0.0;
333  double distance;
334  bool use_spheroid = true;
335  SPHEROID s;
336 
337  /* Get our geometry objects loaded into memory. */
338  g1 = PG_GETARG_GSERIALIZED_P(0);
339  g2 = PG_GETARG_GSERIALIZED_P(1);
340 
341  /* Return FALSE on empty arguments. */
343  {
344  PG_FREE_IF_COPY(g1, 0);
345  PG_FREE_IF_COPY(g2, 1);
346  PG_RETURN_FLOAT8(0.0);
347  }
348 
349  /* Read our tolerance value. */
350  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
351  tolerance = PG_GETARG_FLOAT8(2);
352 
353  /* Read our calculation type. */
354  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
355  use_spheroid = PG_GETARG_BOOL(3);
356 
358 
359  /* Initialize spheroid */
360  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
361 
362  /* Set to sphere if requested */
363  if ( ! use_spheroid )
364  s.a = s.b = s.radius;
365 
366  if ( geography_tree_distance(g1, g2, &s, tolerance, &distance) == LW_FAILURE )
367  {
368  elog(ERROR, "geography_distance_tree failed!");
369  PG_RETURN_NULL();
370  }
371 
372  PG_RETURN_FLOAT8(distance);
373 }
374 
375 
376 
377 /*
378 ** geography_dwithin_uncached(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
379 ** returns double distance in meters
380 */
382 Datum geography_dwithin_uncached(PG_FUNCTION_ARGS)
383 {
384  LWGEOM *lwgeom1 = NULL;
385  LWGEOM *lwgeom2 = NULL;
386  GSERIALIZED *g1 = NULL;
387  GSERIALIZED *g2 = NULL;
388  double tolerance = 0.0;
389  double distance;
390  bool use_spheroid = true;
391  SPHEROID s;
392 
393  /* Get our geometry objects loaded into memory. */
394  g1 = PG_GETARG_GSERIALIZED_P(0);
395  g2 = PG_GETARG_GSERIALIZED_P(1);
396 
397  /* Read our tolerance value. */
398  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
399  tolerance = PG_GETARG_FLOAT8(2);
400 
401  /* Read our calculation type. */
402  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
403  use_spheroid = PG_GETARG_BOOL(3);
404 
406 
407  /* Initialize spheroid */
408  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
409 
410  /* Set to sphere if requested */
411  if ( ! use_spheroid )
412  s.a = s.b = s.radius;
413 
414  lwgeom1 = lwgeom_from_gserialized(g1);
415  lwgeom2 = lwgeom_from_gserialized(g2);
416 
417  /* Return FALSE on empty arguments. */
418  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
419  {
420  PG_RETURN_BOOL(FALSE);
421  }
422 
423  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
424 
425  /* Clean up */
426  lwgeom_free(lwgeom1);
427  lwgeom_free(lwgeom2);
428  PG_FREE_IF_COPY(g1, 0);
429  PG_FREE_IF_COPY(g2, 1);
430 
431  /* Something went wrong... should already be eloged, return FALSE */
432  if ( distance < 0.0 )
433  {
434  elog(ERROR, "lwgeom_distance_spheroid returned negative!");
435  PG_RETURN_BOOL(FALSE);
436  }
437 
438  PG_RETURN_BOOL(distance <= tolerance);
439 }
440 
441 
442 /*
443 ** geography_expand(GSERIALIZED *g) returns *GSERIALIZED
444 **
445 ** warning, this tricky little function does not expand the
446 ** geometry at all, just re-writes bounding box value to be
447 ** a bit bigger. only useful when passing the result along to
448 ** an index operator (&&)
449 */
451 Datum geography_expand(PG_FUNCTION_ARGS)
452 {
453  GSERIALIZED *g = NULL;
454  GSERIALIZED *g_out = NULL;
455  double distance;
456 
457  /* Get a wholly-owned pointer to the geography */
458  g = PG_GETARG_GSERIALIZED_P_COPY(0);
459 
460  /* Read our distance value and normalize to unit-sphere. */
461  distance = PG_GETARG_FLOAT8(1) / WGS84_RADIUS;
462 
463  /* Try the expansion */
464  g_out = gserialized_expand(g, distance);
465 
466  /* If the expansion fails, the return our input */
467  if ( g_out == NULL )
468  {
469  PG_RETURN_POINTER(g);
470  }
471 
472  if ( g_out != g )
473  {
474  pfree(g);
475  }
476 
477  PG_RETURN_POINTER(g_out);
478 }
479 
480 /*
481 ** geography_area(GSERIALIZED *g)
482 ** returns double area in meters square
483 */
485 Datum geography_area(PG_FUNCTION_ARGS)
486 {
487  LWGEOM *lwgeom = NULL;
488  GSERIALIZED *g = NULL;
489  GBOX gbox;
490  double area;
491  bool use_spheroid = LW_TRUE;
492  SPHEROID s;
493 
494  /* Get our geometry object loaded into memory. */
495  g = PG_GETARG_GSERIALIZED_P(0);
496 
497  /* Read our calculation type */
498  use_spheroid = PG_GETARG_BOOL(1);
499 
500  /* Initialize spheroid */
501  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
502 
503  lwgeom = lwgeom_from_gserialized(g);
504 
505  /* EMPTY things have no area */
506  if ( lwgeom_is_empty(lwgeom) )
507  {
508  lwgeom_free(lwgeom);
509  PG_RETURN_FLOAT8(0.0);
510  }
511 
512  if ( lwgeom->bbox )
513  gbox = *(lwgeom->bbox);
514  else
515  lwgeom_calculate_gbox_geodetic(lwgeom, &gbox);
516 
517 #if ! PROJ_GEODESIC
518  /* Test for cases that are currently not handled by spheroid code */
519  if ( use_spheroid )
520  {
521  /* We can't circle the poles right now */
522  if ( FP_GTEQ(gbox.zmax,1.0) || FP_LTEQ(gbox.zmin,-1.0) )
523  use_spheroid = LW_FALSE;
524  /* We can't cross the equator right now */
525  if ( gbox.zmax > 0.0 && gbox.zmin < 0.0 )
526  use_spheroid = LW_FALSE;
527  }
528 #endif /* if ! PROJ_GEODESIC */
529 
530  /* User requests spherical calculation, turn our spheroid into a sphere */
531  if ( ! use_spheroid )
532  s.a = s.b = s.radius;
533 
534  /* Calculate the area */
535  if ( use_spheroid )
536  area = lwgeom_area_spheroid(lwgeom, &s);
537  else
538  area = lwgeom_area_sphere(lwgeom, &s);
539 
540  /* Clean up */
541  lwgeom_free(lwgeom);
542  PG_FREE_IF_COPY(g, 0);
543 
544  /* Something went wrong... */
545  if ( area < 0.0 )
546  {
547  elog(ERROR, "lwgeom_area_spher(oid) returned area < 0.0");
548  PG_RETURN_NULL();
549  }
550 
551  PG_RETURN_FLOAT8(area);
552 }
553 
554 /*
555 ** geography_perimeter(GSERIALIZED *g)
556 ** returns double perimeter in meters for area features
557 */
559 Datum geography_perimeter(PG_FUNCTION_ARGS)
560 {
561  LWGEOM *lwgeom = NULL;
562  GSERIALIZED *g = NULL;
563  double length;
564  bool use_spheroid = LW_TRUE;
565  SPHEROID s;
566  int type;
567 
568  /* Get our geometry object loaded into memory. */
569  g = PG_GETARG_GSERIALIZED_P(0);
570 
571  /* Only return for area features. */
572  type = gserialized_get_type(g);
573  if ( ! (type == POLYGONTYPE || type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE) )
574  {
575  PG_RETURN_FLOAT8(0.0);
576  }
577 
578  lwgeom = lwgeom_from_gserialized(g);
579 
580  /* EMPTY things have no perimeter */
581  if ( lwgeom_is_empty(lwgeom) )
582  {
583  lwgeom_free(lwgeom);
584  PG_RETURN_FLOAT8(0.0);
585  }
586 
587  /* Read our calculation type */
588  use_spheroid = PG_GETARG_BOOL(1);
589 
590  /* Initialize spheroid */
591  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
592 
593  /* User requests spherical calculation, turn our spheroid into a sphere */
594  if ( ! use_spheroid )
595  s.a = s.b = s.radius;
596 
597  /* Calculate the length */
598  length = lwgeom_length_spheroid(lwgeom, &s);
599 
600  /* Something went wrong... */
601  if ( length < 0.0 )
602  {
603  elog(ERROR, "lwgeom_length_spheroid returned length < 0.0");
604  PG_RETURN_NULL();
605  }
606 
607  /* Clean up, but not all the way to the point arrays */
608  lwgeom_free(lwgeom);
609 
610  PG_FREE_IF_COPY(g, 0);
611  PG_RETURN_FLOAT8(length);
612 }
613 
614 /*
615 ** geography_length(GSERIALIZED *g)
616 ** returns double length in meters
617 */
619 Datum geography_length(PG_FUNCTION_ARGS)
620 {
621  LWGEOM *lwgeom = NULL;
622  GSERIALIZED *g = NULL;
623  double length;
624  bool use_spheroid = LW_TRUE;
625  SPHEROID s;
626 
627  /* Get our geometry object loaded into memory. */
628  g = PG_GETARG_GSERIALIZED_P(0);
629  lwgeom = lwgeom_from_gserialized(g);
630 
631  /* EMPTY things have no length */
632  if ( lwgeom_is_empty(lwgeom) || lwgeom->type == POLYGONTYPE || lwgeom->type == MULTIPOLYGONTYPE )
633  {
634  lwgeom_free(lwgeom);
635  PG_RETURN_FLOAT8(0.0);
636  }
637 
638  /* Read our calculation type */
639  use_spheroid = PG_GETARG_BOOL(1);
640 
641  /* Initialize spheroid */
642  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
643 
644  /* User requests spherical calculation, turn our spheroid into a sphere */
645  if ( ! use_spheroid )
646  s.a = s.b = s.radius;
647 
648  /* Calculate the length */
649  length = lwgeom_length_spheroid(lwgeom, &s);
650 
651  /* Something went wrong... */
652  if ( length < 0.0 )
653  {
654  elog(ERROR, "lwgeom_length_spheroid returned length < 0.0");
655  PG_RETURN_NULL();
656  }
657 
658  /* Clean up */
659  lwgeom_free(lwgeom);
660 
661  PG_FREE_IF_COPY(g, 0);
662  PG_RETURN_FLOAT8(length);
663 }
664 
665 /*
666 ** geography_point_outside(GSERIALIZED *g)
667 ** returns point outside the object
668 */
670 Datum geography_point_outside(PG_FUNCTION_ARGS)
671 {
672  GBOX gbox;
673  GSERIALIZED *g = NULL;
674  GSERIALIZED *g_out = NULL;
675  size_t g_out_size;
676  LWPOINT *lwpoint = NULL;
677  POINT2D pt;
678 
679  /* Get our geometry object loaded into memory. */
680  g = PG_GETARG_GSERIALIZED_P(0);
681 
682  /* We need the bounding box to get an outside point for area algorithm */
683  if ( gserialized_get_gbox_p(g, &gbox) == LW_FAILURE )
684  {
685  POSTGIS_DEBUG(4,"gserialized_get_gbox_p returned LW_FAILURE");
686  elog(ERROR, "Error in gserialized_get_gbox_p calculation.");
687  PG_RETURN_NULL();
688  }
689  POSTGIS_DEBUGF(4, "got gbox %s", gbox_to_string(&gbox));
690 
691  /* Get an exterior point, based on this gbox */
692  gbox_pt_outside(&gbox, &pt);
693 
694  lwpoint = lwpoint_make2d(4326, pt.x, pt.y);
695 
696  g_out = gserialized_from_lwgeom((LWGEOM*)lwpoint, 1, &g_out_size);
697  SET_VARSIZE(g_out, g_out_size);
698 
699  PG_FREE_IF_COPY(g, 0);
700  PG_RETURN_POINTER(g_out);
701 
702 }
703 
704 /*
705 ** geography_covers(GSERIALIZED *g, GSERIALIZED *g) returns boolean
706 ** Only works for (multi)points and (multi)polygons currently.
707 ** Attempts a simple point-in-polygon test on the polygon and point.
708 ** Current algorithm does not distinguish between points on edge
709 ** and points within.
710 */
712 Datum geography_covers(PG_FUNCTION_ARGS)
713 {
714  LWGEOM *lwgeom1 = NULL;
715  LWGEOM *lwgeom2 = NULL;
716  GSERIALIZED *g1 = NULL;
717  GSERIALIZED *g2 = NULL;
718  int type1, type2;
719  int result = LW_FALSE;
720 
721  /* Get our geometry objects loaded into memory. */
722  g1 = PG_GETARG_GSERIALIZED_P(0);
723  g2 = PG_GETARG_GSERIALIZED_P(1);
724 
725  type1 = gserialized_get_type(g1);
726  type2 = gserialized_get_type(g2);
727 
728  /* Right now we only handle points and polygons */
729  if ( ! ( (type1 == POLYGONTYPE || type1 == MULTIPOLYGONTYPE || type1 == COLLECTIONTYPE) &&
730  (type2 == POINTTYPE || type2 == MULTIPOINTTYPE || type2 == COLLECTIONTYPE) ) )
731  {
732  elog(ERROR, "geography_covers: only POLYGON and POINT types are currently supported");
733  PG_RETURN_NULL();
734  }
735 
736  /* Construct our working geometries */
737  lwgeom1 = lwgeom_from_gserialized(g1);
738  lwgeom2 = lwgeom_from_gserialized(g2);
739 
740  error_if_srid_mismatch(lwgeom1->srid, lwgeom2->srid);
741 
742  /* EMPTY never intersects with another geometry */
743  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
744  {
745  lwgeom_free(lwgeom1);
746  lwgeom_free(lwgeom2);
747  PG_FREE_IF_COPY(g1, 0);
748  PG_FREE_IF_COPY(g2, 1);
749  PG_RETURN_BOOL(false);
750  }
751 
752  /* Calculate answer */
753  result = lwgeom_covers_lwgeom_sphere(lwgeom1, lwgeom2);
754 
755  /* Clean up */
756  lwgeom_free(lwgeom1);
757  lwgeom_free(lwgeom2);
758  PG_FREE_IF_COPY(g1, 0);
759  PG_FREE_IF_COPY(g2, 1);
760 
761  PG_RETURN_BOOL(result);
762 }
763 
764 /*
765 ** geography_bestsrid(GSERIALIZED *g, GSERIALIZED *g) returns int
766 ** Utility function. Returns negative SRID numbers that match to the
767 ** numbers handled in code by the transform(lwgeom, srid) function.
768 ** UTM, polar stereographic and mercator as fallback. To be used
769 ** in wrapping existing geometry functions in SQL to provide access
770 ** to them in the geography module.
771 */
773 Datum geography_bestsrid(PG_FUNCTION_ARGS)
774 {
775  GBOX gbox, gbox1, gbox2;
776  GSERIALIZED *g1 = NULL;
777  GSERIALIZED *g2 = NULL;
778  int empty1 = LW_FALSE;
779  int empty2 = LW_FALSE;
780  double xwidth, ywidth;
781  POINT2D center;
782 
783  Datum d1 = PG_GETARG_DATUM(0);
784  Datum d2 = PG_GETARG_DATUM(1);
785 
786  /* Get our geometry objects loaded into memory. */
787  g1 = (GSERIALIZED*)PG_DETOAST_DATUM(d1);
788  /* Synchronize our box types */
789  gbox1.flags = g1->flags;
790  /* Calculate if the geometry is empty. */
791  empty1 = gserialized_is_empty(g1);
792  /* Calculate a geocentric bounds for the objects */
793  if ( ! empty1 && gserialized_get_gbox_p(g1, &gbox1) == LW_FAILURE )
794  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g1, &gbox1)");
795 
796  POSTGIS_DEBUGF(4, "calculated gbox = %s", gbox_to_string(&gbox1));
797 
798  /* If we have a unique second argument, fill in all the necessary variables. */
799  if ( d1 != d2 )
800  {
801  g2 = (GSERIALIZED*)PG_DETOAST_DATUM(d2);
802  gbox2.flags = g2->flags;
803  empty2 = gserialized_is_empty(g2);
804  if ( ! empty2 && gserialized_get_gbox_p(g2, &gbox2) == LW_FAILURE )
805  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g2, &gbox2)");
806  }
807  /*
808  ** If no unique second argument, copying the box for the first
809  ** argument will give us the right answer for all subsequent tests.
810  */
811  else
812  {
813  gbox = gbox2 = gbox1;
814  }
815 
816  /* Both empty? We don't have an answer. */
817  if ( empty1 && empty2 )
818  PG_RETURN_NULL();
819 
820  /* One empty? We can use the other argument values as infill. Otherwise merge the boxen */
821  if ( empty1 )
822  gbox = gbox2;
823  else if ( empty2 )
824  gbox = gbox1;
825  else
826  gbox_union(&gbox1, &gbox2, &gbox);
827 
828  gbox_centroid(&gbox, &center);
829 
830  /* Width and height in degrees */
831  xwidth = 180.0 * gbox_angular_width(&gbox) / M_PI;
832  ywidth = 180.0 * gbox_angular_height(&gbox) / M_PI;
833 
834  POSTGIS_DEBUGF(2, "xwidth %g", xwidth);
835  POSTGIS_DEBUGF(2, "ywidth %g", ywidth);
836  POSTGIS_DEBUGF(2, "center POINT(%g %g)", center.x, center.y);
837 
838  /* Are these data arctic? Lambert Azimuthal Equal Area North. */
839  if ( center.y > 70.0 && ywidth < 45.0 )
840  {
841  PG_RETURN_INT32(SRID_NORTH_LAMBERT);
842  }
843 
844  /* Are these data antarctic? Lambert Azimuthal Equal Area South. */
845  if ( center.y < -70.0 && ywidth < 45.0 )
846  {
847  PG_RETURN_INT32(SRID_SOUTH_LAMBERT);
848  }
849 
850  /*
851  ** Can we fit these data into one UTM zone?
852  ** We will assume we can push things as
853  ** far as a half zone past a zone boundary.
854  ** Note we have no handling for the date line in here.
855  */
856  if ( xwidth < 6.0 )
857  {
858  int zone = floor((center.x + 180.0) / 6.0);
859 
860  if ( zone > 59 ) zone = 59;
861 
862  /* Are these data below the equator? UTM South. */
863  if ( center.y < 0.0 )
864  {
865  PG_RETURN_INT32( SRID_SOUTH_UTM_START + zone );
866  }
867  /* Are these data above the equator? UTM North. */
868  else
869  {
870  PG_RETURN_INT32( SRID_NORTH_UTM_START + zone );
871  }
872  }
873 
874  /*
875  ** Can we fit into a custom LAEA area? (30 degrees high, variable width)
876  ** We will allow overlap into adjoining areas, but use a slightly narrower test (25) to try
877  ** and minimize the worst case.
878  ** Again, we are hoping the dateline doesn't trip us up much
879  */
880  if ( ywidth < 25.0 )
881  {
882  int xzone = -1;
883  int yzone = 3 + floor(center.y / 30.0); /* (range of 0-5) */
884 
885  /* Equatorial band, 12 zones, 30 degrees wide */
886  if ( (yzone == 2 || yzone == 3) && xwidth < 30.0 )
887  {
888  xzone = 6 + floor(center.x / 30.0);
889  }
890  /* Temperate band, 8 zones, 45 degrees wide */
891  else if ( (yzone == 1 || yzone == 4) && xwidth < 45.0 )
892  {
893  xzone = 4 + floor(center.x / 45.0);
894  }
895  /* Arctic band, 4 zones, 90 degrees wide */
896  else if ( (yzone == 0 || yzone == 5) && xwidth < 90.0 )
897  {
898  xzone = 2 + floor(center.x / 90.0);
899  }
900 
901  /* Did we fit into an appropriate xzone? */
902  if ( xzone != -1 )
903  {
904  PG_RETURN_INT32(SRID_LAEA_START + 20 * yzone + xzone);
905  }
906  }
907 
908  /*
909  ** Running out of options... fall-back to Mercator
910  ** and hope for the best.
911  */
912  PG_RETURN_INT32(SRID_WORLD_MERCATOR);
913 
914 }
915 
916 /*
917 ** geography_project(GSERIALIZED *g, distance, azimuth)
918 ** returns point of projection given start point,
919 ** azimuth in radians (bearing) and distance in meters
920 */
922 Datum geography_project(PG_FUNCTION_ARGS)
923 {
924  LWGEOM *lwgeom = NULL;
925  LWPOINT *lwp_projected;
926  GSERIALIZED *g = NULL;
927  GSERIALIZED *g_out = NULL;
928  double azimuth = 0.0;
929  double distance;
930  SPHEROID s;
931  uint32_t type;
932 
933  /* Return NULL on NULL distance or geography */
934  if ( PG_NARGS() < 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1) )
935  PG_RETURN_NULL();
936 
937  /* Get our geometry object loaded into memory. */
938  g = PG_GETARG_GSERIALIZED_P(0);
939 
940  /* Only return for points. */
941  type = gserialized_get_type(g);
942  if ( type != POINTTYPE )
943  {
944  elog(ERROR, "ST_Project(geography) is only valid for point inputs");
945  PG_RETURN_NULL();
946  }
947 
948  distance = PG_GETARG_FLOAT8(1); /* Distance in Meters */
949  lwgeom = lwgeom_from_gserialized(g);
950 
951  /* EMPTY things cannot be projected from */
952  if ( lwgeom_is_empty(lwgeom) )
953  {
954  lwgeom_free(lwgeom);
955  elog(ERROR, "ST_Project(geography) cannot project from an empty start point");
956  PG_RETURN_NULL();
957  }
958 
959  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
960  azimuth = PG_GETARG_FLOAT8(2); /* Azimuth in Radians */
961 
962  /* Initialize spheroid */
963  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
964 
965  /* Handle the zero distance case */
966  if( FP_EQUALS(distance, 0.0) )
967  {
968  PG_RETURN_POINTER(g);
969  }
970 
971  /* Calculate the length */
972  lwp_projected = lwgeom_project_spheroid(lwgeom_as_lwpoint(lwgeom), &s, distance, azimuth);
973 
974  /* Something went wrong... */
975  if ( lwp_projected == NULL )
976  {
977  elog(ERROR, "lwgeom_project_spheroid returned null");
978  PG_RETURN_NULL();
979  }
980 
981  /* Clean up, but not all the way to the point arrays */
982  lwgeom_free(lwgeom);
983  g_out = geography_serialize(lwpoint_as_lwgeom(lwp_projected));
984  lwpoint_free(lwp_projected);
985 
986  PG_FREE_IF_COPY(g, 0);
987  PG_RETURN_POINTER(g_out);
988 }
989 
990 
991 /*
992 ** geography_azimuth(GSERIALIZED *g1, GSERIALIZED *g2)
993 ** returns direction between points (north = 0)
994 ** azimuth (bearing) and distance
995 */
997 Datum geography_azimuth(PG_FUNCTION_ARGS)
998 {
999  LWGEOM *lwgeom1 = NULL;
1000  LWGEOM *lwgeom2 = NULL;
1001  GSERIALIZED *g1 = NULL;
1002  GSERIALIZED *g2 = NULL;
1003  double azimuth;
1004  SPHEROID s;
1005  uint32_t type1, type2;
1006 
1007  /* Get our geometry object loaded into memory. */
1008  g1 = PG_GETARG_GSERIALIZED_P(0);
1009  g2 = PG_GETARG_GSERIALIZED_P(1);
1010 
1011  /* Only return for points. */
1012  type1 = gserialized_get_type(g1);
1013  type2 = gserialized_get_type(g2);
1014  if ( type1 != POINTTYPE || type2 != POINTTYPE )
1015  {
1016  elog(ERROR, "ST_Azimuth(geography, geography) is only valid for point inputs");
1017  PG_RETURN_NULL();
1018  }
1019 
1020  lwgeom1 = lwgeom_from_gserialized(g1);
1021  lwgeom2 = lwgeom_from_gserialized(g2);
1022 
1023  /* EMPTY things cannot be used */
1024  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
1025  {
1026  lwgeom_free(lwgeom1);
1027  lwgeom_free(lwgeom2);
1028  elog(ERROR, "ST_Azimuth(geography, geography) cannot work with empty points");
1029  PG_RETURN_NULL();
1030  }
1031 
1032  /* Initialize spheroid */
1033  spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
1034 
1035  /* Calculate the direction */
1036  azimuth = lwgeom_azumith_spheroid(lwgeom_as_lwpoint(lwgeom1), lwgeom_as_lwpoint(lwgeom2), &s);
1037 
1038  /* Clean up */
1039  lwgeom_free(lwgeom1);
1040  lwgeom_free(lwgeom2);
1041 
1042  PG_FREE_IF_COPY(g1, 0);
1043  PG_FREE_IF_COPY(g2, 1);
1044 
1045  /* Return NULL for unknown (same point) azimuth */
1046  if( isnan(azimuth) )
1047  {
1048  PG_RETURN_NULL();
1049  }
1050 
1051  PG_RETURN_FLOAT8(azimuth);
1052 }
1053 
1054 
1055 
1056 /*
1057 ** geography_segmentize(GSERIALIZED *g1, double max_seg_length)
1058 ** returns densified geometry with no segment longer than max
1059 */
1061 Datum geography_segmentize(PG_FUNCTION_ARGS)
1062 {
1063  LWGEOM *lwgeom1 = NULL;
1064  LWGEOM *lwgeom2 = NULL;
1065  GSERIALIZED *g1 = NULL;
1066  GSERIALIZED *g2 = NULL;
1067  double max_seg_length;
1068  uint32_t type1;
1069 
1070  /* Get our geometry object loaded into memory. */
1071  g1 = PG_GETARG_GSERIALIZED_P(0);
1072  type1 = gserialized_get_type(g1);
1073 
1074  /* Convert max_seg_length from metric units to radians */
1075  max_seg_length = PG_GETARG_FLOAT8(1) / WGS84_RADIUS;
1076 
1077  /* We can't densify points or points, reflect them back */
1078  if ( type1 == POINTTYPE || type1 == MULTIPOINTTYPE || gserialized_is_empty(g1) )
1079  PG_RETURN_POINTER(g1);
1080 
1081  /* Deserialize */
1082  lwgeom1 = lwgeom_from_gserialized(g1);
1083 
1084  /* Calculate the densified geometry */
1085  lwgeom2 = lwgeom_segmentize_sphere(lwgeom1, max_seg_length);
1086 
1087  /*
1088  ** Set the geodetic flag so subsequent
1089  ** functions do the right thing.
1090  */
1091  lwgeom_set_geodetic(lwgeom2, true);
1092 
1093  /* Recalculate the boxes after re-setting the geodetic bit */
1094  lwgeom_drop_bbox(lwgeom2);
1095  lwgeom_add_bbox(lwgeom2);
1096 
1097  g2 = geography_serialize(lwgeom2);
1098 
1099  /* Clean up */
1100  lwgeom_free(lwgeom1);
1101  lwgeom_free(lwgeom2);
1102  PG_FREE_IF_COPY(g1, 0);
1103 
1104  PG_RETURN_POINTER(g2);
1105 }
1106 
1107 
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box)
Read the bounding box off a serialization and calculate one if it is not already there.
Definition: g_serialized.c:371
uint32_t gserialized_get_type(const GSERIALIZED *s)
Extract the geometry type from the serialized form (it hides in the anonymous data area...
Definition: g_serialized.c:55
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:2615
void lwgeom_add_bbox_deep(LWGEOM *lwgeom, GBOX *gbox)
Compute a box for geom and all sub-geometries, if not already computed.
Definition: lwgeom.c:611
GBOX * bbox
Definition: liblwgeom.h:382
Datum geography_segmentize(PG_FUNCTION_ARGS)
double lwgeom_azumith_spheroid(const LWPOINT *r, const LWPOINT *s, const SPHEROID *spheroid)
Calculate the bearing between two points on a spheroid.
Definition: lwgeodetic.c:2050
Datum geography_distance(PG_FUNCTION_ARGS)
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
Definition: g_box.c:369
#define WGS84_RADIUS
Definition: liblwgeom.h:116
Datum geography_length(PG_FUNCTION_ARGS)
int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
Calculate covers predicate for two lwgeoms on the sphere.
Definition: lwgeodetic.c:2303
#define POLYGONTYPE
Definition: liblwgeom.h:72
LWPOINT * lwpoint_make2d(int srid, double x, double y)
Definition: lwpoint.c:132
Datum area(PG_FUNCTION_ARGS)
double b
Definition: liblwgeom.h:298
Datum geography_distance_knn(PG_FUNCTION_ARGS)
void lwpoint_free(LWPOINT *pt)
Definition: lwpoint.c:182
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1050
#define INVMINDIST
Datum geography_dwithin_uncached(PG_FUNCTION_ARGS)
#define MULTIPOINTTYPE
Definition: liblwgeom.h:73
double radius
Definition: liblwgeom.h:302
int gbox_centroid(const GBOX *gbox, POINT2D *out)
Computes the average(ish) center of the box and returns success.
Definition: lwgeodetic.c:243
#define FP_LTEQ(A, B)
LWPOINT * lwgeom_project_spheroid(const LWPOINT *r, const SPHEROID *spheroid, double distance, double azimuth)
Calculate the location of a point on a spheroid, give a start point, bearing and distance.
Definition: lwgeodetic.c:1995
void error_if_srid_mismatch(int srid1, int srid2)
Definition: lwutil.c:341
int geography_tree_distance(const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double tolerance, double *distance)
LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwgeom.c:80
double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
Calculate the geodetic length of a lwgeom on the unit sphere.
Definition: lwgeodetic.c:2884
Datum geography_area(PG_FUNCTION_ARGS)
void lwgeom_drop_bbox(LWGEOM *lwgeom)
Call this function to drop BBOX and SRID from LWGEOM.
Definition: lwgeom.c:586
int32_t srid
Definition: liblwgeom.h:383
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: g_serialized.c:139
#define LW_FAILURE
Definition: liblwgeom.h:64
GSERIALIZED * gserialized_expand(GSERIALIZED *g, double distance)
Return a GSERIALIZED with an expanded bounding box.
double x
Definition: liblwgeom.h:312
Datum geography_bestsrid(PG_FUNCTION_ARGS)
double zmax
Definition: liblwgeom.h:281
#define LW_FALSE
Definition: liblwgeom.h:62
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:61
Datum geography_point_outside(PG_FUNCTION_ARGS)
GSERIALIZED * gserialized_from_lwgeom(LWGEOM *geom, int is_geodetic, size_t *size)
Allocate a new GSERIALIZED from an LWGEOM.
Definition: g_serialized.c:906
#define FP_TOLERANCE
Floating point comparators.
double y
Definition: liblwgeom.h:312
char * s
Definition: cu_in_wkt.c:23
Datum distance(PG_FUNCTION_ARGS)
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition: g_box.c:111
double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid)
Calculate the geodetic area of a lwgeom on the sphere.
Definition: lwgeodetic.c:1927
void lwgeom_set_geodetic(LWGEOM *geom, int value)
Set the FLAGS geodetic bit on geometry an all sub-geometries and pointlists.
Definition: lwgeom.c:858
uint8_t flags
Definition: liblwgeom.h:275
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:75
void gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside)
Calculate a spherical point that falls outside the geocentric gbox.
Definition: lwgeodetic.c:1444
double a
Definition: liblwgeom.h:297
int geography_dwithin_cache(FunctionCallInfoData *fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double tolerance, int *dwithin)
#define FP_GTEQ(A, B)
LWGEOM * lwgeom_segmentize_sphere(const LWGEOM *lwg_in, double max_seg_length)
Derive a new geometry with vertices added to ensure no vertex is more than max_seg_length (in radians...
Definition: lwgeodetic.c:1639
#define FALSE
Definition: dbfopen.c:168
Datum geography_expand(PG_FUNCTION_ARGS)
Datum geography_distance_tree(PG_FUNCTION_ARGS)
double zmin
Definition: liblwgeom.h:280
double gbox_angular_height(const GBOX *gbox)
GBOX utility functions to figure out coverage/location on the globe.
Definition: lwgeodetic.c:164
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:70
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:599
int geography_distance_cache(FunctionCallInfoData *fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double *distance)
Datum geography_covers(PG_FUNCTION_ARGS)
Datum geography_azimuth(PG_FUNCTION_ARGS)
uint8_t type
Definition: liblwgeom.h:380
type
Definition: ovdump.py:41
#define FP_EQUALS(A, B)
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:254
Datum geography_distance_uncached(PG_FUNCTION_ARGS)
double lwgeom_area_spheroid(const LWGEOM *lwgeom, const SPHEROID *spheroid)
Calculate the geodetic area of a lwgeom on the spheroid.
Definition: lwspheroid.c:628
double lwgeom_distance_spheroid(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, const SPHEROID *spheroid, double tolerance)
Calculate the geodetic distance from lwgeom1 to lwgeom2 on the spheroid.
Definition: lwgeodetic.c:2081
PG_FUNCTION_INFO_V1(geography_distance_knn)
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
Definition: lwgeom.c:1297
Datum geography_dwithin(PG_FUNCTION_ARGS)
int32_t gserialized_get_srid(const GSERIALIZED *s)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: g_serialized.c:69
#define COLLECTIONTYPE
Definition: liblwgeom.h:76
This library is the generic geometry handling section of PostGIS.
uint8_t flags
Definition: liblwgeom.h:367
Datum geography_perimeter(PG_FUNCTION_ARGS)
Datum geography_project(PG_FUNCTION_ARGS)
double gbox_angular_width(const GBOX *gbox)
Returns the angular width (longitudinal span) of the box in radians.
Definition: lwgeodetic.c:191