PostGIS  3.4.0dev-r@@SVN_REVISION@@
geography_measurement.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) 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22  *
23  **********************************************************************/
24 
25 
26 #include "postgres.h"
27 #include "catalog/pg_type.h" /* for CSTRINGOID */
28 
29 #include "../postgis_config.h"
30 
31 #include <math.h>
32 #include <float.h>
33 #include <string.h>
34 #include <stdio.h>
35 
36 #include "liblwgeom.h" /* For standard geometry types. */
37 #include "liblwgeom_internal.h" /* For FP comparators. */
38 #include "lwgeom_pg.h" /* For debugging macros. */
39 #include "geography.h" /* For utility functions. */
40 #include "geography_measurement_trees.h" /* For circ_tree caching */
41 #include "lwgeom_transform.h" /* For SRID functions */
42 
43 #ifdef PROJ_GEODESIC
44 /* round to 10 nm precision */
45 #define INVMINDIST 1.0e8
46 #else
47 /* round to 100 nm precision */
48 #define INVMINDIST 1.0e7
49 #endif
50 
51 Datum geography_distance(PG_FUNCTION_ARGS);
52 Datum geography_distance_uncached(PG_FUNCTION_ARGS);
53 Datum geography_distance_knn(PG_FUNCTION_ARGS);
54 Datum geography_distance_tree(PG_FUNCTION_ARGS);
55 Datum geography_dwithin(PG_FUNCTION_ARGS);
56 Datum geography_dwithin_uncached(PG_FUNCTION_ARGS);
57 Datum geography_area(PG_FUNCTION_ARGS);
58 Datum geography_length(PG_FUNCTION_ARGS);
59 Datum geography_expand(PG_FUNCTION_ARGS);
60 Datum geography_point_outside(PG_FUNCTION_ARGS);
61 Datum geography_covers(PG_FUNCTION_ARGS);
62 Datum geography_coveredby(PG_FUNCTION_ARGS);
63 Datum geography_bestsrid(PG_FUNCTION_ARGS);
64 Datum geography_perimeter(PG_FUNCTION_ARGS);
65 Datum geography_project(PG_FUNCTION_ARGS);
66 Datum geography_azimuth(PG_FUNCTION_ARGS);
67 Datum geography_segmentize(PG_FUNCTION_ARGS);
68 
69 Datum geography_line_locate_point(PG_FUNCTION_ARGS);
70 Datum geography_line_interpolate_point(PG_FUNCTION_ARGS);
71 Datum geography_line_substring(PG_FUNCTION_ARGS);
72 Datum geography_closestpoint(PG_FUNCTION_ARGS);
73 Datum geography_shortestline(PG_FUNCTION_ARGS);
74 
75 
77 Datum geography_distance_knn(PG_FUNCTION_ARGS)
78 {
79  LWGEOM *lwgeom1 = NULL;
80  LWGEOM *lwgeom2 = NULL;
81  GSERIALIZED *g1 = NULL;
82  GSERIALIZED *g2 = NULL;
83  double distance;
84  double tolerance = FP_TOLERANCE;
85  bool use_spheroid = false; /* must use sphere, can't get index to harmonize with spheroid */
86  SPHEROID s;
87 
88  /* Get our geometry objects loaded into memory. */
89  g1 = PG_GETARG_GSERIALIZED_P(0);
90  g2 = PG_GETARG_GSERIALIZED_P(1);
91 
92  gserialized_error_if_srid_mismatch(g1, g2, __func__);
93 
94  /* Initialize spheroid */
95  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
96 
97  /* Set to sphere if requested */
98  if ( ! use_spheroid )
99  s.a = s.b = s.radius;
100 
101  lwgeom1 = lwgeom_from_gserialized(g1);
102  lwgeom2 = lwgeom_from_gserialized(g2);
103 
104  /* Return NULL on empty arguments. */
105  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
106  {
107  PG_FREE_IF_COPY(g1, 0);
108  PG_FREE_IF_COPY(g2, 1);
109  PG_RETURN_NULL();
110  }
111 
112  /* Make sure we have boxes attached */
113  lwgeom_add_bbox_deep(lwgeom1, NULL);
114  lwgeom_add_bbox_deep(lwgeom2, NULL);
115 
116  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
117 
118  POSTGIS_DEBUGF(2, "[GIST] '%s' got distance %g", __func__, distance);
119 
120  /* Clean up */
121  lwgeom_free(lwgeom1);
122  lwgeom_free(lwgeom2);
123  PG_FREE_IF_COPY(g1, 0);
124  PG_FREE_IF_COPY(g2, 1);
125 
126  /* Something went wrong, negative return... should already be eloged, return NULL */
127  if ( distance < 0.0 )
128  {
129  PG_RETURN_NULL();
130  }
131 
132  PG_RETURN_FLOAT8(distance);
133 }
134 
135 /*
136 ** geography_distance_uncached(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
137 ** returns double distance in meters
138 */
140 Datum geography_distance_uncached(PG_FUNCTION_ARGS)
141 {
142  LWGEOM *lwgeom1 = NULL;
143  LWGEOM *lwgeom2 = NULL;
144  GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
145  GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
146  double distance;
147  double tolerance = FP_TOLERANCE;
148  bool use_spheroid = true;
149  SPHEROID s;
150 
151  /* Read our tolerance value. */
152  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
153  tolerance = PG_GETARG_FLOAT8(2);
154 
155  /* Read our calculation type. */
156  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
157  use_spheroid = PG_GETARG_BOOL(3);
158 
159  gserialized_error_if_srid_mismatch(g1, g2, __func__);
160 
161  /* Initialize spheroid */
162  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
163 
164  /* Set to sphere if requested */
165  if ( ! use_spheroid )
166  s.a = s.b = s.radius;
167 
168  lwgeom1 = lwgeom_from_gserialized(g1);
169  lwgeom2 = lwgeom_from_gserialized(g2);
170 
171  /* Return NULL on empty arguments. */
172  if ( !lwgeom1 || !lwgeom2 || lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
173  {
174  PG_FREE_IF_COPY(g1, 0);
175  PG_FREE_IF_COPY(g2, 1);
176  PG_RETURN_NULL();
177  }
178 
179  /* Make sure we have boxes attached */
180  lwgeom_add_bbox_deep(lwgeom1, NULL);
181  lwgeom_add_bbox_deep(lwgeom2, NULL);
182 
183  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
184 
185  /* Clean up */
186  lwgeom_free(lwgeom1);
187  lwgeom_free(lwgeom2);
188  PG_FREE_IF_COPY(g1, 0);
189  PG_FREE_IF_COPY(g2, 1);
190 
191  /* Something went wrong, negative return... should already be eloged, return NULL */
192  if ( distance < 0.0 )
193  {
194  PG_RETURN_NULL();
195  }
196 
197  PG_RETURN_FLOAT8(distance);
198 }
199 
200 
201 /*
202 ** geography_distance(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
203 ** returns double distance in meters
204 */
206 Datum geography_distance(PG_FUNCTION_ARGS)
207 {
208  SHARED_GSERIALIZED *shared_geom1 = ToastCacheGetGeometry(fcinfo, 0);
209  SHARED_GSERIALIZED *shared_geom2 = ToastCacheGetGeometry(fcinfo, 1);
210  const GSERIALIZED *g1 = shared_gserialized_get(shared_geom1);
211  const GSERIALIZED *g2 = shared_gserialized_get(shared_geom2);
212  double distance;
213  bool use_spheroid = true;
214  SPHEROID s;
215 
216  if (PG_NARGS() > 2)
217  use_spheroid = PG_GETARG_BOOL(2);
218 
219  gserialized_error_if_srid_mismatch(g1, g2, __func__);
220 
221  /* Initialize spheroid */
222  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
223 
224  /* Set to sphere if requested */
225  if ( ! use_spheroid )
226  s.a = s.b = s.radius;
227 
228  /* Return NULL on empty arguments. */
230  {
231  PG_RETURN_NULL();
232  }
233 
234  /* Do the brute force calculation if the cached calculation doesn't tick over */
235  if (LW_FAILURE == geography_distance_cache(fcinfo, shared_geom1, shared_geom2, &s, &distance))
236  {
237  /* default to using tree-based distance calculation at all times */
238  /* in standard distance call. */
240  /*
241  LWGEOM* lwgeom1 = lwgeom_from_gserialized(g1);
242  LWGEOM* lwgeom2 = lwgeom_from_gserialized(g2);
243  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
244  lwgeom_free(lwgeom1);
245  lwgeom_free(lwgeom2);
246  */
247  }
248 
249  /* Knock off any funny business at the nanometer level, ticket #2168 */
250  distance = round(distance * INVMINDIST) / INVMINDIST;
251 
252  /* Something went wrong, negative return... should already be eloged, return NULL */
253  if ( distance < 0.0 )
254  {
255  elog(ERROR, "distance returned negative!");
256  PG_RETURN_NULL();
257  }
258 
259  PG_RETURN_FLOAT8(distance);
260 }
261 
262 /*
263 ** geography_dwithin(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
264 ** returns double distance in meters
265 */
267 Datum geography_dwithin(PG_FUNCTION_ARGS)
268 {
269  SHARED_GSERIALIZED *shared_geom1 = ToastCacheGetGeometry(fcinfo, 0);
270  SHARED_GSERIALIZED *shared_geom2 = ToastCacheGetGeometry(fcinfo, 1);
271  const GSERIALIZED *g1 = shared_gserialized_get(shared_geom1);
272  const GSERIALIZED *g2 = shared_gserialized_get(shared_geom2);
273  SPHEROID s;
274  double tolerance = FP_TOLERANCE;
275  bool use_spheroid = true;
276  double distance;
277  int dwithin = LW_FALSE;
278 
279  gserialized_error_if_srid_mismatch(g1, g2, __func__);
280 
281  /* Read our tolerance value. */
282  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
283  tolerance = PG_GETARG_FLOAT8(2);
284 
285  /* Read our calculation type. */
286  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
287  use_spheroid = PG_GETARG_BOOL(3);
288 
289  /* Initialize spheroid */
290  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
291 
292  /* Set to sphere if requested */
293  if ( ! use_spheroid )
294  s.a = s.b = s.radius;
295 
296  /* Return FALSE on empty arguments. */
298  PG_RETURN_BOOL(false);
299 
300  /* Do the brute force calculation if the cached calculation doesn't tick over */
301  if (LW_FAILURE == geography_dwithin_cache(fcinfo, shared_geom1, shared_geom2, &s, tolerance, &dwithin))
302  {
303  LWGEOM* lwgeom1 = lwgeom_from_gserialized(g1);
304  LWGEOM* lwgeom2 = lwgeom_from_gserialized(g2);
305  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
306  /* Something went wrong... */
307  if ( distance < 0.0 )
308  elog(ERROR, "lwgeom_distance_spheroid returned negative!");
309  dwithin = (distance <= tolerance);
310  lwgeom_free(lwgeom1);
311  lwgeom_free(lwgeom2);
312  }
313 
314  PG_RETURN_BOOL(dwithin);
315 }
316 
318 Datum geography_intersects(PG_FUNCTION_ARGS)
319 {
320  PG_RETURN_BOOL(CallerFInfoFunctionCall2(
321  geography_dwithin, fcinfo->flinfo, InvalidOid,
322  PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)));
323 }
324 
325 /*
326 ** geography_distance_tree(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
327 ** returns double distance in meters
328 */
330 Datum geography_distance_tree(PG_FUNCTION_ARGS)
331 {
332  GSERIALIZED *g1 = NULL;
333  GSERIALIZED *g2 = NULL;
334  double tolerance = 0.0;
335  double distance;
336  bool use_spheroid = true;
337  SPHEROID s;
338 
339  /* Get our geometry objects loaded into memory. */
340  g1 = PG_GETARG_GSERIALIZED_P(0);
341  g2 = PG_GETARG_GSERIALIZED_P(1);
342 
343  gserialized_error_if_srid_mismatch(g1, g2, __func__);
344 
345  /* Return zero on empty arguments. */
347  {
348  PG_FREE_IF_COPY(g1, 0);
349  PG_FREE_IF_COPY(g2, 1);
350  PG_RETURN_FLOAT8(0.0);
351  }
352 
353  /* Read our tolerance value. */
354  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
355  tolerance = PG_GETARG_FLOAT8(2);
356 
357  /* Read our calculation type. */
358  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
359  use_spheroid = PG_GETARG_BOOL(3);
360 
361  /* Initialize spheroid */
362  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
363 
364  /* Set to sphere if requested */
365  if ( ! use_spheroid )
366  s.a = s.b = s.radius;
367 
368  if ( geography_tree_distance(g1, g2, &s, tolerance, &distance) == LW_FAILURE )
369  {
370  elog(ERROR, "geography_distance_tree failed!");
371  PG_RETURN_NULL();
372  }
373  /* Knock off any funny business at the nanometer level, ticket #2168 */
374  distance = round(distance * INVMINDIST) / INVMINDIST;
375 
376  PG_RETURN_FLOAT8(distance);
377 }
378 
379 
380 
381 /*
382 ** geography_dwithin_uncached(GSERIALIZED *g1, GSERIALIZED *g2, double tolerance, boolean use_spheroid)
383 ** returns double distance in meters
384 */
386 Datum geography_dwithin_uncached(PG_FUNCTION_ARGS)
387 {
388  LWGEOM *lwgeom1 = NULL;
389  LWGEOM *lwgeom2 = NULL;
390  GSERIALIZED *g1 = NULL;
391  GSERIALIZED *g2 = NULL;
392  double tolerance = 0.0;
393  double distance;
394  bool use_spheroid = true;
395  SPHEROID s;
396 
397  /* Get our geometry objects loaded into memory. */
398  g1 = PG_GETARG_GSERIALIZED_P(0);
399  g2 = PG_GETARG_GSERIALIZED_P(1);
400  gserialized_error_if_srid_mismatch(g1, g2, __func__);
401 
402  /* Read our tolerance value. */
403  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
404  tolerance = PG_GETARG_FLOAT8(2);
405 
406  /* Read our calculation type. */
407  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
408  use_spheroid = PG_GETARG_BOOL(3);
409 
410  /* Initialize spheroid */
411  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
412 
413  /* Set to sphere if requested */
414  if ( ! use_spheroid )
415  s.a = s.b = s.radius;
416 
417  lwgeom1 = lwgeom_from_gserialized(g1);
418  lwgeom2 = lwgeom_from_gserialized(g2);
419 
420  /* Return FALSE on empty arguments. */
421  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
422  {
423  PG_RETURN_BOOL(false);
424  }
425 
426  distance = lwgeom_distance_spheroid(lwgeom1, lwgeom2, &s, tolerance);
427 
428  /* Clean up */
429  lwgeom_free(lwgeom1);
430  lwgeom_free(lwgeom2);
431  PG_FREE_IF_COPY(g1, 0);
432  PG_FREE_IF_COPY(g2, 1);
433 
434  /* Something went wrong... should already be eloged, return FALSE */
435  if ( distance < 0.0 )
436  {
437  elog(ERROR, "lwgeom_distance_spheroid returned negative!");
438  PG_RETURN_BOOL(false);
439  }
440 
441  PG_RETURN_BOOL(distance <= tolerance);
442 }
443 
444 
445 /*
446 ** geography_expand(GSERIALIZED *g) returns *GSERIALIZED
447 **
448 ** warning, this tricky little function does not expand the
449 ** geometry at all, just re-writes bounding box value to be
450 ** a bit bigger. only useful when passing the result along to
451 ** an index operator (&&)
452 */
454 Datum geography_expand(PG_FUNCTION_ARGS)
455 {
456  GSERIALIZED *g = NULL;
457  GSERIALIZED *g_out = NULL;
458  double unit_distance, distance;
459 
460  /* Get a wholly-owned pointer to the geography */
461  g = PG_GETARG_GSERIALIZED_P_COPY(0);
462 
463  /* Read our distance value and normalize to unit-sphere. */
464  distance = PG_GETARG_FLOAT8(1);
465  /* Magic 1% expansion is to bridge difference between potential */
466  /* spheroidal input distance and fact that expanded box filter is */
467  /* calculated on sphere */
468  unit_distance = 1.01 * distance / WGS84_RADIUS;
469 
470  /* Try the expansion */
471  g_out = gserialized_expand(g, unit_distance);
472 
473  /* If the expansion fails, the return our input */
474  if ( g_out == NULL )
475  {
476  PG_RETURN_POINTER(g);
477  }
478 
479  if ( g_out != g )
480  {
481  pfree(g);
482  }
483 
484  PG_RETURN_POINTER(g_out);
485 }
486 
487 /*
488 ** geography_area(GSERIALIZED *g)
489 ** returns double area in meters square
490 */
492 Datum geography_area(PG_FUNCTION_ARGS)
493 {
494  LWGEOM *lwgeom = NULL;
495  GSERIALIZED *g = NULL;
496  GBOX gbox;
497  double area;
498  bool use_spheroid = LW_TRUE;
499  SPHEROID s;
500 
501  /* Get our geometry object loaded into memory. */
502  g = PG_GETARG_GSERIALIZED_P(0);
503 
504  /* Read our calculation type */
505  use_spheroid = PG_GETARG_BOOL(1);
506 
507  /* Initialize spheroid */
508  spheroid_init_from_srid(gserialized_get_srid(g), &s);
509 
510  lwgeom = lwgeom_from_gserialized(g);
511 
512  /* EMPTY things have no area */
513  if ( lwgeom_is_empty(lwgeom) )
514  {
515  lwgeom_free(lwgeom);
516  PG_RETURN_FLOAT8(0.0);
517  }
518 
519  if ( lwgeom->bbox )
520  gbox = *(lwgeom->bbox);
521  else
522  lwgeom_calculate_gbox_geodetic(lwgeom, &gbox);
523 
524 #ifndef PROJ_GEODESIC
525  /* Test for cases that are currently not handled by spheroid code */
526  if ( use_spheroid )
527  {
528  /* We can't circle the poles right now */
529  if ( FP_GTEQ(gbox.zmax,1.0) || FP_LTEQ(gbox.zmin,-1.0) )
530  use_spheroid = LW_FALSE;
531  /* We can't cross the equator right now */
532  if ( gbox.zmax > 0.0 && gbox.zmin < 0.0 )
533  use_spheroid = LW_FALSE;
534  }
535 #endif /* ifndef PROJ_GEODESIC */
536 
537  /* User requests spherical calculation, turn our spheroid into a sphere */
538  if ( ! use_spheroid )
539  s.a = s.b = s.radius;
540 
541  /* Calculate the area */
542  if ( use_spheroid )
543  area = lwgeom_area_spheroid(lwgeom, &s);
544  else
545  area = lwgeom_area_sphere(lwgeom, &s);
546 
547  /* Clean up */
548  lwgeom_free(lwgeom);
549  PG_FREE_IF_COPY(g, 0);
550 
551  /* Something went wrong... */
552  if ( area < 0.0 )
553  {
554  elog(ERROR, "lwgeom_area_spher(oid) returned area < 0.0");
555  PG_RETURN_NULL();
556  }
557 
558  PG_RETURN_FLOAT8(area);
559 }
560 
561 /*
562 ** geography_perimeter(GSERIALIZED *g)
563 ** returns double perimeter in meters for area features
564 */
566 Datum geography_perimeter(PG_FUNCTION_ARGS)
567 {
568  LWGEOM *lwgeom = NULL;
569  GSERIALIZED *g = NULL;
570  double length;
571  bool use_spheroid = LW_TRUE;
572  SPHEROID s;
573  int type;
574 
575  /* Get our geometry object loaded into memory. */
576  g = PG_GETARG_GSERIALIZED_P(0);
577 
578  /* Only return for area features. */
580  if ( ! (type == POLYGONTYPE || type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE) )
581  {
582  PG_RETURN_FLOAT8(0.0);
583  }
584 
585  lwgeom = lwgeom_from_gserialized(g);
586 
587  /* EMPTY things have no perimeter */
588  if ( lwgeom_is_empty(lwgeom) )
589  {
590  lwgeom_free(lwgeom);
591  PG_RETURN_FLOAT8(0.0);
592  }
593 
594  /* Read our calculation type */
595  use_spheroid = PG_GETARG_BOOL(1);
596 
597  /* Initialize spheroid */
598  spheroid_init_from_srid(gserialized_get_srid(g), &s);
599 
600  /* User requests spherical calculation, turn our spheroid into a sphere */
601  if ( ! use_spheroid )
602  s.a = s.b = s.radius;
603 
604  /* Calculate the length */
605  length = lwgeom_length_spheroid(lwgeom, &s);
606 
607  /* Something went wrong... */
608  if ( length < 0.0 )
609  {
610  elog(ERROR, "lwgeom_length_spheroid returned length < 0.0");
611  PG_RETURN_NULL();
612  }
613 
614  /* Clean up, but not all the way to the point arrays */
615  lwgeom_free(lwgeom);
616 
617  PG_FREE_IF_COPY(g, 0);
618  PG_RETURN_FLOAT8(length);
619 }
620 
621 /*
622 ** geography_length(GSERIALIZED *g)
623 ** returns double length in meters
624 */
626 Datum geography_length(PG_FUNCTION_ARGS)
627 {
628  LWGEOM *lwgeom = NULL;
629  GSERIALIZED *g = NULL;
630  double length;
631  bool use_spheroid = LW_TRUE;
632  SPHEROID s;
633 
634  /* Get our geometry object loaded into memory. */
635  g = PG_GETARG_GSERIALIZED_P(0);
636  lwgeom = lwgeom_from_gserialized(g);
637 
638  /* EMPTY things have no length */
639  if ( lwgeom_is_empty(lwgeom) || lwgeom->type == POLYGONTYPE || lwgeom->type == MULTIPOLYGONTYPE )
640  {
641  lwgeom_free(lwgeom);
642  PG_RETURN_FLOAT8(0.0);
643  }
644 
645  /* Read our calculation type */
646  use_spheroid = PG_GETARG_BOOL(1);
647 
648  /* Initialize spheroid */
649  spheroid_init_from_srid(gserialized_get_srid(g), &s);
650 
651  /* User requests spherical calculation, turn our spheroid into a sphere */
652  if ( ! use_spheroid )
653  s.a = s.b = s.radius;
654 
655  /* Calculate the length */
656  length = lwgeom_length_spheroid(lwgeom, &s);
657 
658  /* Something went wrong... */
659  if ( length < 0.0 )
660  {
661  elog(ERROR, "lwgeom_length_spheroid returned length < 0.0");
662  PG_RETURN_NULL();
663  }
664 
665  /* Clean up */
666  lwgeom_free(lwgeom);
667 
668  PG_FREE_IF_COPY(g, 0);
669  PG_RETURN_FLOAT8(length);
670 }
671 
672 /*
673 ** geography_point_outside(GSERIALIZED *g)
674 ** returns point outside the object
675 */
677 Datum geography_point_outside(PG_FUNCTION_ARGS)
678 {
679  GBOX gbox;
680  LWGEOM *lwpoint = NULL;
681  POINT2D pt;
682 
683  /* We need the bounding box to get an outside point for area algorithm */
684  if (gserialized_datum_get_gbox_p(PG_GETARG_DATUM(0), &gbox) == LW_FAILURE)
685  {
686  POSTGIS_DEBUG(4, "gserialized_datum_get_gbox_p returned LW_FAILURE");
687  elog(ERROR, "Error in gserialized_datum_get_gbox_p calculation.");
688  PG_RETURN_NULL();
689  }
690  POSTGIS_DEBUGF(4, "got gbox %s", gbox_to_string(&gbox));
691 
692  /* Get an exterior point, based on this gbox */
693  gbox_pt_outside(&gbox, &pt);
694 
695  lwpoint = (LWGEOM*) lwpoint_make2d(4326, pt.x, pt.y);
696 
697  PG_RETURN_POINTER(geography_serialize(lwpoint));
698 }
699 
700 /*
701 ** geography_covers(GSERIALIZED *g, GSERIALIZED *g) returns boolean
702 ** Only works for (multi)points and (multi)polygons currently.
703 ** Attempts a simple point-in-polygon test on the polygon and point.
704 ** Current algorithm does not distinguish between points on edge
705 ** and points within.
706 */
708 Datum geography_covers(PG_FUNCTION_ARGS)
709 {
710  LWGEOM *lwgeom1 = NULL;
711  LWGEOM *lwgeom2 = NULL;
712  GSERIALIZED *g1 = NULL;
713  GSERIALIZED *g2 = NULL;
714  int result = LW_FALSE;
715 
716  /* Get our geometry objects loaded into memory. */
717  g1 = PG_GETARG_GSERIALIZED_P(0);
718  g2 = PG_GETARG_GSERIALIZED_P(1);
719  gserialized_error_if_srid_mismatch(g1, g2, __func__);
720 
721  /* Construct our working geometries */
722  lwgeom1 = lwgeom_from_gserialized(g1);
723  lwgeom2 = lwgeom_from_gserialized(g2);
724 
725  /* EMPTY never intersects with another geometry */
726  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
727  {
728  lwgeom_free(lwgeom1);
729  lwgeom_free(lwgeom2);
730  PG_FREE_IF_COPY(g1, 0);
731  PG_FREE_IF_COPY(g2, 1);
732  PG_RETURN_BOOL(false);
733  }
734 
735  /* Calculate answer */
736  result = lwgeom_covers_lwgeom_sphere(lwgeom1, lwgeom2);
737 
738  /* Clean up */
739  lwgeom_free(lwgeom1);
740  lwgeom_free(lwgeom2);
741  PG_FREE_IF_COPY(g1, 0);
742  PG_FREE_IF_COPY(g2, 1);
743 
744  PG_RETURN_BOOL(result);
745 }
746 
748 Datum geography_coveredby(PG_FUNCTION_ARGS)
749 {
750  LWGEOM *lwgeom1 = NULL;
751  LWGEOM *lwgeom2 = NULL;
752  GSERIALIZED *g1 = NULL;
753  GSERIALIZED *g2 = NULL;
754  int result = LW_FALSE;
755 
756  /* Get our geometry objects loaded into memory. */
757  /* Pick them up in reverse order to covers */
758  g1 = PG_GETARG_GSERIALIZED_P(1);
759  g2 = PG_GETARG_GSERIALIZED_P(0);
760  gserialized_error_if_srid_mismatch(g1, g2, __func__);
761 
762  /* Construct our working geometries */
763  lwgeom1 = lwgeom_from_gserialized(g1);
764  lwgeom2 = lwgeom_from_gserialized(g2);
765 
766  /* EMPTY never intersects with another geometry */
767  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
768  {
769  lwgeom_free(lwgeom1);
770  lwgeom_free(lwgeom2);
771  PG_FREE_IF_COPY(g1, 1);
772  PG_FREE_IF_COPY(g2, 0);
773  PG_RETURN_BOOL(false);
774  }
775 
776  /* Calculate answer */
777  result = lwgeom_covers_lwgeom_sphere(lwgeom1, lwgeom2);
778 
779  /* Clean up */
780  lwgeom_free(lwgeom1);
781  lwgeom_free(lwgeom2);
782  PG_FREE_IF_COPY(g1, 1);
783  PG_FREE_IF_COPY(g2, 0);
784 
785  PG_RETURN_BOOL(result);
786 }
787 
788 /*
789 ** geography_bestsrid(GSERIALIZED *g, GSERIALIZED *g) returns int
790 ** Utility function. Returns negative SRID numbers that match to the
791 ** numbers handled in code by the transform(lwgeom, srid) function.
792 ** UTM, polar stereographic and mercator as fallback. To be used
793 ** in wrapping existing geometry functions in SQL to provide access
794 ** to them in the geography module.
795 */
797 Datum geography_bestsrid(PG_FUNCTION_ARGS)
798 {
799  GBOX gbox, gbox1, gbox2;
800  GSERIALIZED *g1 = NULL;
801  GSERIALIZED *g2 = NULL;
802  int empty1 = LW_FALSE;
803  int empty2 = LW_FALSE;
804  double xwidth, ywidth;
805  POINT2D center;
806  LWGEOM *lwgeom;
807 
808  /* Get our geometry objects loaded into memory. */
809  g1 = PG_GETARG_GSERIALIZED_P(0);
810  /* Synchronize our box types */
811  gbox1.flags = gserialized_get_lwflags(g1);
812  /* Calculate if the geometry is empty. */
813  empty1 = gserialized_is_empty(g1);
814 
815  /* Convert g1 to LWGEOM type */
816  lwgeom = lwgeom_from_gserialized(g1);
817 
818  /* Calculate a geocentric bounds for the objects */
819  if ( ! empty1 && gserialized_get_gbox_p(g1, &gbox1) == LW_FAILURE )
820  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g1, &gbox1)");
821 
822  POSTGIS_DEBUGF(4, "calculated gbox = %s", gbox_to_string(&gbox1));
823 
824  if ( !lwgeom_isfinite(lwgeom) ) {
825  elog(ERROR, "Error in geography_bestsrid calling with infinite coordinate geographies");
826  }
827  lwgeom_free(lwgeom);
828 
829  /* If we have a unique second argument, fill in all the necessary variables. */
830  if (PG_NARGS() > 1)
831  {
832  g2 = PG_GETARG_GSERIALIZED_P(1);
833  gbox2.flags = gserialized_get_lwflags(g2);
834  empty2 = gserialized_is_empty(g2);
835  if ( ! empty2 && gserialized_get_gbox_p(g2, &gbox2) == LW_FAILURE )
836  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g2, &gbox2)");
837 
838  /* Convert g2 to LWGEOM type */
839  lwgeom = lwgeom_from_gserialized(g2);
840 
841  if ( !lwgeom_isfinite(lwgeom) ) {
842  elog(ERROR, "Error in geography_bestsrid calling with second arg infinite coordinate geographies");
843  }
844  lwgeom_free(lwgeom);
845  }
846  /*
847  ** If no unique second argument, copying the box for the first
848  ** argument will give us the right answer for all subsequent tests.
849  */
850  else
851  {
852  gbox = gbox2 = gbox1;
853  }
854 
855  /* Both empty? We don't have an answer. */
856  if ( empty1 && empty2 )
857  PG_RETURN_NULL();
858 
859  /* One empty? We can use the other argument values as infill. Otherwise merge the boxen */
860  if ( empty1 )
861  gbox = gbox2;
862  else if ( empty2 )
863  gbox = gbox1;
864  else
865  gbox_union(&gbox1, &gbox2, &gbox);
866 
867  gbox_centroid(&gbox, &center);
868 
869  /* Width and height in degrees */
870  xwidth = 180.0 * gbox_angular_width(&gbox) / M_PI;
871  ywidth = 180.0 * gbox_angular_height(&gbox) / M_PI;
872 
873  POSTGIS_DEBUGF(2, "xwidth %g", xwidth);
874  POSTGIS_DEBUGF(2, "ywidth %g", ywidth);
875  POSTGIS_DEBUGF(2, "center POINT(%g %g)", center.x, center.y);
876 
877  /* Are these data arctic? Lambert Azimuthal Equal Area North. */
878  if ( center.y > 70.0 && ywidth < 45.0 )
879  {
880  PG_RETURN_INT32(SRID_NORTH_LAMBERT);
881  }
882 
883  /* Are these data antarctic? Lambert Azimuthal Equal Area South. */
884  if ( center.y < -70.0 && ywidth < 45.0 )
885  {
886  PG_RETURN_INT32(SRID_SOUTH_LAMBERT);
887  }
888 
889  /*
890  ** Can we fit these data into one UTM zone?
891  ** We will assume we can push things as
892  ** far as a half zone past a zone boundary.
893  ** Note we have no handling for the date line in here.
894  */
895  if ( xwidth < 6.0 )
896  {
897  int zone = floor((center.x + 180.0) / 6.0);
898 
899  if ( zone > 59 ) zone = 59;
900 
901  /* Are these data below the equator? UTM South. */
902  if ( center.y < 0.0 )
903  {
904  PG_RETURN_INT32( SRID_SOUTH_UTM_START + zone );
905  }
906  /* Are these data above the equator? UTM North. */
907  else
908  {
909  PG_RETURN_INT32( SRID_NORTH_UTM_START + zone );
910  }
911  }
912 
913  /*
914  ** Can we fit into a custom LAEA area? (30 degrees high, variable width)
915  ** We will allow overlap into adjoining areas, but use a slightly narrower test (25) to try
916  ** and minimize the worst case.
917  ** Again, we are hoping the dateline doesn't trip us up much
918  */
919  if ( ywidth < 25.0 )
920  {
921  int xzone = -1;
922  int yzone = 3 + floor(center.y / 30.0); /* (range of 0-5) */
923 
924  /* Equatorial band, 12 zones, 30 degrees wide */
925  if ( (yzone == 2 || yzone == 3) && xwidth < 30.0 )
926  {
927  xzone = 6 + floor(center.x / 30.0);
928  }
929  /* Temperate band, 8 zones, 45 degrees wide */
930  else if ( (yzone == 1 || yzone == 4) && xwidth < 45.0 )
931  {
932  xzone = 4 + floor(center.x / 45.0);
933  }
934  /* Arctic band, 4 zones, 90 degrees wide */
935  else if ( (yzone == 0 || yzone == 5) && xwidth < 90.0 )
936  {
937  xzone = 2 + floor(center.x / 90.0);
938  }
939 
940  /* Did we fit into an appropriate xzone? */
941  if ( xzone != -1 )
942  {
943  PG_RETURN_INT32(SRID_LAEA_START + 20 * yzone + xzone);
944  }
945  }
946 
947  /*
948  ** Running out of options... fall-back to Mercator
949  ** and hope for the best.
950  */
951  PG_RETURN_INT32(SRID_WORLD_MERCATOR);
952 
953 }
954 
955 /*
956 ** geography_project(GSERIALIZED *g, distance, azimuth)
957 ** returns point of projection given start point,
958 ** azimuth in radians (bearing) and distance in meters
959 */
961 Datum geography_project(PG_FUNCTION_ARGS)
962 {
963  LWGEOM *lwgeom = NULL;
964  LWPOINT *lwp_projected;
965  GSERIALIZED *g = NULL;
966  GSERIALIZED *g_out = NULL;
967  double azimuth = 0.0;
968  double distance;
969  SPHEROID s;
970  uint32_t type;
971 
972  /* Return NULL on NULL distance or geography */
973  if ( PG_NARGS() < 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1) )
974  PG_RETURN_NULL();
975 
976  /* Get our geometry object loaded into memory. */
977  g = PG_GETARG_GSERIALIZED_P(0);
978 
979  /* Only return for points. */
981  if ( type != POINTTYPE )
982  {
983  elog(ERROR, "ST_Project(geography) is only valid for point inputs");
984  PG_RETURN_NULL();
985  }
986 
987  distance = PG_GETARG_FLOAT8(1); /* Distance in Meters */
988  lwgeom = lwgeom_from_gserialized(g);
989 
990  /* EMPTY things cannot be projected from */
991  if ( lwgeom_is_empty(lwgeom) )
992  {
993  lwgeom_free(lwgeom);
994  elog(ERROR, "ST_Project(geography) cannot project from an empty start point");
995  PG_RETURN_NULL();
996  }
997 
998  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
999  azimuth = PG_GETARG_FLOAT8(2); /* Azimuth in Radians */
1000 
1001  /* Initialize spheroid */
1002  spheroid_init_from_srid(gserialized_get_srid(g), &s);
1003 
1004  /* Handle the zero distance case */
1005  if( FP_EQUALS(distance, 0.0) )
1006  {
1007  PG_RETURN_POINTER(g);
1008  }
1009 
1010  /* Calculate the length */
1011  lwp_projected = lwgeom_project_spheroid(lwgeom_as_lwpoint(lwgeom), &s, distance, azimuth);
1012 
1013  /* Something went wrong... */
1014  if ( lwp_projected == NULL )
1015  {
1016  elog(ERROR, "lwgeom_project_spheroid returned null");
1017  PG_RETURN_NULL();
1018  }
1019 
1020  /* Clean up, but not all the way to the point arrays */
1021  lwgeom_free(lwgeom);
1022  g_out = geography_serialize(lwpoint_as_lwgeom(lwp_projected));
1023  lwpoint_free(lwp_projected);
1024 
1025  PG_FREE_IF_COPY(g, 0);
1026  PG_RETURN_POINTER(g_out);
1027 }
1028 
1029 /*
1030 ** geography_project_geography(geog1, geog2, distance, azimuth)
1031 ** returns point of projection given from/pt points,
1032 ** azimuth in radians (bearing) and distance in meters
1033 */
1035 Datum geography_project_geography(PG_FUNCTION_ARGS)
1036 {
1037  LWGEOM *lwgeom1, *lwgeom2;
1038  LWPOINT *lwp1, *lwp2, *lwp3;
1039  GSERIALIZED *g1, *g2, *g3;
1040  double distance;
1041  SPHEROID s;
1042 
1043  /* Get our geometry object loaded into memory. */
1044  g1 = PG_GETARG_GSERIALIZED_P(0);
1045  g2 = PG_GETARG_GSERIALIZED_P(1);
1046 
1047  if ( gserialized_get_type(g1) != POINTTYPE ||
1049  {
1050  elog(ERROR, "ST_Project(geography) is only valid for point inputs");
1051  PG_RETURN_NULL();
1052  }
1053 
1054  distance = PG_GETARG_FLOAT8(2); /* Distance in meters */
1055 
1056  /* Handle the zero distance case */
1057  if ( FP_EQUALS(distance, 0.0) )
1058  {
1059  PG_RETURN_POINTER(g2);
1060  }
1061 
1062  lwgeom1 = lwgeom_from_gserialized(g1);
1063  lwgeom2 = lwgeom_from_gserialized(g2);
1064 
1065  /* EMPTY things cannot be projected from */
1066  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
1067  {
1068  lwgeom_free(lwgeom1);
1069  lwgeom_free(lwgeom2);
1070  elog(ERROR, "ST_Project(geography) cannot project from an empty point");
1071  PG_RETURN_NULL();
1072  }
1073 
1074  /* Initialize spheroid */
1075  spheroid_init_from_srid(lwgeom_get_srid(lwgeom1), &s);
1076 
1077  /* Calculate the length */
1078  lwp1 = lwgeom_as_lwpoint(lwgeom1);
1079  lwp2 = lwgeom_as_lwpoint(lwgeom2);
1080  lwp3 = lwgeom_project_spheroid_lwpoint(lwp1, lwp2, &s, distance);
1081 
1082  /* Something went wrong... */
1083  if ( lwp3 == NULL )
1084  {
1085  elog(ERROR, "lwgeom_project_spheroid_lwpoint returned null");
1086  PG_RETURN_NULL();
1087  }
1088 
1089  /* Clean up, but not all the way to the point arrays */
1090  lwgeom_free(lwgeom1);
1091  lwgeom_free(lwgeom2);
1092  g3 = geography_serialize(lwpoint_as_lwgeom(lwp3));
1093  lwpoint_free(lwp3);
1094 
1095  PG_FREE_IF_COPY(g1, 0);
1096  PG_FREE_IF_COPY(g2, 1);
1097  PG_RETURN_POINTER(g3);
1098 }
1099 
1100 
1101 /*
1102 ** geography_azimuth(GSERIALIZED *g1, GSERIALIZED *g2)
1103 ** returns direction between points (north = 0)
1104 ** azimuth (bearing) and distance
1105 */
1107 Datum geography_azimuth(PG_FUNCTION_ARGS)
1108 {
1109  GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
1110  GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
1111  LWGEOM *lwgeom1 = NULL;
1112  LWGEOM *lwgeom2 = NULL;
1113  double azimuth;
1114  SPHEROID s;
1115  uint32_t type1, type2;
1116 
1117  /* Only return for points. */
1118  type1 = gserialized_get_type(g1);
1119  type2 = gserialized_get_type(g2);
1120  if ( type1 != POINTTYPE || type2 != POINTTYPE )
1121  {
1122  elog(ERROR, "ST_Azimuth(geography, geography) is only valid for point inputs");
1123  PG_RETURN_NULL();
1124  }
1125 
1126  lwgeom1 = lwgeom_from_gserialized(g1);
1127  lwgeom2 = lwgeom_from_gserialized(g2);
1128 
1129  /* EMPTY things cannot be used */
1130  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
1131  {
1132  lwgeom_free(lwgeom1);
1133  lwgeom_free(lwgeom2);
1134  elog(ERROR, "ST_Azimuth(geography, geography) cannot work with empty points");
1135  PG_RETURN_NULL();
1136  }
1137 
1138  /* Initialize spheroid */
1139  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
1140 
1141  /* Calculate the direction */
1142  azimuth = lwgeom_azumith_spheroid(lwgeom_as_lwpoint(lwgeom1), lwgeom_as_lwpoint(lwgeom2), &s);
1143 
1144  /* Clean up */
1145  lwgeom_free(lwgeom1);
1146  lwgeom_free(lwgeom2);
1147 
1148  PG_FREE_IF_COPY(g1, 0);
1149  PG_FREE_IF_COPY(g2, 1);
1150 
1151  /* Return NULL for unknown (same point) azimuth */
1152  if( !isfinite(azimuth) )
1153  {
1154  PG_RETURN_NULL();
1155  }
1156 
1157  PG_RETURN_FLOAT8(azimuth);
1158 }
1159 
1160 
1161 
1167 Datum geography_segmentize(PG_FUNCTION_ARGS)
1168 {
1169  LWGEOM *lwgeom1 = NULL;
1170  LWGEOM *lwgeom2 = NULL;
1171  GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
1172  GSERIALIZED *g2 = NULL;
1173  double max_seg_length = PG_GETARG_FLOAT8(1) / WGS84_RADIUS;
1174  uint32_t type1 = gserialized_get_type(g1);
1175 
1176  /* We can't densify points or points, reflect them back */
1177  if ( type1 == POINTTYPE || type1 == MULTIPOINTTYPE || gserialized_is_empty(g1) )
1178  PG_RETURN_POINTER(g1);
1179 
1180  /* Deserialize */
1181  lwgeom1 = lwgeom_from_gserialized(g1);
1182 
1183  /* Calculate the densified geometry */
1184  lwgeom2 = lwgeom_segmentize_sphere(lwgeom1, max_seg_length);
1185 
1186  /*
1187  ** Set the geodetic flag so subsequent
1188  ** functions do the right thing.
1189  */
1190  lwgeom_set_geodetic(lwgeom2, true);
1191 
1192  /* Recalculate the boxes after re-setting the geodetic bit */
1193  lwgeom_drop_bbox(lwgeom2);
1194 
1195  /* We are trusting geography_serialize will add a box if needed */
1196  g2 = geography_serialize(lwgeom2);
1197 
1198  /* Clean up */
1199  lwgeom_free(lwgeom1);
1200  lwgeom_free(lwgeom2);
1201  PG_FREE_IF_COPY(g1, 0);
1202 
1203  PG_RETURN_POINTER(g2);
1204 }
1205 
1206 
1207 /********************************************************************************/
1208 
1214 Datum geography_line_substring(PG_FUNCTION_ARGS)
1215 {
1216  GSERIALIZED *gs = PG_GETARG_GSERIALIZED_P(0);
1217  double from_fraction = PG_GETARG_FLOAT8(1);
1218  double to_fraction = PG_GETARG_FLOAT8(2);
1219  LWLINE *lwline;
1220  LWGEOM *lwresult;
1221  SPHEROID s;
1223  bool use_spheroid = true;
1224 
1225  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
1226  use_spheroid = PG_GETARG_BOOL(3);
1227 
1228  /* Return NULL on empty argument. */
1229  if ( gserialized_is_empty(gs) )
1230  {
1231  PG_FREE_IF_COPY(gs, 0);
1232  PG_RETURN_NULL();
1233  }
1234 
1235  if ( from_fraction < 0 || from_fraction > 1 )
1236  {
1237  elog(ERROR,"%s: second argument is not within [0,1]", __func__);
1238  PG_FREE_IF_COPY(gs, 0);
1239  PG_RETURN_NULL();
1240  }
1241  if ( to_fraction < 0 || to_fraction > 1 )
1242  {
1243  elog(ERROR,"%s: argument arg is not within [0,1]", __func__);
1244  PG_FREE_IF_COPY(gs, 0);
1245  PG_RETURN_NULL();
1246  }
1247  if ( from_fraction > to_fraction )
1248  {
1249  elog(ERROR, "%s: second argument must be smaller than third argument", __func__);
1250  PG_RETURN_NULL();
1251  }
1252 
1254  if ( !lwline )
1255  {
1256  elog(ERROR,"%s: first argument is not a line", __func__);
1257  PG_FREE_IF_COPY(gs, 0);
1258  PG_RETURN_NULL();
1259  }
1260 
1261  /* Initialize spheroid */
1262  spheroid_init_from_srid(gserialized_get_srid(gs), &s);
1263  /* Set to sphere if requested */
1264  if ( ! use_spheroid )
1265  s.a = s.b = s.radius;
1266 
1267  lwresult = geography_substring(lwline, &s,
1268  from_fraction, to_fraction, FP_TOLERANCE);
1269 
1270  lwline_free(lwline);
1271  PG_FREE_IF_COPY(gs, 0);
1272  lwgeom_set_geodetic(lwresult, true);
1273  result = geography_serialize(lwresult);
1274  lwgeom_free(lwresult);
1275 
1276  PG_RETURN_POINTER(result);
1277 }
1278 
1279 
1289 Datum geography_line_interpolate_point(PG_FUNCTION_ARGS)
1290 {
1291  GSERIALIZED *gs = PG_GETARG_GSERIALIZED_P(0);
1292  double distance_fraction = PG_GETARG_FLOAT8(1);
1293  /* Read calculation type */
1294  bool use_spheroid = PG_GETARG_BOOL(2);
1295  /* Read repeat mode */
1296  bool repeat = (PG_NARGS() > 3) && PG_GETARG_BOOL(3);
1297  LWLINE* lwline;
1298  LWGEOM* lwresult;
1299  SPHEROID s;
1301 
1302  /* Return NULL on empty argument. */
1303  if ( gserialized_is_empty(gs) )
1304  {
1305  PG_FREE_IF_COPY(gs, 0);
1306  PG_RETURN_NULL();
1307  }
1308 
1309  if ( distance_fraction < 0 || distance_fraction > 1 )
1310  {
1311  elog(ERROR,"%s: second arg is not within [0,1]", __func__);
1312  PG_FREE_IF_COPY(gs, 0);
1313  PG_RETURN_NULL();
1314  }
1315 
1317  if ( !lwline )
1318  {
1319  elog(ERROR,"%s: first arg is not a line", __func__);
1320  PG_FREE_IF_COPY(gs, 0);
1321  PG_RETURN_NULL();
1322  }
1323 
1324  /* Initialize spheroid */
1325  spheroid_init_from_srid(gserialized_get_srid(gs), &s);
1326 
1327  /* Set to sphere if requested */
1328  if ( ! use_spheroid )
1329  s.a = s.b = s.radius;
1330 
1331  lwresult = geography_interpolate_points(lwline, distance_fraction, &s, repeat);
1332 
1333  lwgeom_free(lwline_as_lwgeom(lwline));
1334  PG_FREE_IF_COPY(gs, 0);
1335 
1336  lwgeom_set_geodetic(lwresult, true);
1337  result = geography_serialize(lwresult);
1338  lwgeom_free(lwresult);
1339 
1340  PG_RETURN_POINTER(result);
1341 }
1342 
1343 
1349 Datum geography_line_locate_point(PG_FUNCTION_ARGS)
1350 {
1351  GSERIALIZED *gs1 = PG_GETARG_GSERIALIZED_P(0);
1352  GSERIALIZED *gs2 = PG_GETARG_GSERIALIZED_P(1);
1353  bool use_spheroid = PG_GETARG_BOOL(2);
1354  double tolerance = FP_TOLERANCE;
1355  SPHEROID s;
1356  LWLINE *lwline;
1357  LWPOINT *lwpoint;
1358  POINTARRAY *pa;
1359  POINT4D p, p_proj;
1360  double ret;
1361 
1362  gserialized_error_if_srid_mismatch(gs1, gs2, __func__);
1363 
1364  /* Return NULL on empty argument. */
1365  if ( gserialized_is_empty(gs1) || gserialized_is_empty(gs2))
1366  {
1367  PG_FREE_IF_COPY(gs1, 0);
1368  PG_FREE_IF_COPY(gs2, 1);
1369  PG_RETURN_NULL();
1370  }
1371 
1372  if ( gserialized_get_type(gs1) != LINETYPE )
1373  {
1374  elog(ERROR,"%s: 1st arg is not a line", __func__);
1375  PG_RETURN_NULL();
1376  }
1377  if ( gserialized_get_type(gs2) != POINTTYPE )
1378  {
1379  elog(ERROR,"%s: 2st arg is not a point", __func__);
1380  PG_RETURN_NULL();
1381  }
1382 
1383  /* Set to sphere if requested */
1384  if ( ! use_spheroid ) {
1385  s.a = s.b = s.radius;
1386  }
1387  else {
1388  /* Initialize spheroid */
1389  spheroid_init_from_srid(gserialized_get_srid(gs1), &s);
1390  }
1391 
1394 
1395  pa = lwline->points;
1396  lwpoint_getPoint4d_p(lwpoint, &p);
1397 
1398  ret = ptarray_locate_point_spheroid(pa, &p, &s, tolerance, NULL, &p_proj);
1399 
1400  PG_RETURN_FLOAT8(ret);
1401 }
1402 
1403 
1410 Datum geography_closestpoint(PG_FUNCTION_ARGS)
1411 {
1412  GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
1413  GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
1414  LWGEOM *point, *lwg1, *lwg2;
1416 
1417  gserialized_error_if_srid_mismatch(g1, g2, __func__);
1418 
1419  lwg1 = lwgeom_from_gserialized(g1);
1420  lwg2 = lwgeom_from_gserialized(g2);
1421 
1422  /* Return NULL on empty/bad arguments. */
1423  if ( !lwg1 || !lwg2 || lwgeom_is_empty(lwg1) || lwgeom_is_empty(lwg2) )
1424  {
1425  PG_FREE_IF_COPY(g1, 0);
1426  PG_FREE_IF_COPY(g2, 1);
1427  PG_RETURN_NULL();
1428  }
1429 
1430  point = geography_tree_closestpoint(lwg1, lwg2, FP_TOLERANCE);
1431  result = geography_serialize(point);
1432  lwgeom_free(point);
1433  lwgeom_free(lwg1);
1434  lwgeom_free(lwg2);
1435 
1436  PG_FREE_IF_COPY(g1, 0);
1437  PG_FREE_IF_COPY(g2, 1);
1438  PG_RETURN_POINTER(result);
1439 }
1440 
1446 Datum geography_shortestline(PG_FUNCTION_ARGS)
1447 {
1448  GSERIALIZED* g1 = PG_GETARG_GSERIALIZED_P(0);
1449  GSERIALIZED* g2 = PG_GETARG_GSERIALIZED_P(1);
1450  bool use_spheroid = PG_GETARG_BOOL(2);
1451  LWGEOM *line, *lwgeom1, *lwgeom2;
1453  SPHEROID s;
1454 
1455  gserialized_error_if_srid_mismatch(g1, g2, __func__);
1456 
1457  lwgeom1 = lwgeom_from_gserialized(g1);
1458  lwgeom2 = lwgeom_from_gserialized(g2);
1459 
1460  /* Return NULL on empty/bad arguments. */
1461  if ( !lwgeom1 || !lwgeom2 || lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
1462  {
1463  PG_FREE_IF_COPY(g1, 0);
1464  PG_FREE_IF_COPY(g2, 1);
1465  PG_RETURN_NULL();
1466  }
1467 
1468  /* Initialize spheroid */
1469  spheroid_init_from_srid(gserialized_get_srid(g1), &s);
1470 
1471  /* Set to sphere if requested */
1472  if ( ! use_spheroid )
1473  s.a = s.b = s.radius;
1474 
1475  line = geography_tree_shortestline(lwgeom1, lwgeom2, FP_TOLERANCE, &s);
1476 
1477  if (lwgeom_is_empty(line))
1478  PG_RETURN_NULL();
1479 
1480  result = geography_serialize(line);
1481  lwgeom_free(line);
1482 
1483  PG_FREE_IF_COPY(g1, 0);
1484  PG_FREE_IF_COPY(g2, 1);
1485  PG_RETURN_POINTER(result);
1486 }
char * s
Definition: cu_in_wkt.c:23
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition: gbox.c:135
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
Definition: gbox.c:392
GSERIALIZED * gserialized_expand(GSERIALIZED *g, double distance)
Return a GSERIALIZED with an expanded bounding box.
Datum geography_length(PG_FUNCTION_ARGS)
Datum geography_area(PG_FUNCTION_ARGS)
Datum geography_expand(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(geography_distance_knn)
Datum geography_azimuth(PG_FUNCTION_ARGS)
Datum geography_line_substring(PG_FUNCTION_ARGS)
Datum geography_line_locate_point(PG_FUNCTION_ARGS)
Datum geography_shortestline(PG_FUNCTION_ARGS)
Datum geography_closestpoint(PG_FUNCTION_ARGS)
Datum geography_distance_tree(PG_FUNCTION_ARGS)
Datum geography_project_geography(PG_FUNCTION_ARGS)
Datum geography_point_outside(PG_FUNCTION_ARGS)
Datum geography_distance_uncached(PG_FUNCTION_ARGS)
Datum geography_distance(PG_FUNCTION_ARGS)
Datum geography_segmentize(PG_FUNCTION_ARGS)
Datum geography_covers(PG_FUNCTION_ARGS)
Datum geography_intersects(PG_FUNCTION_ARGS)
Datum geography_dwithin(PG_FUNCTION_ARGS)
Datum geography_coveredby(PG_FUNCTION_ARGS)
Datum geography_perimeter(PG_FUNCTION_ARGS)
Datum geography_bestsrid(PG_FUNCTION_ARGS)
Datum geography_dwithin_uncached(PG_FUNCTION_ARGS)
Datum geography_project(PG_FUNCTION_ARGS)
#define INVMINDIST
Datum geography_line_interpolate_point(PG_FUNCTION_ARGS)
Datum geography_distance_knn(PG_FUNCTION_ARGS)
int geography_tree_distance(const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double tolerance, double *distance)
int geography_dwithin_cache(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1, SHARED_GSERIALIZED *g2, const SPHEROID *s, double tolerance, int *dwithin)
int geography_distance_cache(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1, SHARED_GSERIALIZED *g2, const SPHEROID *s, double *distance)
void gserialized_error_if_srid_mismatch(const GSERIALIZED *g1, const GSERIALIZED *g2, const char *funcname)
Definition: gserialized.c:403
int32_t gserialized_get_srid(const GSERIALIZED *g)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: gserialized.c:126
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or calculate it if necessary.
Definition: gserialized.c:65
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:239
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:152
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:89
lwflags_t gserialized_get_lwflags(const GSERIALIZED *g)
Read the flags from a GSERIALIZED and return a standard lwflag integer.
Definition: gserialized.c:18
int gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
Given a GSERIALIZED datum, as quickly as possible (peaking into the top of the memory) return the gbo...
int lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out)
Definition: lwpoint.c:57
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
void lwgeom_set_geodetic(LWGEOM *geom, int value)
Set the FLAGS geodetic bit on geometry an all sub-geometries and pointlists.
Definition: lwgeom.c:964
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
#define LW_FALSE
Definition: liblwgeom.h:94
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:927
double ptarray_locate_point_spheroid(const POINTARRAY *pa, const POINT4D *p4d, const SPHEROID *s, double tolerance, double *mindistout, POINT4D *proj4d)
Locate a point along the point array defining a geographic line.
LWGEOM * geography_interpolate_points(const LWLINE *line, double length_fraction, const SPHEROID *s, char repeat)
Interpolate a point along a geographic line.
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:2105
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition: lwpoint.c:163
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:1749
void lwpoint_free(LWPOINT *pt)
Definition: lwpoint.c:213
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:2204
#define LW_FAILURE
Definition: liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside)
Calculate a spherical point that falls outside the geocentric gbox.
Definition: lwgeodetic.c:1558
#define LINETYPE
Definition: liblwgeom.h:103
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:3027
#define WGS84_RADIUS
Definition: liblwgeom.h:148
double lwgeom_area_spheroid(const LWGEOM *lwgeom, const SPHEROID *spheroid)
Calculate the geodetic area of a lwgeom on the spheroid.
Definition: lwspheroid.c:647
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
Calculate covers predicate for two lwgeoms on the sphere.
Definition: lwgeodetic.c:2430
LWGEOM * geography_substring(const LWLINE *line, const SPHEROID *s, double from, double to, double tolerance)
Return the part of a line between two fractional locations.
LWPOINT * lwgeom_project_spheroid_lwpoint(const LWPOINT *from, const LWPOINT *to, const SPHEROID *spheroid, double distance)
Calculate the location of a point on a spheroid, give a start point, end point and distance.
Definition: lwgeodetic.c:2154
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
void lwgeom_drop_bbox(LWGEOM *lwgeom)
Call this function to drop BBOX and SRID from LWGEOM.
Definition: lwgeom.c:682
int lwgeom_isfinite(const LWGEOM *lwgeom)
Check if a LWGEOM has any non-finite (NaN or Inf) coordinates.
Definition: lwgeom.c:2681
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
#define POLYGONTYPE
Definition: liblwgeom.h:104
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:714
double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
Calculate the geodetic length of a lwgeom on the unit sphere.
Definition: lwgeodetic.c:3296
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
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:2170
void lwline_free(LWLINE *line)
Definition: lwline.c:67
double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid)
Calculate the geodetic area of a lwgeom on the sphere.
Definition: lwgeodetic.c:2037
This library is the generic geometry handling section of PostGIS.
#define FP_GTEQ(A, B)
double gbox_angular_height(const GBOX *gbox)
GBOX utility functions to figure out coverage/location on the globe.
Definition: lwgeodetic.c:188
double gbox_angular_width(const GBOX *gbox)
Returns the angular width (longitudinal span) of the box in radians.
Definition: lwgeodetic.c:215
#define FP_TOLERANCE
Floating point comparators.
#define FP_EQUALS(A, B)
int gbox_centroid(const GBOX *gbox, POINT2D *out)
Computes the average(ish) center of the box and returns success.
Definition: lwgeodetic.c:267
#define FP_LTEQ(A, B)
LWGEOM * geography_tree_closestpoint(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, double threshold)
LWGEOM * geography_tree_shortestline(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, double threshold, const SPHEROID *spheroid)
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:203
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:131
static double distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
type
Definition: ovdump.py:42
double zmax
Definition: liblwgeom.h:359
double zmin
Definition: liblwgeom.h:358
lwflags_t flags
Definition: liblwgeom.h:353
uint8_t type
Definition: liblwgeom.h:462
GBOX * bbox
Definition: liblwgeom.h:458
POINTARRAY * points
Definition: liblwgeom.h:483
double y
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:390