PostGIS  3.7.0dev-r@@SVN_REVISION@@
lwgeodetic.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 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22  * Copyright 2009 David Skea <David.Skea@gov.bc.ca>
23  *
24  **********************************************************************/
25 
26 
27 #include "liblwgeom_internal.h"
28 #include "lwgeodetic.h"
29 #include "lwgeom_log.h"
30 
37 
41 static int
42 point3d_equals(const POINT3D *p1, const POINT3D *p2)
43 {
44  return FP_EQUALS(p1->x, p2->x) && FP_EQUALS(p1->y, p2->y) && FP_EQUALS(p1->z, p2->z);
45 }
46 
50 double longitude_radians_normalize(double lon)
51 {
52  if ( lon == -1.0 * M_PI )
53  return M_PI;
54  if ( lon == -2.0 * M_PI )
55  return 0.0;
56 
57  if ( lon > 2.0 * M_PI )
58  lon = remainder(lon, 2.0 * M_PI);
59 
60  if ( lon < -2.0 * M_PI )
61  lon = remainder(lon, -2.0 * M_PI);
62 
63  if ( lon > M_PI )
64  lon = -2.0 * M_PI + lon;
65 
66  if ( lon < -1.0 * M_PI )
67  lon = 2.0 * M_PI + lon;
68 
69  if ( lon == -2.0 * M_PI )
70  lon *= -1.0;
71 
72  return lon;
73 }
74 
78 double latitude_radians_normalize(double lat)
79 {
80 
81  if ( lat > 2.0 * M_PI )
82  lat = remainder(lat, 2.0 * M_PI);
83 
84  if ( lat < -2.0 * M_PI )
85  lat = remainder(lat, -2.0 * M_PI);
86 
87  if ( lat > M_PI )
88  lat = M_PI - lat;
89 
90  if ( lat < -1.0 * M_PI )
91  lat = -1.0 * M_PI - lat;
92 
93  if ( lat > M_PI_2 )
94  lat = M_PI - lat;
95 
96  if ( lat < -1.0 * M_PI_2 )
97  lat = -1.0 * M_PI - lat;
98 
99  return lat;
100 }
101 
106 double longitude_degrees_normalize(double lon)
107 {
108  if ( lon > 360.0 )
109  lon = remainder(lon, 360.0);
110 
111  if ( lon < -360.0 )
112  lon = remainder(lon, -360.0);
113 
114  if ( lon > 180.0 )
115  lon = -360.0 + lon;
116 
117  if ( lon < -180.0 )
118  lon = 360 + lon;
119 
120  if ( lon == -180.0 )
121  return 180.0;
122 
123  if ( lon == -360.0 )
124  return 0.0;
125 
126  return lon;
127 }
128 
133 double latitude_degrees_normalize(double lat)
134 {
135 
136  if ( lat > 360.0 )
137  lat = remainder(lat, 360.0);
138 
139  if ( lat < -360.0 )
140  lat = remainder(lat, -360.0);
141 
142  if ( lat > 180.0 )
143  lat = 180.0 - lat;
144 
145  if ( lat < -180.0 )
146  lat = -180.0 - lat;
147 
148  if ( lat > 90.0 )
149  lat = 180.0 - lat;
150 
151  if ( lat < -90.0 )
152  lat = -180.0 - lat;
153 
154  return lat;
155 }
156 
160 void point_shift(GEOGRAPHIC_POINT *p, double shift)
161 {
162  double lon = p->lon + shift;
163  if ( lon > M_PI )
164  p->lon = -1.0 * M_PI + (lon - M_PI);
165  else
166  p->lon = lon;
167  return;
168 }
169 
171 {
172  return FP_EQUALS(g1->lat, g2->lat) && FP_EQUALS(g1->lon, g2->lon);
173 }
174 
180 void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
181 {
184 }
185 
187 double
189 {
190  double d[6];
191  int i;
192  double zmin = FLT_MAX;
193  double zmax = -1 * FLT_MAX;
194  POINT3D pt;
195 
196  /* Take a copy of the box corners so we can treat them as a list */
197  /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */
198  memcpy(d, &(gbox->xmin), 6*sizeof(double));
199 
200  /* Generate all 8 corner vectors of the box */
201  for ( i = 0; i < 8; i++ )
202  {
203  pt.x = d[i / 4];
204  pt.y = d[2 + (i % 4) / 2];
205  pt.z = d[4 + (i % 2)];
206  normalize(&pt);
207  if ( pt.z < zmin ) zmin = pt.z;
208  if ( pt.z > zmax ) zmax = pt.z;
209  }
210  return asin(zmax) - asin(zmin);
211 }
212 
214 double
216 {
217  double d[6];
218  int i, j;
219  POINT3D pt[3];
220  double maxangle;
221  double magnitude;
222 
223  /* Take a copy of the box corners so we can treat them as a list */
224  /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */
225  memcpy(d, &(gbox->xmin), 6*sizeof(double));
226 
227  /* Start with the bottom corner */
228  pt[0].x = gbox->xmin;
229  pt[0].y = gbox->ymin;
230  magnitude = sqrt(pt[0].x*pt[0].x + pt[0].y*pt[0].y);
231  pt[0].x /= magnitude;
232  pt[0].y /= magnitude;
233 
234  /* Generate all 8 corner vectors of the box */
235  /* Find the vector furthest from our seed vector */
236  for ( j = 0; j < 2; j++ )
237  {
238  maxangle = -1 * FLT_MAX;
239  for ( i = 0; i < 4; i++ )
240  {
241  double angle, dotprod;
242  POINT3D pt_n;
243 
244  pt_n.x = d[i / 2];
245  pt_n.y = d[2 + (i % 2)];
246  magnitude = sqrt(pt_n.x*pt_n.x + pt_n.y*pt_n.y);
247  pt_n.x /= magnitude;
248  pt_n.y /= magnitude;
249  pt_n.z = 0.0;
250 
251  dotprod = pt_n.x*pt[j].x + pt_n.y*pt[j].y;
252  angle = acos(dotprod > 1.0 ? 1.0 : dotprod);
253  if ( angle > maxangle )
254  {
255  pt[j+1] = pt_n;
256  maxangle = angle;
257  }
258  }
259  }
260 
261  /* Return the distance between the two furthest vectors */
262  return maxangle;
263 }
264 
266 int
267 gbox_centroid(const GBOX* gbox, POINT2D* out)
268 {
269  double d[6];
271  POINT3D pt;
272  int i;
273 
274  /* Take a copy of the box corners so we can treat them as a list */
275  /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */
276  memcpy(d, &(gbox->xmin), 6*sizeof(double));
277 
278  /* Zero out our return vector */
279  pt.x = pt.y = pt.z = 0.0;
280 
281  for ( i = 0; i < 8; i++ )
282  {
283  POINT3D pt_n;
284 
285  pt_n.x = d[i / 4];
286  pt_n.y = d[2 + ((i % 4) / 2)];
287  pt_n.z = d[4 + (i % 2)];
288  normalize(&pt_n);
289 
290  pt.x += pt_n.x;
291  pt.y += pt_n.y;
292  pt.z += pt_n.z;
293  }
294 
295  pt.x /= 8.0;
296  pt.y /= 8.0;
297  pt.z /= 8.0;
298  normalize(&pt);
299 
300  cart2geog(&pt, &g);
303 
304  return LW_SUCCESS;
305 }
306 
316 static int gbox_check_poles(GBOX *gbox)
317 {
318  int rv = LW_FALSE;
319 #if POSTGIS_DEBUG_LEVEL >= 4
320  char *gbox_str = gbox_to_string(gbox);
321  LWDEBUG(4, "checking poles");
322  LWDEBUGF(4, "gbox %s", gbox_str);
323  lwfree(gbox_str);
324 #endif
325  /* Z axis */
326  if (gbox->xmin < 0.0 && gbox->xmax > 0.0 &&
327  gbox->ymin < 0.0 && gbox->ymax > 0.0)
328  {
329  /* Extrema lean positive */
330  if ((gbox->zmin > 0.0) && (gbox->zmax > 0.0))
331  {
332  LWDEBUG(4, "enclosed positive z axis");
333  gbox->zmax = 1.0;
334  }
335  /* Extrema lean negative */
336  else if ((gbox->zmin < 0.0) && (gbox->zmax < 0.0))
337  {
338  LWDEBUG(4, "enclosed negative z axis");
339  gbox->zmin = -1.0;
340  }
341  /* Extrema both sides! */
342  else
343  {
344  LWDEBUG(4, "enclosed both z axes");
345  gbox->zmin = -1.0;
346  gbox->zmax = 1.0;
347  }
348  rv = LW_TRUE;
349  }
350 
351  /* Y axis */
352  if (gbox->xmin < 0.0 && gbox->xmax > 0.0 &&
353  gbox->zmin < 0.0 && gbox->zmax > 0.0)
354  {
355  if ((gbox->ymin > 0.0) && (gbox->ymax > 0.0))
356  {
357  LWDEBUG(4, "enclosed positive y axis");
358  gbox->ymax = 1.0;
359  }
360  else if ((gbox->ymin < 0.0) && (gbox->ymax < 0.0))
361  {
362  LWDEBUG(4, "enclosed negative y axis");
363  gbox->ymin = -1.0;
364  }
365  else
366  {
367  LWDEBUG(4, "enclosed both y axes");
368  gbox->ymax = 1.0;
369  gbox->ymin = -1.0;
370  }
371  rv = LW_TRUE;
372  }
373 
374  /* X axis */
375  if (gbox->ymin < 0.0 && gbox->ymax > 0.0 &&
376  gbox->zmin < 0.0 && gbox->zmax > 0.0)
377  {
378  if ((gbox->xmin > 0.0) && (gbox->xmax > 0.0))
379  {
380  LWDEBUG(4, "enclosed positive x axis");
381  gbox->xmax = 1.0;
382  }
383  else if ((gbox->xmin < 0.0) && (gbox->xmax < 0.0))
384  {
385  LWDEBUG(4, "enclosed negative x axis");
386  gbox->xmin = -1.0;
387  }
388  else
389  {
390  LWDEBUG(4, "enclosed both x axes");
391  gbox->xmax = 1.0;
392  gbox->xmin = -1.0;
393  }
394 
395  rv = LW_TRUE;
396  }
397 
398  return rv;
399 }
400 
405 {
406  p->x = cos(g->lat) * cos(g->lon);
407  p->y = cos(g->lat) * sin(g->lon);
408  p->z = sin(g->lat);
409 }
410 
415 {
416  g->lon = atan2(p->y, p->x);
417  g->lat = asin(p->z);
418 }
419 
423 void ll2cart(const POINT2D *g, POINT3D *p)
424 {
425  double x_rad = M_PI * g->x / 180.0;
426  double y_rad = M_PI * g->y / 180.0;
427  double cos_y_rad = cos(y_rad);
428  p->x = cos_y_rad * cos(x_rad);
429  p->y = cos_y_rad * sin(x_rad);
430  p->z = sin(y_rad);
431 }
432 
446 static double dot_product(const POINT3D *p1, const POINT3D *p2)
447 {
448  return (p1->x*p2->x) + (p1->y*p2->y) + (p1->z*p2->z);
449 }
450 
454 static void cross_product(const POINT3D *a, const POINT3D *b, POINT3D *n)
455 {
456  n->x = a->y * b->z - a->z * b->y;
457  n->y = a->z * b->x - a->x * b->z;
458  n->z = a->x * b->y - a->y * b->x;
459  return;
460 }
461 
465 void vector_sum(const POINT3D *a, const POINT3D *b, POINT3D *n)
466 {
467  n->x = a->x + b->x;
468  n->y = a->y + b->y;
469  n->z = a->z + b->z;
470  return;
471 }
472 
476 static void vector_difference(const POINT3D *a, const POINT3D *b, POINT3D *n)
477 {
478  n->x = a->x - b->x;
479  n->y = a->y - b->y;
480  n->z = a->z - b->z;
481  return;
482 }
483 
487 void vector_scale(POINT3D *n, double scale)
488 {
489  n->x *= scale;
490  n->y *= scale;
491  n->z *= scale;
492  return;
493 }
494 
495 /*
496 * static inline double vector_magnitude(const POINT3D* v)
497 * {
498 * return sqrt(v->x*v->x + v->y*v->y + v->z*v->z);
499 * }
500 */
501 
505 double vector_angle(const POINT3D* v1, const POINT3D* v2)
506 {
507  POINT3D v3, normal;
508  double angle, x, y;
509 
510  cross_product(v1, v2, &normal);
511  normalize(&normal);
512  cross_product(&normal, v1, &v3);
513 
514  x = dot_product(v1, v2);
515  y = dot_product(v2, &v3);
516 
517  angle = atan2(y, x);
518  return angle;
519 }
520 
524 static void normalize2d(POINT2D *p)
525 {
526  double d = sqrt(p->x*p->x + p->y*p->y);
527  if (FP_IS_ZERO(d))
528  {
529  p->x = p->y = 0.0;
530  return;
531  }
532  p->x = p->x / d;
533  p->y = p->y / d;
534  return;
535 }
536 
541 void unit_normal(const POINT3D *P1, const POINT3D *P2, POINT3D *normal)
542 {
543  double p_dot = dot_product(P1, P2);
544  POINT3D P3;
545 
546  /* If edge is really large, calculate a narrower equivalent angle A1/A3. */
547  if ( p_dot < 0 )
548  {
549  vector_sum(P1, P2, &P3);
550  normalize(&P3);
551  }
552  /* If edge is narrow, calculate a wider equivalent angle A1/A3. */
553  else if ( p_dot > 0.95 )
554  {
555  vector_difference(P2, P1, &P3);
556  normalize(&P3);
557  }
558  /* Just keep the current angle in A1/A3. */
559  else
560  {
561  P3 = *P2;
562  }
563 
564  /* Normals to the A-plane and B-plane */
565  cross_product(P1, &P3, normal);
566  normalize(normal);
567 }
568 
573 void vector_rotate(const POINT3D* v1, const POINT3D* v2, double angle, POINT3D* n)
574 {
575  POINT3D u;
576  double cos_a = cos(angle);
577  double sin_a = sin(angle);
578  double uxuy, uyuz, uxuz;
579  double ux2, uy2, uz2;
580  double rxx, rxy, rxz, ryx, ryy, ryz, rzx, rzy, rzz;
581 
582  /* Need a unit vector normal to rotate around */
583  unit_normal(v1, v2, &u);
584 
585  uxuy = u.x * u.y;
586  uxuz = u.x * u.z;
587  uyuz = u.y * u.z;
588 
589  ux2 = u.x * u.x;
590  uy2 = u.y * u.y;
591  uz2 = u.z * u.z;
592 
593  rxx = cos_a + ux2 * (1 - cos_a);
594  rxy = uxuy * (1 - cos_a) - u.z * sin_a;
595  rxz = uxuz * (1 - cos_a) + u.y * sin_a;
596 
597  ryx = uxuy * (1 - cos_a) + u.z * sin_a;
598  ryy = cos_a + uy2 * (1 - cos_a);
599  ryz = uyuz * (1 - cos_a) - u.x * sin_a;
600 
601  rzx = uxuz * (1 - cos_a) - u.y * sin_a;
602  rzy = uyuz * (1 - cos_a) + u.x * sin_a;
603  rzz = cos_a + uz2 * (1 - cos_a);
604 
605  n->x = rxx * v1->x + rxy * v1->y + rxz * v1->z;
606  n->y = ryx * v1->x + ryy * v1->y + ryz * v1->z;
607  n->z = rzx * v1->x + rzy * v1->y + rzz * v1->z;
608 
609  normalize(n);
610 }
611 
616 {
617  double d = sqrt(p->x*p->x + p->y*p->y + p->z*p->z);
618  if (FP_IS_ZERO(d))
619  {
620  p->x = p->y = p->z = 0.0;
621  return;
622  }
623  p->x = p->x / d;
624  p->y = p->y / d;
625  p->z = p->z / d;
626  return;
627 }
628 
629 
635 {
636  double lon_qpp = (q->lon + p->lon) / -2.0;
637  double lon_qmp = (q->lon - p->lon) / 2.0;
638  double sin_p_lat_minus_q_lat = sin(p->lat-q->lat);
639  double sin_p_lat_plus_q_lat = sin(p->lat+q->lat);
640  double sin_lon_qpp = sin(lon_qpp);
641  double sin_lon_qmp = sin(lon_qmp);
642  double cos_lon_qpp = cos(lon_qpp);
643  double cos_lon_qmp = cos(lon_qmp);
644  a->x = sin_p_lat_minus_q_lat * sin_lon_qpp * cos_lon_qmp -
645  sin_p_lat_plus_q_lat * cos_lon_qpp * sin_lon_qmp;
646  a->y = sin_p_lat_minus_q_lat * cos_lon_qpp * cos_lon_qmp +
647  sin_p_lat_plus_q_lat * sin_lon_qpp * sin_lon_qmp;
648  a->z = cos(p->lat) * cos(q->lat) * sin(q->lon-p->lon);
649 }
650 
651 void x_to_z(POINT3D *p)
652 {
653  double tmp = p->z;
654  p->z = p->x;
655  p->x = tmp;
656 }
657 
658 void y_to_z(POINT3D *p)
659 {
660  double tmp = p->z;
661  p->z = p->y;
662  p->y = tmp;
663 }
664 
665 
667 {
668  double sign_s = SIGNUM(s->lon);
669  double sign_e = SIGNUM(e->lon);
670  double ss = fabs(s->lon);
671  double ee = fabs(e->lon);
672  if ( sign_s == sign_e )
673  {
674  return LW_FALSE;
675  }
676  else
677  {
678  double dl = ss + ee;
679  if ( dl < M_PI )
680  return LW_FALSE;
681  else if ( FP_EQUALS(dl, M_PI) )
682  return LW_FALSE;
683  else
684  return LW_TRUE;
685  }
686 }
687 
693 static int
695 {
696  POINT3D normal, pt;
697  double w;
698  /* Normal to the plane defined by e */
699  robust_cross_product(&(e->start), &(e->end), &normal);
700  normalize(&normal);
701  geog2cart(p, &pt);
702  /* We expect the dot product of with normal with any vector in the plane to be zero */
703  w = dot_product(&normal, &pt);
704  LWDEBUGF(4,"dot product %.9g",w);
705  if ( FP_IS_ZERO(w) )
706  {
707  LWDEBUG(4, "point is on plane (dot product is zero)");
708  return 0;
709  }
710 
711  if ( w < 0 )
712  return -1;
713  else
714  return 1;
715 }
716 
724 {
725  int side = edge_point_side(e, p);
726  if ( side == 0 )
727  return LW_TRUE;
728 
729  return LW_FALSE;
730 }
731 
737 {
738  POINT3D vcp, vs, ve, vp;
739  double vs_dot_vcp, vp_dot_vcp;
740  geog2cart(&(e->start), &vs);
741  geog2cart(&(e->end), &ve);
742  /* Antipodal case, everything is inside. */
743  if ( vs.x == -1.0 * ve.x && vs.y == -1.0 * ve.y && vs.z == -1.0 * ve.z )
744  return LW_TRUE;
745  geog2cart(p, &vp);
746  /* The normalized sum bisects the angle between start and end. */
747  vector_sum(&vs, &ve, &vcp);
748  normalize(&vcp);
749  /* The projection of start onto the center defines the minimum similarity */
750  vs_dot_vcp = dot_product(&vs, &vcp);
751  LWDEBUGF(4,"vs_dot_vcp %.19g",vs_dot_vcp);
752  /* The projection of candidate p onto the center */
753  vp_dot_vcp = dot_product(&vp, &vcp);
754  LWDEBUGF(4,"vp_dot_vcp %.19g",vp_dot_vcp);
755  /* If p is more similar than start then p is inside the cone */
756  LWDEBUGF(4,"fabs(vp_dot_vcp - vs_dot_vcp) %.39g",fabs(vp_dot_vcp - vs_dot_vcp));
757 
758  /*
759  ** We want to test that vp_dot_vcp is >= vs_dot_vcp but there are
760  ** numerical stability issues for values that are very very nearly
761  ** equal. Unfortunately there are also values of vp_dot_vcp that are legitimately
762  ** very close to but still less than vs_dot_vcp which we also need to catch.
763  ** The tolerance of 10-17 seems to do the trick on 32-bit and 64-bit architectures,
764  ** for the test cases here.
765  ** However, tuning the tolerance value feels like a dangerous hack.
766  ** Fundamentally, the problem is that this test is so sensitive.
767  */
768 
769  /* 1.1102230246251565404236316680908203125e-16 */
770 
771  if ( vp_dot_vcp > vs_dot_vcp || fabs(vp_dot_vcp - vs_dot_vcp) < 2e-16 )
772  {
773  LWDEBUG(4, "point is in cone");
774  return LW_TRUE;
775  }
776  LWDEBUG(4, "point is not in cone");
777  return LW_FALSE;
778 }
779 
784 {
785  GEOGRAPHIC_EDGE g;
787  double slon = fabs((e->start).lon) + fabs((e->end).lon);
788  double dlon = fabs(fabs((e->start).lon) - fabs((e->end).lon));
789  double slat = (e->start).lat + (e->end).lat;
790 
791  LWDEBUGF(4, "e.start == GPOINT(%.6g %.6g) ", (e->start).lat, (e->start).lon);
792  LWDEBUGF(4, "e.end == GPOINT(%.6g %.6g) ", (e->end).lat, (e->end).lon);
793  LWDEBUGF(4, "p == GPOINT(%.6g %.6g) ", p->lat, p->lon);
794 
795  /* Copy values into working registers */
796  g = *e;
797  q = *p;
798 
799  /* Vertical plane, we need to do this calculation in latitude */
800  if ( FP_EQUALS( g.start.lon, g.end.lon ) )
801  {
802  LWDEBUG(4, "vertical plane, we need to do this calculation in latitude");
803  /* Supposed to be co-planar... */
804  if ( ! FP_EQUALS( q.lon, g.start.lon ) )
805  return LW_FALSE;
806 
807  if ( ( g.start.lat <= q.lat && q.lat <= g.end.lat ) ||
808  ( g.end.lat <= q.lat && q.lat <= g.start.lat ) )
809  {
810  return LW_TRUE;
811  }
812  else
813  {
814  return LW_FALSE;
815  }
816  }
817 
818  /* Over the pole, we need normalize latitude and do this calculation in latitude */
819  if ( FP_EQUALS( slon, M_PI ) && ( SIGNUM(g.start.lon) != SIGNUM(g.end.lon) || FP_EQUALS(dlon, M_PI) ) )
820  {
821  LWDEBUG(4, "over the pole...");
822  /* Antipodal, everything (or nothing?) is inside */
823  if ( FP_EQUALS( slat, 0.0 ) )
824  return LW_TRUE;
825 
826  /* Point *is* the north pole */
827  if ( slat > 0.0 && FP_EQUALS(q.lat, M_PI_2 ) )
828  return LW_TRUE;
829 
830  /* Point *is* the south pole */
831  if ( slat < 0.0 && FP_EQUALS(q.lat, -1.0 * M_PI_2) )
832  return LW_TRUE;
833 
834  LWDEBUG(4, "coplanar?...");
835 
836  /* Supposed to be co-planar... */
837  if ( ! FP_EQUALS( q.lon, g.start.lon ) )
838  return LW_FALSE;
839 
840  LWDEBUG(4, "north or south?...");
841 
842  /* Over north pole, test based on south pole */
843  if ( slat > 0.0 )
844  {
845  LWDEBUG(4, "over the north pole...");
846  if ( q.lat > FP_MIN(g.start.lat, g.end.lat) )
847  return LW_TRUE;
848  else
849  return LW_FALSE;
850  }
851  else
852  /* Over south pole, test based on north pole */
853  {
854  LWDEBUG(4, "over the south pole...");
855  if ( q.lat < FP_MAX(g.start.lat, g.end.lat) )
856  return LW_TRUE;
857  else
858  return LW_FALSE;
859  }
860  }
861 
862  /* Dateline crossing, flip everything to the opposite hemisphere */
863  else if ( slon > M_PI && ( SIGNUM(g.start.lon) != SIGNUM(g.end.lon) ) )
864  {
865  LWDEBUG(4, "crosses dateline, flip longitudes...");
866  if ( g.start.lon > 0.0 )
867  g.start.lon -= M_PI;
868  else
869  g.start.lon += M_PI;
870  if ( g.end.lon > 0.0 )
871  g.end.lon -= M_PI;
872  else
873  g.end.lon += M_PI;
874 
875  if ( q.lon > 0.0 )
876  q.lon -= M_PI;
877  else
878  q.lon += M_PI;
879  }
880 
881  if ( ( g.start.lon <= q.lon && q.lon <= g.end.lon ) ||
882  ( g.end.lon <= q.lon && q.lon <= g.start.lon ) )
883  {
884  LWDEBUG(4, "true, this edge contains point");
885  return LW_TRUE;
886  }
887 
888  LWDEBUG(4, "false, this edge does not contain point");
889  return LW_FALSE;
890 }
891 
892 
897 {
898  double d_lon, cos_d_lon, cos_lat_e, sin_lat_e, cos_lat_s, sin_lat_s;
899  double a1, a2, a, b;
900 
901  if (FP_EQUALS(s->lat, e->lat) && FP_EQUALS(s->lon, e->lon)) return 0.0;
902  d_lon = e->lon - s->lon;
903  cos_d_lon = cos(d_lon);
904  cos_lat_e = cos(e->lat);
905  sin_lat_e = sin(e->lat);
906  cos_lat_s = cos(s->lat);
907  sin_lat_s = sin(s->lat);
908 
909  a1 = POW2(cos_lat_e * sin(d_lon));
910  a2 = POW2(cos_lat_s * sin_lat_e - sin_lat_s * cos_lat_e * cos_d_lon);
911  a = sqrt(a1 + a2);
912  b = sin_lat_s * sin_lat_e + cos_lat_s * cos_lat_e * cos_d_lon;
913  return atan2(a, b);
914 }
915 
919 double sphere_distance_cartesian(const POINT3D *s, const POINT3D *e)
920 {
921  return acos(FP_MIN(1.0, dot_product(s, e)));
922 }
923 
927 double sphere_direction(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d)
928 {
929  double heading = 0.0;
930  double f;
931 
932  /* Starting from the poles? Special case. */
933  if ( FP_IS_ZERO(cos(s->lat)) )
934  return (s->lat > 0.0) ? M_PI : 0.0;
935 
936  f = (sin(e->lat) - sin(s->lat) * cos(d)) / (sin(d) * cos(s->lat));
937  if ( FP_EQUALS(f, 1.0) )
938  heading = 0.0;
939  else if ( FP_EQUALS(f, -1.0) )
940  heading = M_PI;
941  else if ( fabs(f) > 1.0 )
942  {
943  LWDEBUGF(4, "f = %g", f);
944  heading = acos(f);
945  }
946  else
947  heading = acos(f);
948 
949  if ( sin(e->lon - s->lon) < 0.0 )
950  heading = -1 * heading;
951 
952  return heading;
953 }
954 
955 #if 0 /* unused */
967 static double sphere_excess(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c)
968 {
969  double a_dist = sphere_distance(b, c);
970  double b_dist = sphere_distance(c, a);
971  double c_dist = sphere_distance(a, b);
972  double hca = sphere_direction(c, a, b_dist);
973  double hcb = sphere_direction(c, b, a_dist);
974  double sign = SIGNUM(hcb-hca);
975  double ss = (a_dist + b_dist + c_dist) / 2.0;
976  double E = tan(ss/2.0)*tan((ss-a_dist)/2.0)*tan((ss-b_dist)/2.0)*tan((ss-c_dist)/2.0);
977  return 4.0 * atan(sqrt(fabs(E))) * sign;
978 }
979 #endif
980 
981 
987 {
988  if ( edge_point_in_cone(e, p) && edge_point_on_plane(e, p) )
989  /* if ( edge_contains_coplanar_point(e, p) && edge_point_on_plane(e, p) ) */
990  {
991  LWDEBUG(4, "point is on edge");
992  return LW_TRUE;
993  }
994  LWDEBUG(4, "point is not on edge");
995  return LW_FALSE;
996 }
997 
1001 double z_to_latitude(double z, int top)
1002 {
1003  double sign = SIGNUM(z);
1004  double tlat = acos(z);
1005  LWDEBUGF(4, "inputs: z(%.8g) sign(%.8g) tlat(%.8g)", z, sign, tlat);
1006  if (FP_IS_ZERO(z))
1007  {
1008  if (top) return M_PI_2;
1009  else return -1.0 * M_PI_2;
1010  }
1011  if (fabs(tlat) > M_PI_2 )
1012  {
1013  tlat = sign * (M_PI - fabs(tlat));
1014  }
1015  else
1016  {
1017  tlat = sign * tlat;
1018  }
1019  LWDEBUGF(4, "output: tlat(%.8g)", tlat);
1020  return tlat;
1021 }
1022 
1028 int clairaut_cartesian(const POINT3D *start, const POINT3D *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom)
1029 {
1030  POINT3D t1, t2;
1031  GEOGRAPHIC_POINT vN1, vN2;
1032  LWDEBUG(4,"entering function");
1033  unit_normal(start, end, &t1);
1034  unit_normal(end, start, &t2);
1035  LWDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z);
1036  LWDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z);
1037  cart2geog(&t1, &vN1);
1038  cart2geog(&t2, &vN2);
1039  g_top->lat = z_to_latitude(t1.z,LW_TRUE);
1040  g_top->lon = vN2.lon;
1041  g_bottom->lat = z_to_latitude(t2.z,LW_FALSE);
1042  g_bottom->lon = vN1.lon;
1043  LWDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon);
1044  LWDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon);
1045  return LW_SUCCESS;
1046 }
1047 
1054 {
1055  POINT3D t1, t2;
1056  GEOGRAPHIC_POINT vN1, vN2;
1057  LWDEBUG(4,"entering function");
1058  robust_cross_product(start, end, &t1);
1059  normalize(&t1);
1060  robust_cross_product(end, start, &t2);
1061  normalize(&t2);
1062  LWDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z);
1063  LWDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z);
1064  cart2geog(&t1, &vN1);
1065  cart2geog(&t2, &vN2);
1066  g_top->lat = z_to_latitude(t1.z,LW_TRUE);
1067  g_top->lon = vN2.lon;
1068  g_bottom->lat = z_to_latitude(t2.z,LW_FALSE);
1069  g_bottom->lon = vN1.lon;
1070  LWDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon);
1071  LWDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon);
1072  return LW_SUCCESS;
1073 }
1074 
1080 {
1081  POINT3D ea, eb, v;
1082  LWDEBUGF(4, "e1 start(%.20g %.20g) end(%.20g %.20g)", e1->start.lat, e1->start.lon, e1->end.lat, e1->end.lon);
1083  LWDEBUGF(4, "e2 start(%.20g %.20g) end(%.20g %.20g)", e2->start.lat, e2->start.lon, e2->end.lat, e2->end.lon);
1084 
1085  LWDEBUGF(4, "e1 start(%.20g %.20g) end(%.20g %.20g)", rad2deg(e1->start.lon), rad2deg(e1->start.lat), rad2deg(e1->end.lon), rad2deg(e1->end.lat));
1086  LWDEBUGF(4, "e2 start(%.20g %.20g) end(%.20g %.20g)", rad2deg(e2->start.lon), rad2deg(e2->start.lat), rad2deg(e2->end.lon), rad2deg(e2->end.lat));
1087 
1088  if ( geographic_point_equals(&(e1->start), &(e2->start)) )
1089  {
1090  *g = e1->start;
1091  return LW_TRUE;
1092  }
1093  if ( geographic_point_equals(&(e1->end), &(e2->end)) )
1094  {
1095  *g = e1->end;
1096  return LW_TRUE;
1097  }
1098  if ( geographic_point_equals(&(e1->end), &(e2->start)) )
1099  {
1100  *g = e1->end;
1101  return LW_TRUE;
1102  }
1103  if ( geographic_point_equals(&(e1->start), &(e2->end)) )
1104  {
1105  *g = e1->start;
1106  return LW_TRUE;
1107  }
1108 
1109  robust_cross_product(&(e1->start), &(e1->end), &ea);
1110  normalize(&ea);
1111  robust_cross_product(&(e2->start), &(e2->end), &eb);
1112  normalize(&eb);
1113  LWDEBUGF(4, "e1 cross product == POINT(%.12g %.12g %.12g)", ea.x, ea.y, ea.z);
1114  LWDEBUGF(4, "e2 cross product == POINT(%.12g %.12g %.12g)", eb.x, eb.y, eb.z);
1115  LWDEBUGF(4, "fabs(dot_product(ea, eb)) == %.14g", fabs(dot_product(&ea, &eb)));
1116  if ( FP_EQUALS(fabs(dot_product(&ea, &eb)), 1.0) )
1117  {
1118  LWDEBUGF(4, "parallel edges found! dot_product = %.12g", dot_product(&ea, &eb));
1119  /* Parallel (maybe equal) edges! */
1120  /* Hack alert, only returning ONE end of the edge right now, most do better later. */
1121  /* Hack alert #2, returning a value of 2 to indicate a co-linear crossing event. */
1122  if ( edge_contains_point(e1, &(e2->start)) )
1123  {
1124  *g = e2->start;
1125  return 2;
1126  }
1127  if ( edge_contains_point(e1, &(e2->end)) )
1128  {
1129  *g = e2->end;
1130  return 2;
1131  }
1132  if ( edge_contains_point(e2, &(e1->start)) )
1133  {
1134  *g = e1->start;
1135  return 2;
1136  }
1137  if ( edge_contains_point(e2, &(e1->end)) )
1138  {
1139  *g = e1->end;
1140  return 2;
1141  }
1142  }
1143  unit_normal(&ea, &eb, &v);
1144  LWDEBUGF(4, "v == POINT(%.12g %.12g %.12g)", v.x, v.y, v.z);
1145  g->lat = atan2(v.z, sqrt(v.x * v.x + v.y * v.y));
1146  g->lon = atan2(v.y, v.x);
1147  LWDEBUGF(4, "g == GPOINT(%.12g %.12g)", g->lat, g->lon);
1148  LWDEBUGF(4, "g == POINT(%.12g %.12g)", rad2deg(g->lon), rad2deg(g->lat));
1149  if ( edge_contains_point(e1, g) && edge_contains_point(e2, g) )
1150  {
1151  return LW_TRUE;
1152  }
1153  else
1154  {
1155  LWDEBUG(4, "flipping point to other side of sphere");
1156  g->lat = -1.0 * g->lat;
1157  g->lon = g->lon + M_PI;
1158  if ( g->lon > M_PI )
1159  {
1160  g->lon = -1.0 * (2.0 * M_PI - g->lon);
1161  }
1162  if ( edge_contains_point(e1, g) && edge_contains_point(e2, g) )
1163  {
1164  return LW_TRUE;
1165  }
1166  }
1167  return LW_FALSE;
1168 }
1169 
1171 {
1172  double d1 = 1000000000.0, d2, d3, d_nearest;
1173  POINT3D n, p, k;
1174  GEOGRAPHIC_POINT gk, g_nearest;
1175 
1176  /* Zero length edge, */
1177  if ( geographic_point_equals(&(e->start), &(e->end)) )
1178  {
1179  if (closest)
1180  *closest = e->start;
1181 
1182  return sphere_distance(&(e->start), gp);
1183  }
1184 
1185  robust_cross_product(&(e->start), &(e->end), &n);
1186  normalize(&n);
1187  geog2cart(gp, &p);
1188  vector_scale(&n, dot_product(&p, &n));
1189  vector_difference(&p, &n, &k);
1190  normalize(&k);
1191  cart2geog(&k, &gk);
1192  if ( edge_point_in_cone(e, &gk) )
1193  {
1194  d1 = sphere_distance(gp, &gk);
1195  }
1196  d2 = sphere_distance(gp, &(e->start));
1197  d3 = sphere_distance(gp, &(e->end));
1198 
1199  d_nearest = d1;
1200  g_nearest = gk;
1201 
1202  if ( d2 < d_nearest )
1203  {
1204  d_nearest = d2;
1205  g_nearest = e->start;
1206  }
1207  if ( d3 < d_nearest )
1208  {
1209  d_nearest = d3;
1210  g_nearest = e->end;
1211  }
1212  if (closest)
1213  *closest = g_nearest;
1214 
1215  return d_nearest;
1216 }
1217 
1224 {
1225  double d;
1226  GEOGRAPHIC_POINT gcp1s, gcp1e, gcp2s, gcp2e, c1, c2;
1227  double d1s = edge_distance_to_point(e1, &(e2->start), &gcp1s);
1228  double d1e = edge_distance_to_point(e1, &(e2->end), &gcp1e);
1229  double d2s = edge_distance_to_point(e2, &(e1->start), &gcp2s);
1230  double d2e = edge_distance_to_point(e2, &(e1->end), &gcp2e);
1231 
1232  d = d1s;
1233  c1 = gcp1s;
1234  c2 = e2->start;
1235 
1236  if ( d1e < d )
1237  {
1238  d = d1e;
1239  c1 = gcp1e;
1240  c2 = e2->end;
1241  }
1242 
1243  if ( d2s < d )
1244  {
1245  d = d2s;
1246  c1 = e1->start;
1247  c2 = gcp2s;
1248  }
1249 
1250  if ( d2e < d )
1251  {
1252  d = d2e;
1253  c1 = e1->end;
1254  c2 = gcp2e;
1255  }
1256 
1257  if ( closest1 ) *closest1 = c1;
1258  if ( closest2 ) *closest2 = c2;
1259 
1260  return d;
1261 }
1262 
1263 
1268 int sphere_project(const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n)
1269 {
1270  double d = distance;
1271  double lat1 = r->lat;
1272  double lon1 = r->lon;
1273  double lat2, lon2;
1274 
1275  lat2 = asin(sin(lat1)*cos(d) + cos(lat1)*sin(d)*cos(azimuth));
1276 
1277  /* If we're going straight up or straight down, we don't need to calculate the longitude */
1278  /* TODO: this isn't quite true, what if we're going over the pole? */
1279  if ( FP_EQUALS(azimuth, M_PI) || FP_EQUALS(azimuth, 0.0) )
1280  {
1281  lon2 = r->lon;
1282  }
1283  else
1284  {
1285  lon2 = lon1 + atan2(sin(azimuth)*sin(d)*cos(lat1), cos(d)-sin(lat1)*sin(lat2));
1286  }
1287 
1288  if ( isnan(lat2) || isnan(lon2) )
1289  return LW_FAILURE;
1290 
1291  n->lat = lat2;
1292  n->lon = lon2;
1293 
1294  return LW_SUCCESS;
1295 }
1296 
1297 
1299 {
1300  int steps = 1000000;
1301  int i;
1302  double dx, dy, dz;
1303  double distance = sphere_distance(&(e->start), &(e->end));
1304  POINT3D pn, p, start, end;
1305 
1306  /* Edge is zero length, just return the naive box */
1307  if ( FP_IS_ZERO(distance) )
1308  {
1309  LWDEBUG(4, "edge is zero length. returning");
1310  geog2cart(&(e->start), &start);
1311  geog2cart(&(e->end), &end);
1312  gbox_init_point3d(&start, gbox);
1313  gbox_merge_point3d(&end, gbox);
1314  return LW_SUCCESS;
1315  }
1316 
1317  /* Edge is antipodal (one point on each side of the globe),
1318  set the box to contain the whole world and return */
1319  if ( FP_EQUALS(distance, M_PI) )
1320  {
1321  LWDEBUG(4, "edge is antipodal. setting to maximum size box, and returning");
1322  gbox->xmin = gbox->ymin = gbox->zmin = -1.0;
1323  gbox->xmax = gbox->ymax = gbox->zmax = 1.0;
1324  return LW_SUCCESS;
1325  }
1326 
1327  /* Walk along the chord between start and end incrementally,
1328  normalizing at each step. */
1329  geog2cart(&(e->start), &start);
1330  geog2cart(&(e->end), &end);
1331  dx = (end.x - start.x)/steps;
1332  dy = (end.y - start.y)/steps;
1333  dz = (end.z - start.z)/steps;
1334  p = start;
1335  gbox->xmin = gbox->xmax = p.x;
1336  gbox->ymin = gbox->ymax = p.y;
1337  gbox->zmin = gbox->zmax = p.z;
1338  for ( i = 0; i < steps; i++ )
1339  {
1340  p.x += dx;
1341  p.y += dy;
1342  p.z += dz;
1343  pn = p;
1344  normalize(&pn);
1345  gbox_merge_point3d(&pn, gbox);
1346  }
1347  return LW_SUCCESS;
1348 }
1349 
1362 int edge_calculate_gbox(const POINT3D *A1, const POINT3D *A2, GBOX *gbox)
1363 {
1364  POINT2D R1, R2, RX, O;
1365  POINT3D AN, A3;
1366  POINT3D X[6];
1367  int i, o_side;
1368 
1369  /* Initialize the box with the edge end points */
1370  gbox_init_point3d(A1, gbox);
1371  gbox_merge_point3d(A2, gbox);
1372 
1373  /* Zero length edge, just return! */
1374  if ( p3d_same(A1, A2) )
1375  return LW_SUCCESS;
1376 
1377  /* Error out on antipodal edge */
1378  if ( FP_EQUALS(A1->x, -1*A2->x) && FP_EQUALS(A1->y, -1*A2->y) && FP_EQUALS(A1->z, -1*A2->z) )
1379  {
1380  lwerror("Antipodal (180 degrees long) edge detected!");
1381  return LW_FAILURE;
1382  }
1383 
1384  /* Create A3, a vector in the plane of A1/A2, orthogonal to A1 */
1385  unit_normal(A1, A2, &AN);
1386  unit_normal(&AN, A1, &A3);
1387 
1388  /* Project A1 and A2 into the 2-space formed by the plane A1/A3 */
1389  R1.x = 1.0;
1390  R1.y = 0.0;
1391  R2.x = dot_product(A2, A1);
1392  R2.y = dot_product(A2, &A3);
1393 
1394  /* Initialize our 3-space axis points (x+, x-, y+, y-, z+, z-) */
1395  memset(X, 0, sizeof(POINT3D) * 6);
1396  X[0].x = X[2].y = X[4].z = 1.0;
1397  X[1].x = X[3].y = X[5].z = -1.0;
1398 
1399  /* Initialize a 2-space origin point. */
1400  O.x = O.y = 0.0;
1401  /* What side of the line joining R1/R2 is O? */
1402  o_side = lw_segment_side(&R1, &R2, &O);
1403 
1404  /* Add any extrema! */
1405  for ( i = 0; i < 6; i++ )
1406  {
1407  /* Convert 3-space axis points to 2-space unit vectors */
1408  RX.x = dot_product(&(X[i]), A1);
1409  RX.y = dot_product(&(X[i]), &A3);
1410  normalize2d(&RX);
1411 
1412  /* Any axis end on the side of R1/R2 opposite the origin */
1413  /* is an extreme point in the arc, so we add the 3-space */
1414  /* version of the point on R1/R2 to the gbox */
1415  if ( lw_segment_side(&R1, &R2, &RX) != o_side )
1416  {
1417  POINT3D Xn;
1418  Xn.x = RX.x * A1->x + RX.y * A3.x;
1419  Xn.y = RX.x * A1->y + RX.y * A3.y;
1420  Xn.z = RX.x * A1->z + RX.y * A3.z;
1421 
1422  gbox_merge_point3d(&Xn, gbox);
1423  }
1424  }
1425 
1426  return LW_SUCCESS;
1427 }
1428 
1429 /*
1430 * When we have a globe-covering gbox but we still want an outside
1431 * point, we do this Very Bad Hack, which is look at the first two points
1432 * in the ring and then nudge a point to the left of that arc.
1433 * There is an assumption of convexity built in there, as well as that
1434 * the shape doesn't have a sharp reversal in it. It's ugly, but
1435 * it fixes some common cases (large selection polygons) that users
1436 * are generating. At some point all of geodetic needs a clean-room
1437 * rewrite.
1438 * There is also an assumption of CCW exterior ring, which is how the
1439 * GeoJSON spec defined geographic ring orientation.
1440 */
1441 static int lwpoly_pt_outside_hack(const LWPOLY *poly, POINT2D *pt_outside)
1442 {
1443  GEOGRAPHIC_POINT g1, g2, gSum;
1444  POINT4D p1, p2;
1445  POINT3D q1, q2, qMid, qCross, qSum;
1446  POINTARRAY *pa;
1447  if (lwgeom_is_empty((LWGEOM*)poly))
1448  return LW_FAILURE;
1449  if (poly->nrings < 1)
1450  return LW_FAILURE;
1451  pa = poly->rings[0];
1452  if (pa->npoints < 2)
1453  return LW_FAILURE;
1454 
1455  /* First two points of ring */
1456  getPoint4d_p(pa, 0, &p1);
1457  getPoint4d_p(pa, 1, &p2);
1458  /* Convert to XYZ unit vectors */
1459  geographic_point_init(p1.x, p1.y, &g1);
1460  geographic_point_init(p2.x, p2.y, &g2);
1461  geog2cart(&g1, &q1);
1462  geog2cart(&g2, &q2);
1463  /* Mid-point of first two points */
1464  vector_sum(&q1, &q2, &qMid);
1465  normalize(&qMid);
1466  /* Cross product of first two points (perpendicular) */
1467  cross_product(&q1, &q2, &qCross);
1468  normalize(&qCross);
1469  /* Invert it to put it outside, and scale down */
1470  vector_scale(&qCross, -0.2);
1471  /* Project midpoint to the right */
1472  vector_sum(&qMid, &qCross, &qSum);
1473  normalize(&qSum);
1474  /* Convert back to lon/lat */
1475  cart2geog(&qSum, &gSum);
1476  pt_outside->x = rad2deg(gSum.lon);
1477  pt_outside->y = rad2deg(gSum.lat);
1478  return LW_SUCCESS;
1479 }
1480 
1481 int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside)
1482 {
1483  int rv;
1484  /* Make sure we have boxes */
1485  if ( poly->bbox )
1486  {
1487  rv = gbox_pt_outside(poly->bbox, pt_outside);
1488  }
1489  else
1490  {
1491  GBOX gbox;
1492  lwgeom_calculate_gbox_geodetic((LWGEOM*)poly, &gbox);
1493  rv = gbox_pt_outside(&gbox, pt_outside);
1494  }
1495 
1496  if (rv == LW_FALSE)
1497  return lwpoly_pt_outside_hack(poly, pt_outside);
1498 
1499  return rv;
1500 }
1501 
1506 int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside)
1507 {
1508  double grow = M_PI / 180.0 / 60.0; /* one arc-minute */
1509  int i;
1510  GBOX ge;
1511  POINT3D corners[8];
1512  POINT3D pt;
1513  GEOGRAPHIC_POINT g;
1514 
1515  while ( grow < M_PI )
1516  {
1517  /* Assign our box and expand it slightly. */
1518  ge = *gbox;
1519  if ( ge.xmin > -1 ) ge.xmin -= grow;
1520  if ( ge.ymin > -1 ) ge.ymin -= grow;
1521  if ( ge.zmin > -1 ) ge.zmin -= grow;
1522  if ( ge.xmax < 1 ) ge.xmax += grow;
1523  if ( ge.ymax < 1 ) ge.ymax += grow;
1524  if ( ge.zmax < 1 ) ge.zmax += grow;
1525 
1526  /* Build our eight corner points */
1527  corners[0].x = ge.xmin;
1528  corners[0].y = ge.ymin;
1529  corners[0].z = ge.zmin;
1530 
1531  corners[1].x = ge.xmin;
1532  corners[1].y = ge.ymax;
1533  corners[1].z = ge.zmin;
1534 
1535  corners[2].x = ge.xmin;
1536  corners[2].y = ge.ymin;
1537  corners[2].z = ge.zmax;
1538 
1539  corners[3].x = ge.xmax;
1540  corners[3].y = ge.ymin;
1541  corners[3].z = ge.zmin;
1542 
1543  corners[4].x = ge.xmax;
1544  corners[4].y = ge.ymax;
1545  corners[4].z = ge.zmin;
1546 
1547  corners[5].x = ge.xmax;
1548  corners[5].y = ge.ymin;
1549  corners[5].z = ge.zmax;
1550 
1551  corners[6].x = ge.xmin;
1552  corners[6].y = ge.ymax;
1553  corners[6].z = ge.zmax;
1554 
1555  corners[7].x = ge.xmax;
1556  corners[7].y = ge.ymax;
1557  corners[7].z = ge.zmax;
1558 
1559  LWDEBUG(4, "trying to use a box corner point...");
1560  for ( i = 0; i < 8; i++ )
1561  {
1562  normalize(&(corners[i]));
1563  LWDEBUGF(4, "testing corner %d: POINT(%.8g %.8g %.8g)", i, corners[i].x, corners[i].y, corners[i].z);
1564  if ( ! gbox_contains_point3d(gbox, &(corners[i])) )
1565  {
1566  LWDEBUGF(4, "corner %d is outside our gbox", i);
1567  pt = corners[i];
1568  normalize(&pt);
1569  cart2geog(&pt, &g);
1570  pt_outside->x = rad2deg(g.lon);
1571  pt_outside->y = rad2deg(g.lat);
1572  LWDEBUGF(4, "returning POINT(%.8g %.8g) as outside point", pt_outside->x, pt_outside->y);
1573  return LW_SUCCESS;
1574  }
1575  }
1576 
1577  /* Try a wider growth to push the corners outside the original box. */
1578  grow *= 2.0;
1579  }
1580 
1581  /* This should never happen! */
1582  // lwerror("BOOM! Could not generate outside point!");
1583  return LW_FAILURE;
1584 }
1585 
1586 
1588  const POINT3D *p1, const POINT3D *p2, /* 3-space points we are interpolating between */
1589  const POINT4D *v1, const POINT4D *v2, /* real values and z/m values */
1590  double d, double max_seg_length, /* current segment length and segment limit */
1591  POINTARRAY *pa) /* write out results here */
1592 {
1593  GEOGRAPHIC_POINT g;
1594  /* Reached the terminal leaf in recursion. Add */
1595  /* the left-most point to the pointarray here */
1596  /* We recurse down the left side first, so outputs should */
1597  /* end up added to the array in order this way */
1598  if (d <= max_seg_length)
1599  {
1600  POINT4D p;
1601  cart2geog(p1, &g);
1602  p.x = v1->x;
1603  p.y = v1->y;
1604  p.z = v1->z;
1605  p.m = v1->m;
1606  return ptarray_append_point(pa, &p, LW_FALSE);
1607  }
1608  /* Find the mid-point and recurse on the left and then the right */
1609  else
1610  {
1611  /* Calculate mid-point */
1612  POINT3D mid;
1613  mid.x = (p1->x + p2->x) / 2.0;
1614  mid.y = (p1->y + p2->y) / 2.0;
1615  mid.z = (p1->z + p2->z) / 2.0;
1616  normalize(&mid);
1617 
1618  /* Calculate z/m mid-values */
1619  POINT4D midv;
1620  cart2geog(&mid, &g);
1621  midv.x = rad2deg(g.lon);
1622  midv.y = rad2deg(g.lat);
1623  midv.z = (v1->z + v2->z) / 2.0;
1624  midv.m = (v1->m + v2->m) / 2.0;
1625  /* Recurse on the left first */
1626  ptarray_segmentize_sphere_edge_recursive(p1, &mid, v1, &midv, d/2.0, max_seg_length, pa);
1627  ptarray_segmentize_sphere_edge_recursive(&mid, p2, &midv, v2, d/2.0, max_seg_length, pa);
1628  return LW_SUCCESS;
1629  }
1630 }
1631 
1637 static POINTARRAY*
1638 ptarray_segmentize_sphere(const POINTARRAY *pa_in, double max_seg_length)
1639 {
1640  POINTARRAY *pa_out;
1641  int hasz = ptarray_has_z(pa_in);
1642  int hasm = ptarray_has_m(pa_in);
1643  POINT4D p1, p2;
1644  POINT3D q1, q2;
1645  GEOGRAPHIC_POINT g1, g2;
1646  uint32_t i;
1647 
1648  /* Just crap out on crazy input */
1649  if ( ! pa_in )
1650  lwerror("%s: null input pointarray", __func__);
1651  if ( max_seg_length <= 0.0 )
1652  lwerror("%s: maximum segment length must be positive", __func__);
1653 
1654  /* Empty starting array */
1655  pa_out = ptarray_construct_empty(hasz, hasm, pa_in->npoints);
1656 
1657  /* Simple loop per edge */
1658  for (i = 1; i < pa_in->npoints; i++)
1659  {
1660  getPoint4d_p(pa_in, i-1, &p1);
1661  getPoint4d_p(pa_in, i, &p2);
1662  geographic_point_init(p1.x, p1.y, &g1);
1663  geographic_point_init(p2.x, p2.y, &g2);
1664 
1665  /* Skip duplicate points (except in case of 2-point lines!) */
1666  if ((pa_in->npoints > 2) && p4d_same(&p1, &p2))
1667  continue;
1668 
1669  /* How long is this edge? */
1670  double d = sphere_distance(&g1, &g2);
1671 
1672  if (d > max_seg_length)
1673  {
1674  geog2cart(&g1, &q1);
1675  geog2cart(&g2, &q2);
1676  /* 3-d end points, XYZM end point, current edge size, min edge size */
1677  ptarray_segmentize_sphere_edge_recursive(&q1, &q2, &p1, &p2, d, max_seg_length, pa_out);
1678  }
1679  /* If we don't segmentize, we need to add first point manually */
1680  else
1681  {
1682  ptarray_append_point(pa_out, &p1, LW_TRUE);
1683  }
1684  }
1685  /* Always add the last point */
1686  ptarray_append_point(pa_out, &p2, LW_TRUE);
1687  return pa_out;
1688 }
1689 
1696 LWGEOM*
1697 lwgeom_segmentize_sphere(const LWGEOM *lwg_in, double max_seg_length)
1698 {
1699  POINTARRAY *pa_out;
1700  LWLINE *lwline;
1701  LWPOLY *lwpoly_in, *lwpoly_out;
1702  LWCOLLECTION *lwcol_in, *lwcol_out;
1703  uint32_t i;
1704 
1705  /* Reflect NULL */
1706  if ( ! lwg_in )
1707  return NULL;
1708 
1709  /* Clone empty */
1710  if ( lwgeom_is_empty(lwg_in) )
1711  return lwgeom_clone(lwg_in);
1712 
1713  switch (lwg_in->type)
1714  {
1715  case MULTIPOINTTYPE:
1716  case POINTTYPE:
1717  return lwgeom_clone_deep(lwg_in);
1718  break;
1719  case LINETYPE:
1720  lwline = lwgeom_as_lwline(lwg_in);
1721  pa_out = ptarray_segmentize_sphere(lwline->points, max_seg_length);
1722  return lwline_as_lwgeom(lwline_construct(lwg_in->srid, NULL, pa_out));
1723  break;
1724  case POLYGONTYPE:
1725  lwpoly_in = lwgeom_as_lwpoly(lwg_in);
1726  lwpoly_out = lwpoly_construct_empty(lwg_in->srid, lwgeom_has_z(lwg_in), lwgeom_has_m(lwg_in));
1727  for ( i = 0; i < lwpoly_in->nrings; i++ )
1728  {
1729  pa_out = ptarray_segmentize_sphere(lwpoly_in->rings[i], max_seg_length);
1730  lwpoly_add_ring(lwpoly_out, pa_out);
1731  }
1732  return lwpoly_as_lwgeom(lwpoly_out);
1733  break;
1734  case MULTILINETYPE:
1735  case MULTIPOLYGONTYPE:
1736  case COLLECTIONTYPE:
1737  lwcol_in = lwgeom_as_lwcollection(lwg_in);
1738  lwcol_out = lwcollection_construct_empty(lwg_in->type, lwg_in->srid, lwgeom_has_z(lwg_in), lwgeom_has_m(lwg_in));
1739  for ( i = 0; i < lwcol_in->ngeoms; i++ )
1740  {
1741  lwcollection_add_lwgeom(lwcol_out, lwgeom_segmentize_sphere(lwcol_in->geoms[i], max_seg_length));
1742  }
1743  return lwcollection_as_lwgeom(lwcol_out);
1744  break;
1745  default:
1746  lwerror("lwgeom_segmentize_sphere: unsupported input geometry type: %d - %s",
1747  lwg_in->type, lwtype_name(lwg_in->type));
1748  break;
1749  }
1750 
1751  lwerror("lwgeom_segmentize_sphere got to the end of the function, should not happen");
1752  return NULL;
1753 }
1754 
1755 
1756 static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
1757 {
1758  GEOGRAPHIC_EDGE e1, e2;
1759  GEOGRAPHIC_POINT g1, g2;
1760  GEOGRAPHIC_POINT nearest1, nearest2;
1761  POINT3D A1, A2, B1, B2;
1762  const POINT2D *p;
1763  double distance;
1764  uint32_t i, j;
1765  int use_sphere = (s->a == s->b ? 1 : 0);
1766 
1767  /* Make result really big, so that everything will be smaller than it */
1768  distance = FLT_MAX;
1769 
1770  /* Empty point arrays? Return negative */
1771  if ( pa1->npoints == 0 || pa2->npoints == 0 )
1772  return -1.0;
1773 
1774  /* Handle point/point case here */
1775  if ( pa1->npoints == 1 && pa2->npoints == 1 )
1776  {
1777  p = getPoint2d_cp(pa1, 0);
1778  geographic_point_init(p->x, p->y, &g1);
1779  p = getPoint2d_cp(pa2, 0);
1780  geographic_point_init(p->x, p->y, &g2);
1781  /* Sphere special case, axes equal */
1782  distance = s->radius * sphere_distance(&g1, &g2);
1783  if ( use_sphere )
1784  return distance;
1785  /* Below tolerance, actual distance isn't of interest */
1786  else if ( distance < 0.95 * tolerance )
1787  return distance;
1788  /* Close or greater than tolerance, get the real answer to be sure */
1789  else
1790  return spheroid_distance(&g1, &g2, s);
1791  }
1792 
1793  /* Handle point/line case here */
1794  if ( pa1->npoints == 1 || pa2->npoints == 1 )
1795  {
1796  /* Handle one/many case here */
1797  uint32_t i;
1798  const POINTARRAY *pa_one;
1799  const POINTARRAY *pa_many;
1800 
1801  if ( pa1->npoints == 1 )
1802  {
1803  pa_one = pa1;
1804  pa_many = pa2;
1805  }
1806  else
1807  {
1808  pa_one = pa2;
1809  pa_many = pa1;
1810  }
1811 
1812  /* Initialize our point */
1813  p = getPoint2d_cp(pa_one, 0);
1814  geographic_point_init(p->x, p->y, &g1);
1815 
1816  /* Initialize start of line */
1817  p = getPoint2d_cp(pa_many, 0);
1818  geographic_point_init(p->x, p->y, &(e1.start));
1819 
1820  /* Iterate through the edges in our line */
1821  for ( i = 1; i < pa_many->npoints; i++ )
1822  {
1823  double d;
1824  p = getPoint2d_cp(pa_many, i);
1825  geographic_point_init(p->x, p->y, &(e1.end));
1826  /* Get the spherical distance between point and edge */
1827  d = s->radius * edge_distance_to_point(&e1, &g1, &g2);
1828  /* New shortest distance! Record this distance / location */
1829  if ( d < distance )
1830  {
1831  distance = d;
1832  nearest2 = g2;
1833  }
1834  /* We've gotten closer than the tolerance... */
1835  if ( d <= tolerance )
1836  {
1837  /* Working on a sphere? The answer is correct, return */
1838  if ( use_sphere )
1839  {
1840  return d;
1841  }
1842  /* Far enough past the tolerance that the spheroid calculation won't change things */
1843  else if ( d <= tolerance * 0.95 )
1844  {
1845  return d;
1846  }
1847  /* On a spheroid and near the tolerance? Confirm that we are *actually* closer than tolerance */
1848  else
1849  {
1850  d = spheroid_distance(&g1, &nearest2, s);
1851  /* Yes, closer than tolerance, return! */
1852  if ( d <= tolerance )
1853  return d;
1854  }
1855  }
1856  e1.start = e1.end;
1857  }
1858 
1859  /* On sphere, return answer */
1860  if ( use_sphere )
1861  return distance;
1862  /* On spheroid, calculate final answer based on closest approach */
1863  else
1864  return spheroid_distance(&g1, &nearest2, s);
1865 
1866  }
1867 
1868  /* Initialize start of line 1 */
1869  p = getPoint2d_cp(pa1, 0);
1870  geographic_point_init(p->x, p->y, &(e1.start));
1871  geog2cart(&(e1.start), &A1);
1872 
1873 
1874  /* Handle line/line case */
1875  for ( i = 1; i < pa1->npoints; i++ )
1876  {
1877  p = getPoint2d_cp(pa1, i);
1878  geographic_point_init(p->x, p->y, &(e1.end));
1879  geog2cart(&(e1.end), &A2);
1880 
1881  /* Initialize start of line 2 */
1882  p = getPoint2d_cp(pa2, 0);
1883  geographic_point_init(p->x, p->y, &(e2.start));
1884  geog2cart(&(e2.start), &B1);
1885 
1886  for ( j = 1; j < pa2->npoints; j++ )
1887  {
1888  double d;
1889 
1890  p = getPoint2d_cp(pa2, j);
1891  geographic_point_init(p->x, p->y, &(e2.end));
1892  geog2cart(&(e2.end), &B2);
1893 
1894  LWDEBUGF(4, "e1.start == GPOINT(%.6g %.6g) ", e1.start.lat, e1.start.lon);
1895  LWDEBUGF(4, "e1.end == GPOINT(%.6g %.6g) ", e1.end.lat, e1.end.lon);
1896  LWDEBUGF(4, "e2.start == GPOINT(%.6g %.6g) ", e2.start.lat, e2.start.lon);
1897  LWDEBUGF(4, "e2.end == GPOINT(%.6g %.6g) ", e2.end.lat, e2.end.lon);
1898 
1899  if ( check_intersection && edge_intersects(&A1, &A2, &B1, &B2) )
1900  {
1901  LWDEBUG(4,"edge intersection! returning 0.0");
1902  return 0.0;
1903  }
1904  d = s->radius * edge_distance_to_edge(&e1, &e2, &g1, &g2);
1905  LWDEBUGF(4,"got edge_distance_to_edge %.8g", d);
1906 
1907  if ( d < distance )
1908  {
1909  distance = d;
1910  nearest1 = g1;
1911  nearest2 = g2;
1912  }
1913  if ( d <= tolerance )
1914  {
1915  if ( use_sphere )
1916  {
1917  return d;
1918  }
1919  else
1920  {
1921  d = spheroid_distance(&nearest1, &nearest2, s);
1922  if ( d <= tolerance )
1923  return d;
1924  }
1925  }
1926 
1927  /* Copy end to start to allow a new end value in next iteration */
1928  e2.start = e2.end;
1929  B1 = B2;
1930  }
1931 
1932  /* Copy end to start to allow a new end value in next iteration */
1933  e1.start = e1.end;
1934  A1 = A2;
1935  LW_ON_INTERRUPT(return -1.0);
1936  }
1937  LWDEBUGF(4,"finished all loops, returning %.8g", distance);
1938 
1939  if ( use_sphere )
1940  return distance;
1941  else
1942  return spheroid_distance(&nearest1, &nearest2, s);
1943 }
1944 
1945 
1950 double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid)
1951 {
1952  SPHEROID s;
1954  return lwgeom_area_spheroid(lwgeom, &s);
1955 }
1956 
1957 
1967 LWPOINT* lwgeom_project_spheroid(const LWPOINT *r, const SPHEROID *spheroid, double distance, double azimuth)
1968 {
1969  GEOGRAPHIC_POINT geo_source, geo_dest;
1970  POINT4D pt_dest;
1971  double x, y;
1972  LWPOINT *lwp;
1973  int has_z, has_m;
1974 
1975  /* Normalize distance to be positive*/
1976  if ( distance < 0.0 ) {
1977  distance = -distance;
1978  azimuth += M_PI;
1979  }
1980 
1981  /* Normalize azimuth */
1982  azimuth -= 2.0 * M_PI * floor(azimuth / (2.0 * M_PI));
1983 
1984  /* Check the distance validity */
1985  if ( distance > (M_PI * spheroid->radius) )
1986  {
1987  lwerror("Distance must not be greater than %g", M_PI * spheroid->radius);
1988  return NULL;
1989  }
1990 
1991  /* Convert to ta geodetic point */
1992  x = lwpoint_get_x(r);
1993  y = lwpoint_get_y(r);
1994  has_z = lwgeom_has_z(lwpoint_as_lwgeom(r));
1995  has_m = lwgeom_has_m(lwpoint_as_lwgeom(r));
1996  geographic_point_init(x, y, &geo_source);
1997 
1998  /* Try the projection */
1999  if( spheroid_project(&geo_source, spheroid, distance, azimuth, &geo_dest) == LW_FAILURE )
2000  {
2001  LWDEBUGF(3, "Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance);
2002  lwerror("Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance);
2003  return NULL;
2004  }
2005 
2006  /* Build the output LWPOINT */
2007  pt_dest.x = rad2deg(longitude_radians_normalize(geo_dest.lon));
2008  pt_dest.y = rad2deg(latitude_radians_normalize(geo_dest.lat));
2009  pt_dest.z = has_z ? lwpoint_get_z(r) : 0.0;
2010  pt_dest.m = has_m ? lwpoint_get_m(r) : 0.0;
2011  lwp = lwpoint_make(r->srid, has_z, has_m, &pt_dest);
2013  return lwp;
2014 }
2015 
2016 LWPOINT* lwgeom_project_spheroid_lwpoint(const LWPOINT *from, const LWPOINT *to, const SPHEROID *spheroid, double distance)
2017 {
2018  double azimuth = lwgeom_azumith_spheroid(from, to, spheroid);
2019  LWPOINT *lwp = lwgeom_project_spheroid(to, spheroid, distance, azimuth);
2020  return lwp;
2021 }
2022 
2023 
2032 double lwgeom_azumith_spheroid(const LWPOINT *r, const LWPOINT *s, const SPHEROID *spheroid)
2033 {
2034  GEOGRAPHIC_POINT g1, g2;
2035  double x1, y1, x2, y2, az;
2036 
2037  /* Convert r to a geodetic point */
2038  x1 = lwpoint_get_x(r);
2039  y1 = lwpoint_get_y(r);
2040  geographic_point_init(x1, y1, &g1);
2041 
2042  /* Convert s to a geodetic point */
2043  x2 = lwpoint_get_x(s);
2044  y2 = lwpoint_get_y(s);
2045  geographic_point_init(x2, y2, &g2);
2046 
2047  /* Same point, return NaN */
2048  if ( FP_EQUALS(x1, x2) && FP_EQUALS(y1, y2) )
2049  {
2050  return NAN;
2051  }
2052 
2053  /* Do the direction calculation */
2054  az = spheroid_direction(&g1, &g2, spheroid);
2055  /* Ensure result is positive */
2056  return az < -0 ? 2*M_PI + az : az;
2057  // return az;
2058 }
2059 
2066 double lwgeom_distance_spheroid(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, const SPHEROID *spheroid, double tolerance)
2067 {
2068  uint8_t type1, type2;
2069  int check_intersection = LW_FALSE;
2070  GBOX gbox1, gbox2;
2071 
2072  gbox_init(&gbox1);
2073  gbox_init(&gbox2);
2074 
2075  assert(lwgeom1);
2076  assert(lwgeom2);
2077 
2078  LWDEBUGF(4, "entered function, tolerance %.8g", tolerance);
2079 
2080  /* What's the distance to an empty geometry? We don't know.
2081  Return a negative number so the caller can catch this case. */
2082  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
2083  {
2084  return -1.0;
2085  }
2086 
2087  type1 = lwgeom1->type;
2088  type2 = lwgeom2->type;
2089 
2090  /* Make sure we have boxes */
2091  if ( FLAGS_GET_GEODETIC(lwgeom1->flags) && lwgeom1->bbox )
2092  gbox1 = *(lwgeom1->bbox);
2093  else
2094  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
2095 
2096  /* Make sure we have boxes */
2097  if ( FLAGS_GET_GEODETIC(lwgeom2->flags) && lwgeom2->bbox )
2098  gbox2 = *(lwgeom2->bbox);
2099  else
2100  lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2);
2101 
2102  /* If the boxes aren't disjoint, we have to check for edge intersections */
2103  if ( gbox_overlaps(&gbox1, &gbox2) )
2104  check_intersection = LW_TRUE;
2105 
2106  /* Point/line combinations can all be handled with simple point array iterations */
2107  if ( ( type1 == POINTTYPE || type1 == LINETYPE ) &&
2108  ( type2 == POINTTYPE || type2 == LINETYPE ) )
2109  {
2110  POINTARRAY *pa1, *pa2;
2111 
2112  if ( type1 == POINTTYPE )
2113  pa1 = ((LWPOINT*)lwgeom1)->point;
2114  else
2115  pa1 = ((LWLINE*)lwgeom1)->points;
2116 
2117  if ( type2 == POINTTYPE )
2118  pa2 = ((LWPOINT*)lwgeom2)->point;
2119  else
2120  pa2 = ((LWLINE*)lwgeom2)->points;
2121 
2122  return ptarray_distance_spheroid(pa1, pa2, spheroid, tolerance, check_intersection);
2123  }
2124 
2125  /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */
2126  if ( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) ||
2127  ( type2 == POLYGONTYPE && type1 == POINTTYPE ) )
2128  {
2129  const POINT2D *p;
2130  LWPOLY *lwpoly;
2131  LWPOINT *lwpt;
2132  double distance = FLT_MAX;
2133  uint32_t i;
2134 
2135  if ( type1 == POINTTYPE )
2136  {
2137  lwpt = (LWPOINT*)lwgeom1;
2138  lwpoly = (LWPOLY*)lwgeom2;
2139  }
2140  else
2141  {
2142  lwpt = (LWPOINT*)lwgeom2;
2143  lwpoly = (LWPOLY*)lwgeom1;
2144  }
2145  p = getPoint2d_cp(lwpt->point, 0);
2146 
2147  /* Point in polygon implies zero distance */
2148  if ( lwpoly_covers_point2d(lwpoly, p) )
2149  {
2150  return 0.0;
2151  }
2152 
2153  /* Not inside, so what's the actual distance? */
2154  for ( i = 0; i < lwpoly->nrings; i++ )
2155  {
2156  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, spheroid, tolerance, check_intersection);
2157  if ( ring_distance < distance )
2158  distance = ring_distance;
2159  if ( distance <= tolerance )
2160  return distance;
2161  }
2162  return distance;
2163  }
2164 
2165  /* Line/polygon case, if start point-in-poly, return zero, else return distance. */
2166  if ( ( type1 == POLYGONTYPE && type2 == LINETYPE ) ||
2167  ( type2 == POLYGONTYPE && type1 == LINETYPE ) )
2168  {
2169  const POINT2D *p;
2170  LWPOLY *lwpoly;
2171  LWLINE *lwline;
2172  double distance = FLT_MAX;
2173  uint32_t i;
2174 
2175  if ( type1 == LINETYPE )
2176  {
2177  lwline = (LWLINE*)lwgeom1;
2178  lwpoly = (LWPOLY*)lwgeom2;
2179  }
2180  else
2181  {
2182  lwline = (LWLINE*)lwgeom2;
2183  lwpoly = (LWPOLY*)lwgeom1;
2184  }
2185  p = getPoint2d_cp(lwline->points, 0);
2186 
2187  LWDEBUG(4, "checking if a point of line is in polygon");
2188 
2189  /* Point in polygon implies zero distance */
2190  if ( lwpoly_covers_point2d(lwpoly, p) )
2191  return 0.0;
2192 
2193  LWDEBUG(4, "checking ring distances");
2194 
2195  /* Not contained, so what's the actual distance? */
2196  for ( i = 0; i < lwpoly->nrings; i++ )
2197  {
2198  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, spheroid, tolerance, check_intersection);
2199  LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance);
2200  if ( ring_distance < distance )
2201  distance = ring_distance;
2202  if ( distance <= tolerance )
2203  return distance;
2204  }
2205  LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance);
2206  return distance;
2207 
2208  }
2209 
2210  /* Polygon/polygon case, if start point-in-poly, return zero, else
2211  * return distance. */
2212  if (type1 == POLYGONTYPE && type2 == POLYGONTYPE)
2213  {
2214  const POINT2D* p;
2215  LWPOLY* lwpoly1 = (LWPOLY*)lwgeom1;
2216  LWPOLY* lwpoly2 = (LWPOLY*)lwgeom2;
2217  double distance = FLT_MAX;
2218  uint32_t i, j;
2219 
2220  /* Point of 2 in polygon 1 implies zero distance */
2221  p = getPoint2d_cp(lwpoly1->rings[0], 0);
2222  if (lwpoly_covers_point2d(lwpoly2, p)) return 0.0;
2223 
2224  /* Point of 1 in polygon 2 implies zero distance */
2225  p = getPoint2d_cp(lwpoly2->rings[0], 0);
2226  if (lwpoly_covers_point2d(lwpoly1, p)) return 0.0;
2227 
2228  /* Not contained, so what's the actual distance? */
2229  for (i = 0; i < lwpoly1->nrings; i++)
2230  {
2231  for (j = 0; j < lwpoly2->nrings; j++)
2232  {
2233  double ring_distance =
2235  lwpoly1->rings[i],
2236  lwpoly2->rings[j],
2237  spheroid,
2238  tolerance,
2239  check_intersection);
2240  if (ring_distance < distance)
2241  distance = ring_distance;
2242  if (distance <= tolerance) return distance;
2243  }
2244  }
2245  return distance;
2246  }
2247 
2248  /* Recurse into collections */
2249  if ( lwtype_is_collection(type1) )
2250  {
2251  uint32_t i;
2252  double distance = FLT_MAX;
2253  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
2254 
2255  for ( i = 0; i < col->ngeoms; i++ )
2256  {
2257  double geom_distance = lwgeom_distance_spheroid(
2258  col->geoms[i], lwgeom2, spheroid, tolerance);
2259  if ( geom_distance < distance )
2260  distance = geom_distance;
2261  if ( distance <= tolerance )
2262  return distance;
2263  }
2264  return distance;
2265  }
2266 
2267  /* Recurse into collections */
2268  if ( lwtype_is_collection(type2) )
2269  {
2270  uint32_t i;
2271  double distance = FLT_MAX;
2272  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
2273 
2274  for ( i = 0; i < col->ngeoms; i++ )
2275  {
2276  double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], spheroid, tolerance);
2277  if ( geom_distance < distance )
2278  distance = geom_distance;
2279  if ( distance <= tolerance )
2280  return distance;
2281  }
2282  return distance;
2283  }
2284 
2285 
2286  lwerror("arguments include unsupported geometry type (%s, %s)", lwtype_name(type1), lwtype_name(type1));
2287  return -1.0;
2288 
2289 }
2290 
2291 
2292 int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
2293 {
2294  int type1, type2;
2295  GBOX gbox1, gbox2;
2296  gbox1.flags = gbox2.flags = 0;
2297 
2298  assert(lwgeom1);
2299  assert(lwgeom2);
2300 
2301  type1 = lwgeom1->type;
2302  type2 = lwgeom2->type;
2303 
2304  /* dim(geom2) > dim(geom1) always returns false (because geom2 is bigger) */
2305  if ( (type1 == POINTTYPE && type2 == LINETYPE)
2306  || (type1 == POINTTYPE && type2 == POLYGONTYPE)
2307  || (type1 == LINETYPE && type2 == POLYGONTYPE) )
2308  {
2309  LWDEBUG(4, "dimension of geom2 is bigger than geom1");
2310  return LW_FALSE;
2311  }
2312 
2313  /* Make sure we have boxes */
2314  if ( lwgeom1->bbox )
2315  gbox1 = *(lwgeom1->bbox);
2316  else
2317  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
2318 
2319  /* Make sure we have boxes */
2320  if ( lwgeom2->bbox )
2321  gbox2 = *(lwgeom2->bbox);
2322  else
2323  lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2);
2324 
2325 
2326  /* Handle the polygon/point case */
2327  if ( type1 == POLYGONTYPE && type2 == POINTTYPE )
2328  {
2329  POINT2D pt_to_test;
2330  getPoint2d_p(((LWPOINT*)lwgeom2)->point, 0, &pt_to_test);
2331  return lwpoly_covers_point2d((LWPOLY*)lwgeom1, &pt_to_test);
2332  }
2333  else if ( type1 == POLYGONTYPE && type2 == LINETYPE)
2334  {
2335  return lwpoly_covers_lwline((LWPOLY*)lwgeom1, (LWLINE*)lwgeom2);
2336  }
2337  else if ( type1 == POLYGONTYPE && type2 == POLYGONTYPE)
2338  {
2339  return lwpoly_covers_lwpoly((LWPOLY*)lwgeom1, (LWPOLY*)lwgeom2);
2340  }
2341  else if ( type1 == LINETYPE && type2 == POINTTYPE)
2342  {
2343  return lwline_covers_lwpoint((LWLINE*)lwgeom1, (LWPOINT*)lwgeom2);
2344  }
2345  else if ( type1 == LINETYPE && type2 == LINETYPE)
2346  {
2347  return lwline_covers_lwline((LWLINE*)lwgeom1, (LWLINE*)lwgeom2);
2348  }
2349  else if ( type1 == POINTTYPE && type2 == POINTTYPE)
2350  {
2351  return lwpoint_same((LWPOINT*)lwgeom1, (LWPOINT*)lwgeom2);
2352  }
2353 
2354  /* If any of the first argument parts covers the second argument, it's true */
2355  if ( lwtype_is_collection( type1 ) )
2356  {
2357  uint32_t i;
2358  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
2359 
2360  for ( i = 0; i < col->ngeoms; i++ )
2361  {
2362  if ( lwgeom_covers_lwgeom_sphere(col->geoms[i], lwgeom2) )
2363  {
2364  return LW_TRUE;
2365  }
2366  }
2367  return LW_FALSE;
2368  }
2369 
2370  /* Only if all of the second arguments are covered by the first argument is the condition true */
2371  if ( lwtype_is_collection( type2 ) )
2372  {
2373  uint32_t i;
2374  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
2375 
2376  for ( i = 0; i < col->ngeoms; i++ )
2377  {
2378  if ( ! lwgeom_covers_lwgeom_sphere(lwgeom1, col->geoms[i]) )
2379  {
2380  return LW_FALSE;
2381  }
2382  }
2383  return LW_TRUE;
2384  }
2385 
2386  /* Don't get here */
2387  lwerror("lwgeom_covers_lwgeom_sphere: reached end of function without resolution");
2388  return LW_FALSE;
2389 
2390 }
2391 
2397 int lwpoly_covers_point2d(const LWPOLY *poly, const POINT2D *pt_to_test)
2398 {
2399  uint32_t i;
2400  int in_hole_count = 0;
2401  POINT3D p;
2402  GEOGRAPHIC_POINT gpt_to_test;
2403  POINT2D pt_outside;
2404  GBOX gbox;
2405 #if POSTGIS_DEBUG_LEVEL >= 4
2406  char *geom_ewkt;
2407 #endif
2408  gbox.flags = 0;
2409 
2410  /* Nulls and empties don't contain anything! */
2411  if ( ! poly || lwgeom_is_empty((LWGEOM*)poly) )
2412  {
2413  LWDEBUG(4,"returning false, geometry is empty or null");
2414  return LW_FALSE;
2415  }
2416 
2417  /* Make sure we have boxes */
2418  if ( poly->bbox )
2419  gbox = *(poly->bbox);
2420  else
2421  lwgeom_calculate_gbox_geodetic((LWGEOM*)poly, &gbox);
2422 
2423  /* Point not in box? Done! */
2424  geographic_point_init(pt_to_test->x, pt_to_test->y, &gpt_to_test);
2425  geog2cart(&gpt_to_test, &p);
2426  if ( ! gbox_contains_point3d(&gbox, &p) )
2427  {
2428  LWDEBUG(4, "the point is not in the box!");
2429  return LW_FALSE;
2430  }
2431 
2432  /* Calculate our outside point from the gbox */
2433  lwpoly_pt_outside(poly, &pt_outside);
2434 
2435  LWDEBUGF(4, "pt_outside POINT(%.18g %.18g)", pt_outside.x, pt_outside.y);
2436  LWDEBUGF(4, "pt_to_test POINT(%.18g %.18g)", pt_to_test->x, pt_to_test->y);
2437 #if POSTGIS_DEBUG_LEVEL >= 4
2438  geom_ewkt = lwgeom_to_ewkt((LWGEOM*)poly);
2439  LWDEBUGF(4, "polygon %s", geom_ewkt);
2440  lwfree(geom_ewkt);
2441  geom_ewkt = gbox_to_string(&gbox);
2442  LWDEBUGF(4, "gbox %s", geom_ewkt);
2443  lwfree(geom_ewkt);
2444 #endif
2445 
2446  /* Not in outer ring? We're done! */
2447  if ( ! ptarray_contains_point_sphere(poly->rings[0], &pt_outside, pt_to_test) )
2448  {
2449  LWDEBUG(4,"returning false, point is outside ring");
2450  return LW_FALSE;
2451  }
2452 
2453  LWDEBUGF(4, "testing %d rings", poly->nrings);
2454 
2455  /* But maybe point is in a hole... */
2456  for ( i = 1; i < poly->nrings; i++ )
2457  {
2458  LWDEBUGF(4, "ring test loop %d", i);
2459  /* Count up hole containment. Odd => outside boundary. */
2460  if ( ptarray_contains_point_sphere(poly->rings[i], &pt_outside, pt_to_test) )
2461  in_hole_count++;
2462  }
2463 
2464  LWDEBUGF(4, "in_hole_count == %d", in_hole_count);
2465 
2466  if ( in_hole_count % 2 )
2467  {
2468  LWDEBUG(4,"returning false, inner ring containment count is odd");
2469  return LW_FALSE;
2470  }
2471 
2472  LWDEBUG(4,"returning true, inner ring containment count is even");
2473  return LW_TRUE;
2474 }
2475 
2481 int lwpoly_covers_lwpoly(const LWPOLY *poly1, const LWPOLY *poly2)
2482 {
2483  uint32_t i;
2484 
2485  /* Nulls and empties don't contain anything! */
2486  if ( ! poly1 || lwgeom_is_empty((LWGEOM*)poly1) )
2487  {
2488  LWDEBUG(4,"returning false, geometry1 is empty or null");
2489  return LW_FALSE;
2490  }
2491 
2492  /* Nulls and empties don't contain anything! */
2493  if ( ! poly2 || lwgeom_is_empty((LWGEOM*)poly2) )
2494  {
2495  LWDEBUG(4,"returning false, geometry2 is empty or null");
2496  return LW_FALSE;
2497  }
2498 
2499  /* check if all vertices of poly2 are inside poly1 */
2500  for (i = 0; i < poly2->nrings; i++)
2501  {
2502  if (LW_FALSE == lwpoly_covers_pointarray(poly1, poly2->rings[i]))
2503  {
2504  LWDEBUG(4,"returning false, geometry2 has point outside of geometry1");
2505  return LW_FALSE;
2506  }
2507  }
2508 
2509  /* check for any edge intersections, so nothing is partially outside of poly1 */
2510  for (i = 0; i < poly2->nrings; i++)
2511  {
2512  if (LW_TRUE == lwpoly_intersects_line(poly1, poly2->rings[i]))
2513  {
2514  LWDEBUG(4,"returning false, geometry2 is partially outside of geometry1");
2515  return LW_FALSE;
2516  }
2517  }
2518 
2519  /* no abort condition found, so the poly2 should be completely inside poly1 */
2520  return LW_TRUE;
2521 }
2522 
2526 int lwpoly_covers_lwline(const LWPOLY *poly, const LWLINE *line)
2527 {
2528  /* Nulls and empties don't contain anything! */
2529  if ( ! poly || lwgeom_is_empty((LWGEOM*)poly) )
2530  {
2531  LWDEBUG(4,"returning false, geometry1 is empty or null");
2532  return LW_FALSE;
2533  }
2534 
2535  /* Nulls and empties don't contain anything! */
2536  if ( ! line || lwgeom_is_empty((LWGEOM*)line) )
2537  {
2538  LWDEBUG(4,"returning false, geometry2 is empty or null");
2539  return LW_FALSE;
2540  }
2541 
2542  if (LW_FALSE == lwpoly_covers_pointarray(poly, line->points))
2543  {
2544  LWDEBUG(4,"returning false, geometry2 has point outside of geometry1");
2545  return LW_FALSE;
2546  }
2547 
2548  /* check for any edge intersections, so nothing is partially outside of poly1 */
2549  if (LW_TRUE == lwpoly_intersects_line(poly, line->points))
2550  {
2551  LWDEBUG(4,"returning false, geometry2 is partially outside of geometry1");
2552  return LW_FALSE;
2553  }
2554 
2555  /* no abort condition found, so the poly2 should be completely inside poly1 */
2556  return LW_TRUE;
2557 }
2558 
2562 int lwpoly_covers_pointarray(const LWPOLY* lwpoly, const POINTARRAY* pta)
2563 {
2564  uint32_t i;
2565  for (i = 0; i < pta->npoints; i++) {
2566  const POINT2D* pt_to_test = getPoint2d_cp(pta, i);
2567 
2568  if ( LW_FALSE == lwpoly_covers_point2d(lwpoly, pt_to_test) ) {
2569  LWDEBUG(4,"returning false, geometry2 has point outside of geometry1");
2570  return LW_FALSE;
2571  }
2572  }
2573 
2574  return LW_TRUE;
2575 }
2576 
2581 int lwpoly_intersects_line(const LWPOLY* lwpoly, const POINTARRAY* line)
2582 {
2583  uint32_t i, j, k;
2584  POINT3D pa1, pa2, pb1, pb2;
2585  for (i = 0; i < lwpoly->nrings; i++)
2586  {
2587  for (j = 0; j < lwpoly->rings[i]->npoints - 1; j++)
2588  {
2589  const POINT2D* a1 = getPoint2d_cp(lwpoly->rings[i], j);
2590  const POINT2D* a2 = getPoint2d_cp(lwpoly->rings[i], j+1);
2591 
2592  /* Set up our stab line */
2593  ll2cart(a1, &pa1);
2594  ll2cart(a2, &pa2);
2595 
2596  for (k = 0; k < line->npoints - 1; k++)
2597  {
2598  const POINT2D* b1 = getPoint2d_cp(line, k);
2599  const POINT2D* b2 = getPoint2d_cp(line, k+1);
2600 
2601  /* Set up our stab line */
2602  ll2cart(b1, &pb1);
2603  ll2cart(b2, &pb2);
2604 
2605  int inter = edge_intersects(&pa1, &pa2, &pb1, &pb2);
2606 
2607  /* ignore same edges */
2608  if (inter & PIR_INTERSECTS
2609  && !(inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR) )
2610  {
2611  return LW_TRUE;
2612  }
2613  }
2614  }
2615  }
2616 
2617  return LW_FALSE;
2618 }
2619 
2623 int lwline_covers_lwpoint(const LWLINE* lwline, const LWPOINT* lwpoint)
2624 {
2625  uint32_t i;
2626  GEOGRAPHIC_POINT p;
2627  GEOGRAPHIC_EDGE e;
2628 
2629  for ( i = 0; i < lwline->points->npoints - 1; i++)
2630  {
2631  const POINT2D* a1 = getPoint2d_cp(lwline->points, i);
2632  const POINT2D* a2 = getPoint2d_cp(lwline->points, i+1);
2633 
2634  geographic_point_init(a1->x, a1->y, &(e.start));
2635  geographic_point_init(a2->x, a2->y, &(e.end));
2636 
2637  geographic_point_init(lwpoint_get_x(lwpoint), lwpoint_get_y(lwpoint), &p);
2638 
2639  if ( edge_contains_point(&e, &p) ) {
2640  return LW_TRUE;
2641  }
2642  }
2643 
2644  return LW_FALSE;
2645 }
2646 
2652 int lwline_covers_lwline(const LWLINE* lwline1, const LWLINE* lwline2)
2653 {
2654  uint32_t i, j;
2655  GEOGRAPHIC_EDGE e1, e2;
2656  GEOGRAPHIC_POINT p1, p2;
2657  int start = LW_FALSE;
2658  int changed = LW_FALSE;
2659 
2660  /* first point on line */
2661  if ( ! lwline_covers_lwpoint(lwline1, lwline_get_lwpoint(lwline2, 0)))
2662  {
2663  LWDEBUG(4,"returning false, first point of line2 is not covered by line1");
2664  return LW_FALSE;
2665  }
2666 
2667  /* last point on line */
2668  if ( ! lwline_covers_lwpoint(lwline1, lwline_get_lwpoint(lwline2, lwline2->points->npoints - 1)))
2669  {
2670  LWDEBUG(4,"returning false, last point of line2 is not covered by line1");
2671  return LW_FALSE;
2672  }
2673 
2674  j = 0;
2675  i = 0;
2676  while (i < lwline1->points->npoints - 1 && j < lwline2->points->npoints - 1)
2677  {
2678  changed = LW_FALSE;
2679  const POINT2D* a1 = getPoint2d_cp(lwline1->points, i);
2680  const POINT2D* a2 = getPoint2d_cp(lwline1->points, i+1);
2681  const POINT2D* b1 = getPoint2d_cp(lwline2->points, j);
2682  const POINT2D* b2 = getPoint2d_cp(lwline2->points, j+1);
2683 
2684  geographic_point_init(a1->x, a1->y, &(e1.start));
2685  geographic_point_init(a2->x, a2->y, &(e1.end));
2686  geographic_point_init(b1->x, b1->y, &p2);
2687 
2688  /* we already know, that the last point is on line1, so we're done */
2689  if ( j == lwline2->points->npoints - 1)
2690  {
2691  return LW_TRUE;
2692  }
2693  else if (start == LW_TRUE)
2694  {
2695  /* point is on current line1 edge, check next point in line2 */
2696  if ( edge_contains_point(&e1, &p2)) {
2697  j++;
2698  changed = LW_TRUE;
2699  }
2700 
2701  geographic_point_init(a1->x, a1->y, &(e2.start));
2702  geographic_point_init(a2->x, b2->y, &(e2.end));
2703  geographic_point_init(a1->x, a1->y, &p1);
2704 
2705  /* point is on current line2 edge, check next point in line1 */
2706  if ( edge_contains_point(&e2, &p1)) {
2707  i++;
2708  changed = LW_TRUE;
2709  }
2710 
2711  /* no edge progressed -> point left one line */
2712  if ( changed == LW_FALSE )
2713  {
2714  LWDEBUG(4,"returning false, found point not covered by both lines");
2715  return LW_FALSE;
2716  }
2717  else
2718  {
2719  continue;
2720  }
2721  }
2722 
2723  /* find first edge to cover line2 */
2724  if (edge_contains_point(&e1, &p2))
2725  {
2726  start = LW_TRUE;
2727  }
2728 
2729  /* next line1 edge */
2730  i++;
2731  }
2732 
2733  /* no uncovered point found */
2734  return LW_TRUE;
2735 }
2736 
2738 {
2739  uint32_t i;
2740  int first = LW_TRUE;
2741  const POINT2D *p;
2742  POINT3D A1, A2;
2743  GBOX edge_gbox;
2744 
2745  assert(gbox);
2746  assert(pa);
2747 
2748  gbox_init(&edge_gbox);
2749  edge_gbox.flags = gbox->flags;
2750 
2751  if ( pa->npoints == 0 ) return LW_FAILURE;
2752 
2753  if ( pa->npoints == 1 )
2754  {
2755  p = getPoint2d_cp(pa, 0);
2756  ll2cart(p, &A1);
2757  gbox->xmin = gbox->xmax = A1.x;
2758  gbox->ymin = gbox->ymax = A1.y;
2759  gbox->zmin = gbox->zmax = A1.z;
2760  return LW_SUCCESS;
2761  }
2762 
2763  p = getPoint2d_cp(pa, 0);
2764  ll2cart(p, &A1);
2765 
2766  for ( i = 1; i < pa->npoints; i++ )
2767  {
2768 
2769  p = getPoint2d_cp(pa, i);
2770  ll2cart(p, &A2);
2771 
2772  edge_calculate_gbox(&A1, &A2, &edge_gbox);
2773 
2774  /* Initialize the box */
2775  if ( first )
2776  {
2777  gbox_duplicate(&edge_gbox, gbox);
2778  first = LW_FALSE;
2779  }
2780  /* Expand the box where necessary */
2781  else
2782  {
2783  gbox_merge(&edge_gbox, gbox);
2784  }
2785 
2786  A1 = A2;
2787  }
2788 
2789  return LW_SUCCESS;
2790 }
2791 
2792 static int lwpoint_calculate_gbox_geodetic(const LWPOINT *point, GBOX *gbox)
2793 {
2794  assert(point);
2795  return ptarray_calculate_gbox_geodetic(point->point, gbox);
2796 }
2797 
2798 static int lwline_calculate_gbox_geodetic(const LWLINE *line, GBOX *gbox)
2799 {
2800  assert(line);
2801  return ptarray_calculate_gbox_geodetic(line->points, gbox);
2802 }
2803 
2804 static int lwpolygon_calculate_gbox_geodetic(const LWPOLY *poly, GBOX *gbox)
2805 {
2806  GBOX ringbox;
2807  uint32_t i;
2808  int first = LW_TRUE;
2809  assert(poly);
2810  if ( poly->nrings == 0 )
2811  return LW_FAILURE;
2812  ringbox.flags = gbox->flags;
2813  for ( i = 0; i < poly->nrings; i++ )
2814  {
2815  if ( ptarray_calculate_gbox_geodetic(poly->rings[i], &ringbox) == LW_FAILURE )
2816  return LW_FAILURE;
2817  if ( first )
2818  {
2819  gbox_duplicate(&ringbox, gbox);
2820  first = LW_FALSE;
2821  }
2822  else
2823  {
2824  gbox_merge(&ringbox, gbox);
2825  }
2826  }
2827 
2828  /* If the box wraps a poly, push that axis to the absolute min/max as appropriate */
2829  gbox_check_poles(gbox);
2830 
2831  return LW_SUCCESS;
2832 }
2833 
2834 static int lwtriangle_calculate_gbox_geodetic(const LWTRIANGLE *triangle, GBOX *gbox)
2835 {
2836  assert(triangle);
2837  return ptarray_calculate_gbox_geodetic(triangle->points, gbox);
2838 }
2839 
2840 
2842 {
2843  GBOX subbox = {0};
2844  uint32_t i;
2845  int result = LW_FAILURE;
2846  int first = LW_TRUE;
2847  assert(coll);
2848  if ( coll->ngeoms == 0 )
2849  return LW_FAILURE;
2850 
2851  subbox.flags = gbox->flags;
2852 
2853  for ( i = 0; i < coll->ngeoms; i++ )
2854  {
2855  if ( lwgeom_calculate_gbox_geodetic((LWGEOM*)(coll->geoms[i]), &subbox) == LW_SUCCESS )
2856  {
2857  /* Keep a copy of the sub-bounding box for later */
2858  if ( coll->geoms[i]->bbox )
2859  lwfree(coll->geoms[i]->bbox);
2860  coll->geoms[i]->bbox = gbox_copy(&subbox);
2861  if ( first )
2862  {
2863  gbox_duplicate(&subbox, gbox);
2864  first = LW_FALSE;
2865  }
2866  else
2867  {
2868  gbox_merge(&subbox, gbox);
2869  }
2870  result = LW_SUCCESS;
2871  }
2872  }
2873  return result;
2874 }
2875 
2877 {
2878  int result = LW_FAILURE;
2879  LWDEBUGF(4, "got type %d", geom->type);
2880 
2881  /* Add a geodetic flag to the incoming gbox */
2882  gbox->flags = lwflags(FLAGS_GET_Z(geom->flags),FLAGS_GET_M(geom->flags),1);
2883 
2884  switch (geom->type)
2885  {
2886  case POINTTYPE:
2888  break;
2889  case LINETYPE:
2890  result = lwline_calculate_gbox_geodetic((LWLINE *)geom, gbox);
2891  break;
2892  case POLYGONTYPE:
2894  break;
2895  case TRIANGLETYPE:
2897  break;
2898  case MULTIPOINTTYPE:
2899  case MULTILINETYPE:
2900  case MULTIPOLYGONTYPE:
2901  case POLYHEDRALSURFACETYPE:
2902  case TINTYPE:
2903  case COLLECTIONTYPE:
2905  break;
2906  default:
2907  lwerror("lwgeom_calculate_gbox_geodetic: unsupported input geometry type: %d - %s",
2908  geom->type, lwtype_name(geom->type));
2909  break;
2910  }
2911  return result;
2912 }
2913 
2914 
2915 
2916 static int ptarray_check_geodetic(const POINTARRAY *pa)
2917 {
2918  uint32_t t;
2919  POINT2D pt;
2920 
2921  assert(pa);
2922 
2923  for (t=0; t<pa->npoints; t++)
2924  {
2925  getPoint2d_p(pa, t, &pt);
2926  /* printf( "%d (%g, %g)\n", t, pt.x, pt.y); */
2927  if ( pt.x < -180.0 || pt.y < -90.0 || pt.x > 180.0 || pt.y > 90.0 )
2928  return LW_FALSE;
2929  }
2930 
2931  return LW_TRUE;
2932 }
2933 
2934 static int lwpoint_check_geodetic(const LWPOINT *point)
2935 {
2936  assert(point);
2937  return ptarray_check_geodetic(point->point);
2938 }
2939 
2940 static int lwline_check_geodetic(const LWLINE *line)
2941 {
2942  assert(line);
2943  return ptarray_check_geodetic(line->points);
2944 }
2945 
2946 static int lwpoly_check_geodetic(const LWPOLY *poly)
2947 {
2948  uint32_t i = 0;
2949  assert(poly);
2950 
2951  for ( i = 0; i < poly->nrings; i++ )
2952  {
2953  if ( ptarray_check_geodetic(poly->rings[i]) == LW_FALSE )
2954  return LW_FALSE;
2955  }
2956  return LW_TRUE;
2957 }
2958 
2959 static int lwtriangle_check_geodetic(const LWTRIANGLE *triangle)
2960 {
2961  assert(triangle);
2962  return ptarray_check_geodetic(triangle->points);
2963 }
2964 
2965 
2967 {
2968  uint32_t i = 0;
2969  assert(col);
2970 
2971  for ( i = 0; i < col->ngeoms; i++ )
2972  {
2973  if ( lwgeom_check_geodetic(col->geoms[i]) == LW_FALSE )
2974  return LW_FALSE;
2975  }
2976  return LW_TRUE;
2977 }
2978 
2980 {
2981  if ( lwgeom_is_empty(geom) )
2982  return LW_TRUE;
2983 
2984  switch (geom->type)
2985  {
2986  case POINTTYPE:
2987  return lwpoint_check_geodetic((LWPOINT *)geom);
2988  case LINETYPE:
2989  return lwline_check_geodetic((LWLINE *)geom);
2990  case POLYGONTYPE:
2991  return lwpoly_check_geodetic((LWPOLY *)geom);
2992  case TRIANGLETYPE:
2993  return lwtriangle_check_geodetic((LWTRIANGLE *)geom);
2994  case MULTIPOINTTYPE:
2995  case MULTILINETYPE:
2996  case MULTIPOLYGONTYPE:
2997  case POLYHEDRALSURFACETYPE:
2998  case TINTYPE:
2999  case COLLECTIONTYPE:
3000  return lwcollection_check_geodetic((LWCOLLECTION *)geom);
3001  default:
3002  lwerror("lwgeom_check_geodetic: unsupported input geometry type: %d - %s",
3003  geom->type, lwtype_name(geom->type));
3004  }
3005  return LW_FALSE;
3006 }
3007 
3009 {
3010  uint32_t t;
3011  int changed = LW_FALSE;
3012  POINT4D pt;
3013 
3014  assert(pa);
3015 
3016  for ( t=0; t < pa->npoints; t++ )
3017  {
3018  getPoint4d_p(pa, t, &pt);
3019  if ( pt.x < -180.0 || pt.x > 180.0 || pt.y < -90.0 || pt.y > 90.0 )
3020  {
3021  pt.x = longitude_degrees_normalize(pt.x);
3022  pt.y = latitude_degrees_normalize(pt.y);
3023  ptarray_set_point4d(pa, t, &pt);
3024  changed = LW_TRUE;
3025  }
3026  }
3027  return changed;
3028 }
3029 
3031 {
3032  assert(point);
3033  return ptarray_force_geodetic(point->point);
3034 }
3035 
3037 {
3038  assert(line);
3039  return ptarray_force_geodetic(line->points);
3040 }
3041 
3043 {
3044  uint32_t i = 0;
3045  int changed = LW_FALSE;
3046  assert(poly);
3047 
3048  for ( i = 0; i < poly->nrings; i++ )
3049  {
3050  if ( ptarray_force_geodetic(poly->rings[i]) == LW_TRUE )
3051  changed = LW_TRUE;
3052  }
3053  return changed;
3054 }
3055 
3057 {
3058  uint32_t i = 0;
3059  int changed = LW_FALSE;
3060  assert(col);
3061 
3062  for ( i = 0; i < col->ngeoms; i++ )
3063  {
3064  if ( lwgeom_force_geodetic(col->geoms[i]) == LW_TRUE )
3065  changed = LW_TRUE;
3066  }
3067  return changed;
3068 }
3069 
3071 {
3072  switch ( lwgeom_get_type(geom) )
3073  {
3074  case POINTTYPE:
3075  return lwpoint_force_geodetic((LWPOINT *)geom);
3076  case LINETYPE:
3077  return lwline_force_geodetic((LWLINE *)geom);
3078  case POLYGONTYPE:
3079  return lwpoly_force_geodetic((LWPOLY *)geom);
3080  case MULTIPOINTTYPE:
3081  case MULTILINETYPE:
3082  case MULTIPOLYGONTYPE:
3083  case COLLECTIONTYPE:
3084  return lwcollection_force_geodetic((LWCOLLECTION *)geom);
3085  default:
3086  lwerror("unsupported input geometry type: %d", lwgeom_get_type(geom));
3087  }
3088  return LW_FALSE;
3089 }
3090 
3091 
3093 {
3094  GEOGRAPHIC_POINT a, b;
3095  double za = 0.0, zb = 0.0;
3096  POINT4D p;
3097  uint32_t i;
3098  int hasz = LW_FALSE;
3099  double length = 0.0;
3100  double seglength = 0.0;
3101 
3102  /* Return zero on non-sensical inputs */
3103  if ( ! pa || pa->npoints < 2 )
3104  return 0.0;
3105 
3106  /* See if we have a third dimension */
3107  hasz = FLAGS_GET_Z(pa->flags);
3108 
3109  /* Initialize first point */
3110  getPoint4d_p(pa, 0, &p);
3111  geographic_point_init(p.x, p.y, &a);
3112  if ( hasz )
3113  za = p.z;
3114 
3115  /* Loop and sum the length for each segment */
3116  for ( i = 1; i < pa->npoints; i++ )
3117  {
3118  seglength = 0.0;
3119  getPoint4d_p(pa, i, &p);
3120  geographic_point_init(p.x, p.y, &b);
3121  if ( hasz )
3122  zb = p.z;
3123 
3124  /* Special sphere case */
3125  if ( s->a == s->b )
3126  seglength = s->radius * sphere_distance(&a, &b);
3127  /* Spheroid case */
3128  else
3129  seglength = spheroid_distance(&a, &b, s);
3130 
3131  /* Add in the vertical displacement if we're in 3D */
3132  if ( hasz )
3133  seglength = sqrt( (zb-za)*(zb-za) + seglength*seglength );
3134 
3135  /* Add this segment length to the total */
3136  length += seglength;
3137 
3138  /* B gets incremented in the next loop, so we save the value here */
3139  a = b;
3140  za = zb;
3141  }
3142  return length;
3143 }
3144 
3145 double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
3146 {
3147  int type;
3148  uint32_t i = 0;
3149  double length = 0.0;
3150 
3151  assert(geom);
3152 
3153  /* No area in nothing */
3154  if ( lwgeom_is_empty(geom) )
3155  return 0.0;
3156 
3157  type = geom->type;
3158 
3159  if ( type == POINTTYPE || type == MULTIPOINTTYPE )
3160  return 0.0;
3161 
3162  if ( type == LINETYPE )
3163  return ptarray_length_spheroid(((LWLINE*)geom)->points, s);
3164 
3165  if ( type == POLYGONTYPE )
3166  {
3167  LWPOLY *poly = (LWPOLY*)geom;
3168  for ( i = 0; i < poly->nrings; i++ )
3169  {
3170  length += ptarray_length_spheroid(poly->rings[i], s);
3171  }
3172  return length;
3173  }
3174 
3175  if ( type == TRIANGLETYPE )
3176  return ptarray_length_spheroid(((LWTRIANGLE*)geom)->points, s);
3177 
3178  if ( lwtype_is_collection( type ) )
3179  {
3180  LWCOLLECTION *col = (LWCOLLECTION*)geom;
3181 
3182  for ( i = 0; i < col->ngeoms; i++ )
3183  {
3184  length += lwgeom_length_spheroid(col->geoms[i], s);
3185  }
3186  return length;
3187  }
3188 
3189  lwerror("unsupported type passed to lwgeom_length_sphere");
3190  return 0.0;
3191 }
3192 
3199 static int
3201 {
3202 
3203  uint32_t i;
3204  POINT4D p;
3205  int altered = LW_FALSE;
3206  int rv = LW_FALSE;
3207  static double tolerance = 1e-10;
3208 
3209  if ( ! pa )
3210  lwerror("ptarray_nudge_geodetic called with null input");
3211 
3212  for(i = 0; i < pa->npoints; i++ )
3213  {
3214  getPoint4d_p(pa, i, &p);
3215  if ( p.x < -180.0 && (-180.0 - p.x <= tolerance) )
3216  {
3217  p.x = -180.0;
3218  altered = LW_TRUE;
3219  }
3220  if ( p.x > 180.0 && (p.x - 180.0 <= tolerance) )
3221  {
3222  p.x = 180.0;
3223  altered = LW_TRUE;
3224  }
3225  if ( p.y < -90.0 && (-90.0 - p.y <= tolerance) )
3226  {
3227  p.y = -90.0;
3228  altered = LW_TRUE;
3229  }
3230  if ( p.y > 90.0 && (p.y - 90.0 <= tolerance) )
3231  {
3232  p.y = 90.0;
3233  altered = LW_TRUE;
3234  }
3235  if ( altered == LW_TRUE )
3236  {
3237  ptarray_set_point4d(pa, i, &p);
3238  altered = LW_FALSE;
3239  rv = LW_TRUE;
3240  }
3241  }
3242  return rv;
3243 }
3244 
3251 int
3253 {
3254  int type;
3255  uint32_t i = 0;
3256  int rv = LW_FALSE;
3257 
3258  assert(geom);
3259 
3260  /* No points in nothing */
3261  if ( lwgeom_is_empty(geom) )
3262  return LW_FALSE;
3263 
3264  type = geom->type;
3265 
3266  if ( type == POINTTYPE )
3267  return ptarray_nudge_geodetic(((LWPOINT*)geom)->point);
3268 
3269  if ( type == LINETYPE )
3270  return ptarray_nudge_geodetic(((LWLINE*)geom)->points);
3271 
3272  if ( type == POLYGONTYPE )
3273  {
3274  LWPOLY *poly = (LWPOLY*)geom;
3275  for ( i = 0; i < poly->nrings; i++ )
3276  {
3277  int n = ptarray_nudge_geodetic(poly->rings[i]);
3278  rv = (rv == LW_TRUE ? rv : n);
3279  }
3280  return rv;
3281  }
3282 
3283  if ( type == TRIANGLETYPE )
3284  return ptarray_nudge_geodetic(((LWTRIANGLE*)geom)->points);
3285 
3286  if ( lwtype_is_collection( type ) )
3287  {
3288  LWCOLLECTION *col = (LWCOLLECTION*)geom;
3289 
3290  for ( i = 0; i < col->ngeoms; i++ )
3291  {
3292  int n = lwgeom_nudge_geodetic(col->geoms[i]);
3293  rv = (rv == LW_TRUE ? rv : n);
3294  }
3295  return rv;
3296  }
3297 
3298  lwerror("unsupported type (%s) passed to lwgeom_nudge_geodetic", lwtype_name(type));
3299  return rv;
3300 }
3301 
3302 
3306 static int
3307 point_in_cone(const POINT3D *A1, const POINT3D *A2, const POINT3D *P)
3308 {
3309  POINT3D AC; /* Center point of A1/A2 */
3310  double min_similarity, similarity;
3311 
3312  /* Boundary case */
3313  if (point3d_equals(A1, P) || point3d_equals(A2, P))
3314  return LW_TRUE;
3315 
3316  /* The normalized sum bisects the angle between start and end. */
3317  vector_sum(A1, A2, &AC);
3318  normalize(&AC);
3319 
3320  /* The projection of start onto the center defines the minimum similarity */
3321  min_similarity = dot_product(A1, &AC);
3322 
3323  /* If the edge is sufficiently curved, use the dot product test */
3324  if (fabs(1.0 - min_similarity) > 1e-10)
3325  {
3326  /* The projection of candidate p onto the center */
3327  similarity = dot_product(P, &AC);
3328 
3329  /* If the projection of the candidate is larger than */
3330  /* the projection of the start point, the candidate */
3331  /* must be closer to the center than the start, so */
3332  /* therefore inside the cone */
3333  if (similarity > min_similarity)
3334  {
3335  return LW_TRUE;
3336  }
3337  else
3338  {
3339  return LW_FALSE;
3340  }
3341  }
3342  else
3343  {
3344  /* Where the edge is very narrow, the dot product test */
3345  /* fails, but we can use the almost-planar nature of the */
3346  /* problem space then to test if the vector from the */
3347  /* candidate to the start point in a different direction */
3348  /* to the vector from candidate to end point */
3349  /* If so, then candidate is between start and end */
3350  POINT3D PA1, PA2;
3351  vector_difference(P, A1, &PA1);
3352  vector_difference(P, A2, &PA2);
3353  normalize(&PA1);
3354  normalize(&PA2);
3355  if (dot_product(&PA1, &PA2) < 0.0)
3356  {
3357  return LW_TRUE;
3358  }
3359  else
3360  {
3361  return LW_FALSE;
3362  }
3363  }
3364  return LW_FALSE;
3365 }
3366 
3367 
3368 
3373 static int
3374 dot_product_side(const POINT3D *p, const POINT3D *q)
3375 {
3376  double dp = dot_product(p, q);
3377 
3378  if ( FP_IS_ZERO(dp) )
3379  return 0;
3380 
3381  return dp < 0.0 ? -1 : 1;
3382 }
3383 
3388 uint32_t
3389 edge_intersects(const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2)
3390 {
3391  POINT3D AN, BN, VN; /* Normals to plane A and plane B */
3392  double ab_dot;
3393  int a1_side, a2_side, b1_side, b2_side;
3394  int rv = PIR_NO_INTERACT;
3395 
3396  /* Normals to the A-plane and B-plane */
3397  unit_normal(A1, A2, &AN);
3398  unit_normal(B1, B2, &BN);
3399 
3400  /* Are A-plane and B-plane basically the same? */
3401  ab_dot = dot_product(&AN, &BN);
3402 
3403  /*
3404  * https://trac.osgeo.org/postgis/ticket/5765
3405  * Failure because the colinearity check was
3406  * triggering due to an overly loose equality
3407  * check here.
3408  * if ( FP_EQUALS(fabs(ab_dot), 1.0) )
3409  */
3410  if ( 1.0 - fabs(ab_dot) <= 10e-16 )
3411  {
3412  /* Co-linear case */
3413  if ( point_in_cone(A1, A2, B1) || point_in_cone(A1, A2, B2) ||
3414  point_in_cone(B1, B2, A1) || point_in_cone(B1, B2, A2) )
3415  {
3416  rv |= PIR_INTERSECTS;
3417  rv |= PIR_COLINEAR;
3418  }
3419  return rv;
3420  }
3421 
3422  /* What side of plane-A and plane-B do the end points */
3423  /* of A and B fall? */
3424  a1_side = dot_product_side(&BN, A1);
3425  a2_side = dot_product_side(&BN, A2);
3426  b1_side = dot_product_side(&AN, B1);
3427  b2_side = dot_product_side(&AN, B2);
3428 
3429  /* Both ends of A on the same side of plane B. */
3430  if ( a1_side == a2_side && a1_side != 0 )
3431  {
3432  /* No intersection. */
3433  return PIR_NO_INTERACT;
3434  }
3435 
3436  /* Both ends of B on the same side of plane A. */
3437  if ( b1_side == b2_side && b1_side != 0 )
3438  {
3439  /* No intersection. */
3440  return PIR_NO_INTERACT;
3441  }
3442 
3443  /* A straddles B and B straddles A, so... */
3444  if ( a1_side != a2_side && (a1_side + a2_side) == 0 &&
3445  b1_side != b2_side && (b1_side + b2_side) == 0 )
3446  {
3447  /* Have to check if intersection point is inside both arcs */
3448  unit_normal(&AN, &BN, &VN);
3449  if ( point_in_cone(A1, A2, &VN) && point_in_cone(B1, B2, &VN) )
3450  {
3451  return PIR_INTERSECTS;
3452  }
3453 
3454  /* Have to check if intersection point is inside both arcs */
3455  vector_scale(&VN, -1);
3456  if ( point_in_cone(A1, A2, &VN) && point_in_cone(B1, B2, &VN) )
3457  {
3458  return PIR_INTERSECTS;
3459  }
3460 
3461  return PIR_NO_INTERACT;
3462  }
3463 
3464  /* The rest are all intersects variants... */
3465  rv |= PIR_INTERSECTS;
3466 
3467  /* A touches B */
3468  if ( a1_side == 0 )
3469  {
3470  /* Touches at A1, A2 is on what side? */
3471  rv |= (a2_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT);
3472  }
3473  else if ( a2_side == 0 )
3474  {
3475  /* Touches at A2, A1 is on what side? */
3476  rv |= (a1_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT);
3477  }
3478 
3479  /* B touches A */
3480  if ( b1_side == 0 )
3481  {
3482  /* Touches at B1, B2 is on what side? */
3483  rv |= (b2_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT);
3484  }
3485  else if ( b2_side == 0 )
3486  {
3487  /* Touches at B2, B1 is on what side? */
3488  rv |= (b1_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT);
3489  }
3490 
3491  return rv;
3492 }
3493 
3502 int ptarray_contains_point_sphere(const POINTARRAY *pa, const POINT2D *pt_outside, const POINT2D *pt_to_test)
3503 {
3504  POINT3D S1, S2; /* Stab line end points */
3505  POINT3D E1, E2; /* Edge end points (3-space) */
3506  POINT2D p; /* Edge end points (lon/lat) */
3507  uint32_t count = 0, i, inter;
3508 
3509  /* Null input, not enough points for a ring? You ain't closed! */
3510  if ( ! pa || pa->npoints < 4 )
3511  return LW_FALSE;
3512 
3513  /* Set up our stab line */
3514  ll2cart(pt_to_test, &S1);
3515  ll2cart(pt_outside, &S2);
3516 
3517  /* Initialize first point */
3518  getPoint2d_p(pa, 0, &p);
3519  ll2cart(&p, &E1);
3520 
3521  /* Walk every edge and see if the stab line hits it */
3522  for ( i = 1; i < pa->npoints; i++ )
3523  {
3524  LWDEBUGF(4, "testing edge (%d)", i);
3525  LWDEBUGF(4, " start point == POINT(%.12g %.12g)", p.x, p.y);
3526 
3527  /* Read next point. */
3528  getPoint2d_p(pa, i, &p);
3529  ll2cart(&p, &E2);
3530 
3531  /* Skip over too-short edges. */
3532  if ( point3d_equals(&E1, &E2) )
3533  {
3534  continue;
3535  }
3536 
3537  /* Our test point is on an edge end! Point is "in ring" by our definition */
3538  if ( point3d_equals(&S1, &E1) )
3539  {
3540  return LW_TRUE;
3541  }
3542 
3543  /* Calculate relationship between stab line and edge */
3544  inter = edge_intersects(&S1, &S2, &E1, &E2);
3545 
3546  /* We have some kind of interaction... */
3547  if ( inter & PIR_INTERSECTS )
3548  {
3549  /* If the stabline is touching the edge, that implies the test point */
3550  /* is on the edge, so we're done, the point is in (on) the ring. */
3551  if ( (inter & PIR_A_TOUCH_RIGHT) || (inter & PIR_A_TOUCH_LEFT) )
3552  {
3553  return LW_TRUE;
3554  }
3555 
3556  /* It's a touching interaction, disregard all the left-side ones. */
3557  /* It's a co-linear intersection, ignore those. */
3558  if ( inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR )
3559  {
3560  /* Do nothing, to avoid double counts. */
3561  LWDEBUGF(4," edge (%d) crossed, count == %d, disregarding to avoid double count", i, count);
3562  }
3563  else
3564  {
3565  /* Increment crossingn count. */
3566  count++;
3567  LWDEBUGF(4," edge (%d) crossed, count == %d", i, count);
3568  }
3569  }
3570  else
3571  {
3572  LWDEBUGF(4," edge (%d) did not cross", i);
3573  }
3574 
3575  /* Increment to next edge */
3576  E1 = E2;
3577  }
3578 
3579  LWDEBUGF(4,"final count == %d", count);
3580 
3581  /* An odd number of crossings implies containment! */
3582  if ( count % 2 )
3583  {
3584  return LW_TRUE;
3585  }
3586 
3587  return LW_FALSE;
3588 }
3589 
3590 
3591 /*
3592 * Given a geodetic bounding volume, calculate a lon/lat bounding
3593 * box that should contain the original feature that gave rise to
3594 * the geodetic box, in plate-carre space (planar lon/lat).
3595 */
3596 int gbox_geocentric_get_gbox_cartesian(const GBOX *gbox_geocentric, GBOX *gbox_planar)
3597 {
3598  /* Normalized corners of the bounding volume */
3599  POINT3D corners[8];
3600  POINT3D cap_center = {0,0,0};
3601  double furthest_angle = 0.0;
3602  double cap_angle = 0.0;
3603  int all_longitudes = LW_FALSE;
3604  double lon0 = -M_PI, lon1 = M_PI;
3605  double lat0, lat1;
3606  GEOGRAPHIC_POINT cap_center_g;
3607 
3608  if (!gbox_geocentric || !gbox_planar)
3609  {
3610  lwerror("Null pointer passed to %s", __func__);
3611  return LW_FALSE;
3612  }
3613 
3614 #define CORNER_SET(ii, xx, yy, zz) { \
3615  corners[ii].x = gbox_geocentric->xx; \
3616  corners[ii].y = gbox_geocentric->yy; \
3617  corners[ii].z = gbox_geocentric->zz; \
3618  }
3619 
3620  /*
3621  * First find a "centered" vector to serve as the mid-point
3622  * of the input bounding volume.
3623  */
3624  CORNER_SET(0, xmin, ymin, zmin);
3625  CORNER_SET(1, xmax, ymin, zmin);
3626  CORNER_SET(2, xmin, ymax, zmin);
3627  CORNER_SET(3, xmax, ymax, zmin);
3628  CORNER_SET(4, xmin, ymin, zmax);
3629  CORNER_SET(5, xmax, ymin, zmax);
3630  CORNER_SET(6, xmin, ymax, zmax);
3631  CORNER_SET(7, xmax, ymax, zmax);
3632 
3633  /*
3634  * Normalize the volume corners
3635  * and normalize the final vector.
3636  */
3637  for (uint32_t i = 0; i < 8; i++)
3638  {
3639  normalize(&(corners[i]));
3640  cap_center.x += corners[i].x;
3641  cap_center.y += corners[i].y;
3642  cap_center.z += corners[i].z;
3643  }
3644  normalize(&cap_center);
3645 
3646  /*
3647  * Find the volume corner that is furthest from the center,
3648  * and calculate the angle between the center and the corner.
3649  * Now we have a "cap" (center and angle)
3650  */
3651  for (uint32_t i = 0; i < 8; i++)
3652  {
3653  double angle = vector_angle(&cap_center, &(corners[i]));
3654  if (angle > furthest_angle)
3655  furthest_angle = angle;
3656  }
3657  cap_angle = furthest_angle;
3658 
3659  /*
3660  * Calculate the planar box that contains the cap.
3661  * If the cap contains a pole, then we include all longitudes
3662  */
3663  cart2geog(&cap_center, &cap_center_g);
3664 
3665  /* Check whether cap includes the south pole */
3666  lat0 = cap_center_g.lat - cap_angle;
3667  if (lat0 <= -M_PI_2)
3668  {
3669  lat0 = -M_PI_2;
3670  all_longitudes = LW_TRUE;
3671  }
3672 
3673  /* Check whether cap includes the north pole */
3674  lat1 = cap_center_g.lat + cap_angle;
3675  if (lat1 >= M_PI_2)
3676  {
3677  lat1 = M_PI_2;
3678  all_longitudes = LW_TRUE;
3679  }
3680 
3681  if (!all_longitudes)
3682  {
3683  // Compute the range of longitudes covered by the cap. We use the law
3684  // of sines for spherical triangles. Consider the triangle ABC where
3685  // A is the north pole, B is the center of the cap, and C is the point
3686  // of tangency between the cap boundary and a line of longitude. Then
3687  // C is a right angle, and letting a,b,c denote the sides opposite A,B,C,
3688  // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c).
3689  // Here "a" is the cap angle, and "c" is the colatitude (90 degrees
3690  // minus the latitude). This formula also works for negative latitudes.
3691  //
3692  // The formula for sin(a) follows from the relationship h = 1 - cos(a).
3693 
3694  double sin_a = sin(cap_angle);
3695  double sin_c = cos(cap_center_g.lat);
3696  if (sin_a <= sin_c)
3697  {
3698  double angle_A = asin(sin_a / sin_c);
3699  lon0 = remainder(cap_center_g.lon - angle_A, 2 * M_PI);
3700  lon1 = remainder(cap_center_g.lon + angle_A, 2 * M_PI);
3701  }
3702  else
3703  {
3704  lon0 = -M_PI;
3705  lon1 = M_PI;
3706  }
3707  }
3708 
3709  gbox_planar->xmin = rad2deg(lon0);
3710  gbox_planar->ymin = rad2deg(lat0);
3711  gbox_planar->xmax = rad2deg(lon1);
3712  gbox_planar->ymax = rad2deg(lat1);
3713  FLAGS_SET_GEODETIC(gbox_planar->flags, 0);
3714  FLAGS_SET_Z(gbox_planar->flags, 0);
3715  FLAGS_SET_M(gbox_planar->flags, 0);
3716 
3717  return LW_TRUE;
3718 }
3719 
char * s
Definition: cu_in_wkt.c:23
char * r
Definition: cu_in_wkt.c:24
static char * w
Definition: cu_out_twkb.c:25
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:267
int gbox_merge(const GBOX *new_box, GBOX *merge_box)
Update the merged GBOX to be large enough to include itself and the new box.
Definition: gbox.c:257
void gbox_duplicate(const GBOX *original, GBOX *duplicate)
Copy the values of original GBOX into duplicate.
Definition: gbox.c:445
int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt)
Return true if the point is inside the gbox.
Definition: gbox.c:247
int gbox_merge_point3d(const POINT3D *p, GBOX *gbox)
Update the GBOX to be large enough to include itself and the new point.
Definition: gbox.c:228
int gbox_overlaps(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps, LW_FALSE otherwise.
Definition: gbox.c:283
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition: gbox.c:40
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
Definition: gbox.c:438
int gbox_init_point3d(const POINT3D *p, GBOX *gbox)
Initialize a GBOX using the values of the point.
Definition: gbox.c:239
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
Definition: gbox.c:404
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
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
double lwpoint_get_m(const LWPOINT *point)
Definition: lwpoint.c:107
#define LW_FAILURE
Definition: liblwgeom.h:96
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
#define WGS84_RADIUS
Definition: liblwgeom.h:148
#define LW_SUCCESS
Definition: liblwgeom.h:97
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:329
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
double lwpoint_get_x(const LWPOINT *point)
Definition: lwpoint.c:63
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:529
int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa)
Add a ring, allocating extra space if necessary.
Definition: lwpoly.c:247
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition: lwgeom_api.c:342
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:1168
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an allocated string.
Definition: lwgeom.c:565
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
#define TINTYPE
Definition: liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
void lwfree(void *mem)
Definition: lwutil.c:248
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
#define POLYGONTYPE
Definition: liblwgeom.h:104
void spheroid_init(SPHEROID *s, double a, double b)
Initialize a spheroid object for use in geodetic functions.
Definition: lwspheroid.c:39
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:114
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:59
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwcollection.c:92
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:166
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:125
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition: lwgeom.c:491
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE,...
Definition: ptarray.c:147
#define TRIANGLETYPE
Definition: liblwgeom.h:115
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:189
#define FLAGS_SET_GEODETIC(flags, value)
Definition: liblwgeom.h:175
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:233
lwflags_t lwflags(int hasz, int hasm, int geodetic)
Construct a new flags bitmask.
Definition: lwutil.c:477
double lwpoint_get_z(const LWPOINT *point)
Definition: lwpoint.c:89
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
#define FLAGS_SET_M(flags, value)
Definition: liblwgeom.h:173
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
#define FLAGS_SET_Z(flags, value)
Definition: liblwgeom.h:172
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoly.c:161
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:369
#define FLAGS_GET_GEODETIC(flags)
Definition: liblwgeom.h:168
double lwpoint_get_y(const LWPOINT *point)
Definition: lwpoint.c:76
LWPOINT * lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p)
Definition: lwpoint.c:206
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:309
int p4d_same(const POINT4D *p1, const POINT4D *p2)
Definition: lwalgorithm.c:32
int p3d_same(const POINT3D *p1, const POINT3D *p2)
Definition: lwalgorithm.c:41
#define LW_ON_INTERRUPT(x)
#define SIGNUM(n)
Macro that returns: -1 if n < 0, 1 if n > 0, 0 if n == 0.
#define FP_MAX(A, B)
#define FP_MIN(A, B)
#define FP_EQUALS(A, B)
int ptarray_has_z(const POINTARRAY *pa)
Definition: ptarray.c:37
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:70
int ptarray_has_m(const POINTARRAY *pa)
Definition: ptarray.c:44
#define FP_IS_ZERO(A)
char lwpoint_same(const LWPOINT *p1, const LWPOINT *p2)
Definition: lwpoint.c:264
int clairaut_geographic(const GEOGRAPHIC_POINT *start, const GEOGRAPHIC_POINT *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom)
Computes the pole of the great circle disk which is the intersection of the great circle with the lin...
Definition: lwgeodetic.c:1053
static int lwline_check_geodetic(const LWLINE *line)
Definition: lwgeodetic.c:2940
static int lwcollection_check_geodetic(const LWCOLLECTION *col)
Definition: lwgeodetic.c:2966
int gbox_geocentric_get_gbox_cartesian(const GBOX *gbox_geocentric, GBOX *gbox_planar)
Definition: lwgeodetic.c:3596
int lwpoly_covers_point2d(const LWPOLY *poly, const POINT2D *pt_to_test)
Given a polygon (lon/lat decimal degrees) and point (lon/lat decimal degrees) and a guaranteed outsid...
Definition: lwgeodetic.c:2397
void normalize(POINT3D *p)
Normalize to a unit vector.
Definition: lwgeodetic.c:615
static int lwpoly_check_geodetic(const LWPOLY *poly)
Definition: lwgeodetic.c:2946
int lwline_covers_lwpoint(const LWLINE *lwline, const LWPOINT *lwpoint)
return LW_TRUE if any of the line segments covers the point
Definition: lwgeodetic.c:2623
int lwpoly_intersects_line(const LWPOLY *lwpoly, const POINTARRAY *line)
Checks if any edges of lwpoly intersect with the line formed by the pointarray return LW_TRUE if any ...
Definition: lwgeodetic.c:2581
double longitude_radians_normalize(double lon)
Convert a longitude to the range of -PI,PI.
Definition: lwgeodetic.c:50
int clairaut_cartesian(const POINT3D *start, const POINT3D *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom)
Computes the pole of the great circle disk which is the intersection of the great circle with the lin...
Definition: lwgeodetic.c:1028
LWPOINT * lwgeom_project_spheroid(const LWPOINT *r, const SPHEROID *spheroid, double distance, double azimuth)
Calculate a projected point given a source point, a distance and a bearing.
Definition: lwgeodetic.c:1967
void point_shift(GEOGRAPHIC_POINT *p, double shift)
Shift a point around by a number of radians.
Definition: lwgeodetic.c:160
static int lwpoly_force_geodetic(LWPOLY *poly)
Definition: lwgeodetic.c:3042
LWGEOM * lwgeom_segmentize_sphere(const LWGEOM *lwg_in, double max_seg_length)
Create a new, densified geometry where no segment is longer than max_seg_length.
Definition: lwgeodetic.c:1697
double lwgeom_distance_spheroid(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, const SPHEROID *spheroid, double tolerance)
Calculate the distance between two LWGEOMs, using the coordinates are longitude and latitude.
Definition: lwgeodetic.c:2066
static int lwcollection_force_geodetic(LWCOLLECTION *col)
Definition: lwgeodetic.c:3056
static int lwtriangle_check_geodetic(const LWTRIANGLE *triangle)
Definition: lwgeodetic.c:2959
int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside)
Given a unit geocentric gbox, return a lon/lat (degrees) coordinate point point that is guaranteed to...
Definition: lwgeodetic.c:1506
double sphere_distance_cartesian(const POINT3D *s, const POINT3D *e)
Given two unit vectors, calculate their distance apart in radians.
Definition: lwgeodetic.c:919
static int ptarray_force_geodetic(POINTARRAY *pa)
Definition: lwgeodetic.c:3008
void vector_rotate(const POINT3D *v1, const POINT3D *v2, double angle, POINT3D *n)
Rotates v1 through an angle (in radians) within the plane defined by v1/v2, returns the rotated vecto...
Definition: lwgeodetic.c:573
static int lwline_force_geodetic(LWLINE *line)
Definition: lwgeodetic.c:3036
static int lwcollection_calculate_gbox_geodetic(const LWCOLLECTION *coll, GBOX *gbox)
Definition: lwgeodetic.c:2841
static int point_in_cone(const POINT3D *A1, const POINT3D *A2, const POINT3D *P)
Utility function for checking if P is within the cone defined by A1/A2.
Definition: lwgeodetic.c:3307
int lwpoly_covers_lwpoly(const LWPOLY *poly1, const LWPOLY *poly2)
Given a polygon1 check if all points of polygon2 are inside polygon1 and no intersections of the poly...
Definition: lwgeodetic.c:2481
static int gbox_check_poles(GBOX *gbox)
Check to see if this geocentric gbox is wrapped around a pole.
Definition: lwgeodetic.c:316
int lwpoly_covers_pointarray(const LWPOLY *lwpoly, const POINTARRAY *pta)
return LW_TRUE if all points are inside the polygon
Definition: lwgeodetic.c:2562
int ptarray_contains_point_sphere(const POINTARRAY *pa, const POINT2D *pt_outside, const POINT2D *pt_to_test)
This routine returns LW_TRUE if the stabline joining the pt_outside and pt_to_test crosses the ring a...
Definition: lwgeodetic.c:3502
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:2876
static int ptarray_check_geodetic(const POINTARRAY *pa)
Definition: lwgeodetic.c:2916
static POINTARRAY * ptarray_segmentize_sphere(const POINTARRAY *pa_in, double max_seg_length)
Create a new point array with no segment longer than the input segment length (expressed in radians!...
Definition: lwgeodetic.c:1638
static int lwpoint_check_geodetic(const LWPOINT *point)
Definition: lwgeodetic.c:2934
int edge_point_on_plane(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p)
Returns true if the point p is on the great circle plane.
Definition: lwgeodetic.c:723
static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
Definition: lwgeodetic.c:1756
double latitude_radians_normalize(double lat)
Convert a latitude to the range of -PI/2,PI/2.
Definition: lwgeodetic.c:78
void vector_scale(POINT3D *n, double scale)
Scale a vector out by a factor.
Definition: lwgeodetic.c:487
int lwgeom_check_geodetic(const LWGEOM *geom)
Check that coordinates of LWGEOM are all within the geodetic range (-180, -90, 180,...
Definition: lwgeodetic.c:2979
void cart2geog(const POINT3D *p, GEOGRAPHIC_POINT *g)
Convert cartesian coordinates on unit sphere to spherical coordinates.
Definition: lwgeodetic.c:414
void y_to_z(POINT3D *p)
Definition: lwgeodetic.c:658
double gbox_angular_height(const GBOX *gbox)
Returns the angular height (latitudinal span) of the box in radians.
Definition: lwgeodetic.c:188
int lwpoly_covers_lwline(const LWPOLY *poly, const LWLINE *line)
Definition: lwgeodetic.c:2526
int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
Calculate covers predicate for two lwgeoms on the sphere.
Definition: lwgeodetic.c:2292
double gbox_angular_width(const GBOX *gbox)
Returns the angular width (longitudinal span) of the box in radians.
Definition: lwgeodetic.c:215
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:2016
static int lwpoint_force_geodetic(LWPOINT *point)
Definition: lwgeodetic.c:3030
int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside)
Definition: lwgeodetic.c:1481
int sphere_project(const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n)
Given a starting location r, a distance and an azimuth to the new point, compute the location of the ...
Definition: lwgeodetic.c:1268
void ll2cart(const POINT2D *g, POINT3D *p)
Convert lon/lat coordinates to cartesian coordinates on unit sphere.
Definition: lwgeodetic.c:423
static int lwline_calculate_gbox_geodetic(const LWLINE *line, GBOX *gbox)
Definition: lwgeodetic.c:2798
static void normalize2d(POINT2D *p)
Normalize to a unit vector.
Definition: lwgeodetic.c:524
int gbox_geocentric_slow
For testing geodetic bounding box, we have a magic global variable.
Definition: lwgeodetic.c:36
int edge_point_in_cone(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p)
Returns true if the point p is inside the cone defined by the two ends of the edge e.
Definition: lwgeodetic.c:736
double longitude_degrees_normalize(double lon)
Convert a longitude to the range of -180,180.
Definition: lwgeodetic.c:106
double z_to_latitude(double z, int top)
Used in great circle to compute the pole of the great circle.
Definition: lwgeodetic.c:1001
void robust_cross_product(const GEOGRAPHIC_POINT *p, const GEOGRAPHIC_POINT *q, POINT3D *a)
Computes the cross product of two vectors using their lat, lng representations.
Definition: lwgeodetic.c:634
static int lwpoly_pt_outside_hack(const LWPOLY *poly, POINT2D *pt_outside)
Definition: lwgeodetic.c:1441
int ptarray_calculate_gbox_geodetic(const POINTARRAY *pa, GBOX *gbox)
Calculate geodetic (x/y/z) box and add values to gbox.
Definition: lwgeodetic.c:2737
void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
Initialize a geographic point.
Definition: lwgeodetic.c:180
static int dot_product_side(const POINT3D *p, const POINT3D *q)
Utility function for edge_intersects(), signum with a tolerance in determining if the value is zero.
Definition: lwgeodetic.c:3374
double ptarray_length_spheroid(const POINTARRAY *pa, const SPHEROID *s)
Definition: lwgeodetic.c:3092
void unit_normal(const POINT3D *P1, const POINT3D *P2, POINT3D *normal)
Calculates the unit normal to two vectors, trying to avoid problems with over-narrow or over-wide cas...
Definition: lwgeodetic.c:541
int lwgeom_force_geodetic(LWGEOM *geom)
Force coordinates of LWGEOM into geodetic range (-180, -90, 180, 90)
Definition: lwgeodetic.c:3070
static int ptarray_segmentize_sphere_edge_recursive(const POINT3D *p1, const POINT3D *p2, const POINT4D *v1, const POINT4D *v2, double d, double max_seg_length, POINTARRAY *pa)
Definition: lwgeodetic.c:1587
#define CORNER_SET(ii, xx, yy, zz)
static void vector_difference(const POINT3D *a, const POINT3D *b, POINT3D *n)
Calculate the difference of two vectors.
Definition: lwgeodetic.c:476
int lwgeom_nudge_geodetic(LWGEOM *geom)
When features are snapped or sometimes they are just this way, they are very close to the geodetic bo...
Definition: lwgeodetic.c:3252
static void cross_product(const POINT3D *a, const POINT3D *b, POINT3D *n)
Calculate the cross product of two vectors.
Definition: lwgeodetic.c:454
static int edge_point_side(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p)
Returns -1 if the point is to the left of the plane formed by the edge, 1 if the point is to the righ...
Definition: lwgeodetic.c:694
double sphere_distance(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
Given two points on a unit sphere, calculate their distance apart in radians.
Definition: lwgeodetic.c:896
static double dot_product(const POINT3D *p1, const POINT3D *p2)
Convert cartesian coordinates on unit sphere to lon/lat coordinates static void cart2ll(const POINT3D...
Definition: lwgeodetic.c:446
double sphere_direction(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d)
Given two points on a unit sphere, calculate the direction from s to e.
Definition: lwgeodetic.c:927
int edge_calculate_gbox_slow(const GEOGRAPHIC_EDGE *e, GBOX *gbox)
Definition: lwgeodetic.c:1298
int edge_intersection(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *g)
Returns true if an intersection can be calculated, and places it in *g.
Definition: lwgeodetic.c:1079
void vector_sum(const POINT3D *a, const POINT3D *b, POINT3D *n)
Calculate the sum of two vectors.
Definition: lwgeodetic.c:465
static int lwtriangle_calculate_gbox_geodetic(const LWTRIANGLE *triangle, GBOX *gbox)
Definition: lwgeodetic.c:2834
uint32_t edge_intersects(const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2)
Returns non-zero if edges A and B interact.
Definition: lwgeodetic.c:3389
double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
Calculate the geodetic length of a lwgeom on the unit sphere.
Definition: lwgeodetic.c:3145
int edge_calculate_gbox(const POINT3D *A1, const POINT3D *A2, GBOX *gbox)
The magic function, given an edge in spherical coordinates, calculate a 3D bounding box that fully co...
Definition: lwgeodetic.c:1362
double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest)
Definition: lwgeodetic.c:1170
void geog2cart(const GEOGRAPHIC_POINT *g, POINT3D *p)
Convert spherical coordinates to cartesian coordinates on unit sphere.
Definition: lwgeodetic.c:404
int gbox_centroid(const GBOX *gbox, POINT2D *out)
Computes the average(ish) center of the box and returns success.
Definition: lwgeodetic.c:267
static int point3d_equals(const POINT3D *p1, const POINT3D *p2)
Utility function for ptarray_contains_point_sphere()
Definition: lwgeodetic.c:42
int lwline_covers_lwline(const LWLINE *lwline1, const LWLINE *lwline2)
Check if first and last point of line2 are covered by line1 and then each point in between has to be ...
Definition: lwgeodetic.c:2652
int edge_contains_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p)
Returns true if the point p is on the minor edge defined by the end points of e.
Definition: lwgeodetic.c:986
void x_to_z(POINT3D *p)
Definition: lwgeodetic.c:651
double edge_distance_to_edge(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *closest1, GEOGRAPHIC_POINT *closest2)
Calculate the distance between two edges.
Definition: lwgeodetic.c:1223
static int ptarray_nudge_geodetic(POINTARRAY *pa)
When features are snapped or sometimes they are just this way, they are very close to the geodetic bo...
Definition: lwgeodetic.c:3200
int geographic_point_equals(const GEOGRAPHIC_POINT *g1, const GEOGRAPHIC_POINT *g2)
Definition: lwgeodetic.c:170
double lwgeom_azumith_spheroid(const LWPOINT *r, const LWPOINT *s, const SPHEROID *spheroid)
Calculate a bearing (azimuth) given a source and destination point.
Definition: lwgeodetic.c:2032
int crosses_dateline(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
Definition: lwgeodetic.c:666
static int lwpoint_calculate_gbox_geodetic(const LWPOINT *point, GBOX *gbox)
Definition: lwgeodetic.c:2792
double latitude_degrees_normalize(double lat)
Convert a latitude to the range of -90,90.
Definition: lwgeodetic.c:133
double vector_angle(const POINT3D *v1, const POINT3D *v2)
Angle between two unit vectors.
Definition: lwgeodetic.c:505
int edge_contains_coplanar_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p)
True if the longitude of p is within the range of the longitude of the ends of e.
Definition: lwgeodetic.c:783
double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid)
Delegate to the spheroid function with a spherically parameterized spheroid.
Definition: lwgeodetic.c:1950
static int lwpolygon_calculate_gbox_geodetic(const LWPOLY *poly, GBOX *gbox)
Definition: lwgeodetic.c:2804
#define rad2deg(r)
Definition: lwgeodetic.h:81
#define POW2(x)
Definition: lwgeodetic.h:48
#define PIR_A_TOUCH_LEFT
Definition: lwgeodetic.h:91
double spheroid_distance(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid)
Computes the shortest distance along the surface of the spheroid between two points,...
Definition: lwspheroid.c:79
#define PIR_COLINEAR
Definition: lwgeodetic.h:89
#define NAN
Definition: lwgeodetic.h:37
int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g)
Given a location, an azimuth and a distance, computes the location of the projected point.
Definition: lwspheroid.c:128
#define PIR_INTERSECTS
Definition: lwgeodetic.h:88
double spheroid_direction(const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid)
Computes the forward azimuth of the geodesic joining two points on the spheroid, using the inverse ge...
Definition: lwspheroid.c:105
#define deg2rad(d)
Conversion functions.
Definition: lwgeodetic.h:80
#define PIR_A_TOUCH_RIGHT
Definition: lwgeodetic.h:90
#define PIR_B_TOUCH_RIGHT
Definition: lwgeodetic.h:92
#define PIR_B_TOUCH_LEFT
Definition: lwgeodetic.h:93
#define PIR_NO_INTERACT
Bitmask elements for edge_intersects() return value.
Definition: lwgeodetic.h:87
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:97
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:141
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:199
static double distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
int count
Definition: genraster.py:57
type
Definition: ovdump.py:42
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
lwflags_t flags
Definition: liblwgeom.h:353
GEOGRAPHIC_POINT start
Definition: lwgeodetic.h:64
GEOGRAPHIC_POINT end
Definition: lwgeodetic.h:65
Two-point great circle segment from a to b.
Definition: lwgeodetic.h:63
Point in spherical coordinates on the world.
Definition: lwgeodetic.h:54
uint32_t ngeoms
Definition: liblwgeom.h:580
LWGEOM ** geoms
Definition: liblwgeom.h:575
uint8_t type
Definition: liblwgeom.h:462
GBOX * bbox
Definition: liblwgeom.h:458
int32_t srid
Definition: liblwgeom.h:460
lwflags_t flags
Definition: liblwgeom.h:461
POINTARRAY * points
Definition: liblwgeom.h:483
POINTARRAY * point
Definition: liblwgeom.h:471
uint8_t type
Definition: liblwgeom.h:474
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524
GBOX * bbox
Definition: liblwgeom.h:518
POINTARRAY * points
Definition: liblwgeom.h:495
double y
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:390
double z
Definition: liblwgeom.h:402
double x
Definition: liblwgeom.h:402
double y
Definition: liblwgeom.h:402
double m
Definition: liblwgeom.h:414
double x
Definition: liblwgeom.h:414
double z
Definition: liblwgeom.h:414
double y
Definition: liblwgeom.h:414
lwflags_t flags
Definition: liblwgeom.h:431
uint32_t npoints
Definition: liblwgeom.h:427
double radius
Definition: liblwgeom.h:380