PostGIS  2.5.7dev-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 
720 static double
722 {
723  POINT3D normal1, normal2;
724  robust_cross_product(b, a, &normal1);
725  robust_cross_product(b, c, &normal2);
726  normalize(&normal1);
727  normalize(&normal2);
728  return sphere_distance_cartesian(&normal1, &normal2);
729 }
730 
740 static double
742 {
743  double angle_a, angle_b, angle_c;
744  double area_radians = 0.0;
745  int side;
746  GEOGRAPHIC_EDGE e;
747 
748  angle_a = sphere_angle(b,a,c);
749  angle_b = sphere_angle(a,b,c);
750  angle_c = sphere_angle(b,c,a);
751 
752  area_radians = angle_a + angle_b + angle_c - M_PI;
753 
754  /* What's the direction of the B/C edge? */
755  e.start = *a;
756  e.end = *b;
757  side = edge_point_side(&e, c);
758 
759  /* Co-linear points implies no area */
760  if ( side == 0 )
761  return 0.0;
762 
763  /* Add the sign to the area */
764  return side * area_radians;
765 }
766 
767 
768 
776 {
777  int side = edge_point_side(e, p);
778  if ( side == 0 )
779  return LW_TRUE;
780 
781  return LW_FALSE;
782 }
783 
789 {
790  POINT3D vcp, vs, ve, vp;
791  double vs_dot_vcp, vp_dot_vcp;
792  geog2cart(&(e->start), &vs);
793  geog2cart(&(e->end), &ve);
794  /* Antipodal case, everything is inside. */
795  if ( vs.x == -1.0 * ve.x && vs.y == -1.0 * ve.y && vs.z == -1.0 * ve.z )
796  return LW_TRUE;
797  geog2cart(p, &vp);
798  /* The normalized sum bisects the angle between start and end. */
799  vector_sum(&vs, &ve, &vcp);
800  normalize(&vcp);
801  /* The projection of start onto the center defines the minimum similarity */
802  vs_dot_vcp = dot_product(&vs, &vcp);
803  LWDEBUGF(4,"vs_dot_vcp %.19g",vs_dot_vcp);
804  /* The projection of candidate p onto the center */
805  vp_dot_vcp = dot_product(&vp, &vcp);
806  LWDEBUGF(4,"vp_dot_vcp %.19g",vp_dot_vcp);
807  /* If p is more similar than start then p is inside the cone */
808  LWDEBUGF(4,"fabs(vp_dot_vcp - vs_dot_vcp) %.39g",fabs(vp_dot_vcp - vs_dot_vcp));
809 
810  /*
811  ** We want to test that vp_dot_vcp is >= vs_dot_vcp but there are
812  ** numerical stability issues for values that are very very nearly
813  ** equal. Unfortunately there are also values of vp_dot_vcp that are legitimately
814  ** very close to but still less than vs_dot_vcp which we also need to catch.
815  ** The tolerance of 10-17 seems to do the trick on 32-bit and 64-bit architectures,
816  ** for the test cases here.
817  ** However, tuning the tolerance value feels like a dangerous hack.
818  ** Fundamentally, the problem is that this test is so sensitive.
819  */
820 
821  /* 1.1102230246251565404236316680908203125e-16 */
822 
823  if ( vp_dot_vcp > vs_dot_vcp || fabs(vp_dot_vcp - vs_dot_vcp) < 2e-16 )
824  {
825  LWDEBUG(4, "point is in cone");
826  return LW_TRUE;
827  }
828  LWDEBUG(4, "point is not in cone");
829  return LW_FALSE;
830 }
831 
836 {
837  GEOGRAPHIC_EDGE g;
839  double slon = fabs((e->start).lon) + fabs((e->end).lon);
840  double dlon = fabs(fabs((e->start).lon) - fabs((e->end).lon));
841  double slat = (e->start).lat + (e->end).lat;
842 
843  LWDEBUGF(4, "e.start == GPOINT(%.6g %.6g) ", (e->start).lat, (e->start).lon);
844  LWDEBUGF(4, "e.end == GPOINT(%.6g %.6g) ", (e->end).lat, (e->end).lon);
845  LWDEBUGF(4, "p == GPOINT(%.6g %.6g) ", p->lat, p->lon);
846 
847  /* Copy values into working registers */
848  g = *e;
849  q = *p;
850 
851  /* Vertical plane, we need to do this calculation in latitude */
852  if ( FP_EQUALS( g.start.lon, g.end.lon ) )
853  {
854  LWDEBUG(4, "vertical plane, we need to do this calculation in latitude");
855  /* Supposed to be co-planar... */
856  if ( ! FP_EQUALS( q.lon, g.start.lon ) )
857  return LW_FALSE;
858 
859  if ( ( g.start.lat <= q.lat && q.lat <= g.end.lat ) ||
860  ( g.end.lat <= q.lat && q.lat <= g.start.lat ) )
861  {
862  return LW_TRUE;
863  }
864  else
865  {
866  return LW_FALSE;
867  }
868  }
869 
870  /* Over the pole, we need normalize latitude and do this calculation in latitude */
871  if ( FP_EQUALS( slon, M_PI ) && ( SIGNUM(g.start.lon) != SIGNUM(g.end.lon) || FP_EQUALS(dlon, M_PI) ) )
872  {
873  LWDEBUG(4, "over the pole...");
874  /* Antipodal, everything (or nothing?) is inside */
875  if ( FP_EQUALS( slat, 0.0 ) )
876  return LW_TRUE;
877 
878  /* Point *is* the north pole */
879  if ( slat > 0.0 && FP_EQUALS(q.lat, M_PI_2 ) )
880  return LW_TRUE;
881 
882  /* Point *is* the south pole */
883  if ( slat < 0.0 && FP_EQUALS(q.lat, -1.0 * M_PI_2) )
884  return LW_TRUE;
885 
886  LWDEBUG(4, "coplanar?...");
887 
888  /* Supposed to be co-planar... */
889  if ( ! FP_EQUALS( q.lon, g.start.lon ) )
890  return LW_FALSE;
891 
892  LWDEBUG(4, "north or south?...");
893 
894  /* Over north pole, test based on south pole */
895  if ( slat > 0.0 )
896  {
897  LWDEBUG(4, "over the north pole...");
898  if ( q.lat > FP_MIN(g.start.lat, g.end.lat) )
899  return LW_TRUE;
900  else
901  return LW_FALSE;
902  }
903  else
904  /* Over south pole, test based on north pole */
905  {
906  LWDEBUG(4, "over the south pole...");
907  if ( q.lat < FP_MAX(g.start.lat, g.end.lat) )
908  return LW_TRUE;
909  else
910  return LW_FALSE;
911  }
912  }
913 
914  /* Dateline crossing, flip everything to the opposite hemisphere */
915  else if ( slon > M_PI && ( SIGNUM(g.start.lon) != SIGNUM(g.end.lon) ) )
916  {
917  LWDEBUG(4, "crosses dateline, flip longitudes...");
918  if ( g.start.lon > 0.0 )
919  g.start.lon -= M_PI;
920  else
921  g.start.lon += M_PI;
922  if ( g.end.lon > 0.0 )
923  g.end.lon -= M_PI;
924  else
925  g.end.lon += M_PI;
926 
927  if ( q.lon > 0.0 )
928  q.lon -= M_PI;
929  else
930  q.lon += M_PI;
931  }
932 
933  if ( ( g.start.lon <= q.lon && q.lon <= g.end.lon ) ||
934  ( g.end.lon <= q.lon && q.lon <= g.start.lon ) )
935  {
936  LWDEBUG(4, "true, this edge contains point");
937  return LW_TRUE;
938  }
939 
940  LWDEBUG(4, "false, this edge does not contain point");
941  return LW_FALSE;
942 }
943 
944 
949 {
950  double d_lon = e->lon - s->lon;
951  double cos_d_lon = cos(d_lon);
952  double cos_lat_e = cos(e->lat);
953  double sin_lat_e = sin(e->lat);
954  double cos_lat_s = cos(s->lat);
955  double sin_lat_s = sin(s->lat);
956 
957  double a1 = POW2(cos_lat_e * sin(d_lon));
958  double a2 = POW2(cos_lat_s * sin_lat_e - sin_lat_s * cos_lat_e * cos_d_lon);
959  double a = sqrt(a1 + a2);
960  double b = sin_lat_s * sin_lat_e + cos_lat_s * cos_lat_e * cos_d_lon;
961  return atan2(a, b);
962 }
963 
967 double sphere_distance_cartesian(const POINT3D *s, const POINT3D *e)
968 {
969  return acos(FP_MIN(1.0, dot_product(s, e)));
970 }
971 
975 double sphere_direction(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d)
976 {
977  double heading = 0.0;
978  double f;
979 
980  /* Starting from the poles? Special case. */
981  if ( FP_IS_ZERO(cos(s->lat)) )
982  return (s->lat > 0.0) ? M_PI : 0.0;
983 
984  f = (sin(e->lat) - sin(s->lat) * cos(d)) / (sin(d) * cos(s->lat));
985  if ( FP_EQUALS(f, 1.0) )
986  heading = 0.0;
987  else if ( FP_EQUALS(f, -1.0) )
988  heading = M_PI;
989  else if ( fabs(f) > 1.0 )
990  {
991  LWDEBUGF(4, "f = %g", f);
992  heading = acos(f);
993  }
994  else
995  heading = acos(f);
996 
997  if ( sin(e->lon - s->lon) < 0.0 )
998  heading = -1 * heading;
999 
1000  return heading;
1001 }
1002 
1003 #if 0 /* unused */
1015 static double sphere_excess(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c)
1016 {
1017  double a_dist = sphere_distance(b, c);
1018  double b_dist = sphere_distance(c, a);
1019  double c_dist = sphere_distance(a, b);
1020  double hca = sphere_direction(c, a, b_dist);
1021  double hcb = sphere_direction(c, b, a_dist);
1022  double sign = SIGNUM(hcb-hca);
1023  double ss = (a_dist + b_dist + c_dist) / 2.0;
1024  double E = tan(ss/2.0)*tan((ss-a_dist)/2.0)*tan((ss-b_dist)/2.0)*tan((ss-c_dist)/2.0);
1025  return 4.0 * atan(sqrt(fabs(E))) * sign;
1026 }
1027 #endif
1028 
1029 
1035 {
1036  if ( edge_point_in_cone(e, p) && edge_point_on_plane(e, p) )
1037  /* if ( edge_contains_coplanar_point(e, p) && edge_point_on_plane(e, p) ) */
1038  {
1039  LWDEBUG(4, "point is on edge");
1040  return LW_TRUE;
1041  }
1042  LWDEBUG(4, "point is not on edge");
1043  return LW_FALSE;
1044 }
1045 
1049 double z_to_latitude(double z, int top)
1050 {
1051  double sign = SIGNUM(z);
1052  double tlat = acos(z);
1053  LWDEBUGF(4, "inputs: z(%.8g) sign(%.8g) tlat(%.8g)", z, sign, tlat);
1054  if (FP_IS_ZERO(z))
1055  {
1056  if (top) return M_PI_2;
1057  else return -1.0 * M_PI_2;
1058  }
1059  if (fabs(tlat) > M_PI_2 )
1060  {
1061  tlat = sign * (M_PI - fabs(tlat));
1062  }
1063  else
1064  {
1065  tlat = sign * tlat;
1066  }
1067  LWDEBUGF(4, "output: tlat(%.8g)", tlat);
1068  return tlat;
1069 }
1070 
1076 int clairaut_cartesian(const POINT3D *start, const POINT3D *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom)
1077 {
1078  POINT3D t1, t2;
1079  GEOGRAPHIC_POINT vN1, vN2;
1080  LWDEBUG(4,"entering function");
1081  unit_normal(start, end, &t1);
1082  unit_normal(end, start, &t2);
1083  LWDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z);
1084  LWDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z);
1085  cart2geog(&t1, &vN1);
1086  cart2geog(&t2, &vN2);
1087  g_top->lat = z_to_latitude(t1.z,LW_TRUE);
1088  g_top->lon = vN2.lon;
1089  g_bottom->lat = z_to_latitude(t2.z,LW_FALSE);
1090  g_bottom->lon = vN1.lon;
1091  LWDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon);
1092  LWDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon);
1093  return LW_SUCCESS;
1094 }
1095 
1102 {
1103  POINT3D t1, t2;
1104  GEOGRAPHIC_POINT vN1, vN2;
1105  LWDEBUG(4,"entering function");
1106  robust_cross_product(start, end, &t1);
1107  normalize(&t1);
1108  robust_cross_product(end, start, &t2);
1109  normalize(&t2);
1110  LWDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z);
1111  LWDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z);
1112  cart2geog(&t1, &vN1);
1113  cart2geog(&t2, &vN2);
1114  g_top->lat = z_to_latitude(t1.z,LW_TRUE);
1115  g_top->lon = vN2.lon;
1116  g_bottom->lat = z_to_latitude(t2.z,LW_FALSE);
1117  g_bottom->lon = vN1.lon;
1118  LWDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon);
1119  LWDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon);
1120  return LW_SUCCESS;
1121 }
1122 
1128 {
1129  POINT3D ea, eb, v;
1130  LWDEBUGF(4, "e1 start(%.20g %.20g) end(%.20g %.20g)", e1->start.lat, e1->start.lon, e1->end.lat, e1->end.lon);
1131  LWDEBUGF(4, "e2 start(%.20g %.20g) end(%.20g %.20g)", e2->start.lat, e2->start.lon, e2->end.lat, e2->end.lon);
1132 
1133  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));
1134  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));
1135 
1136  if ( geographic_point_equals(&(e1->start), &(e2->start)) )
1137  {
1138  *g = e1->start;
1139  return LW_TRUE;
1140  }
1141  if ( geographic_point_equals(&(e1->end), &(e2->end)) )
1142  {
1143  *g = e1->end;
1144  return LW_TRUE;
1145  }
1146  if ( geographic_point_equals(&(e1->end), &(e2->start)) )
1147  {
1148  *g = e1->end;
1149  return LW_TRUE;
1150  }
1151  if ( geographic_point_equals(&(e1->start), &(e2->end)) )
1152  {
1153  *g = e1->start;
1154  return LW_TRUE;
1155  }
1156 
1157  robust_cross_product(&(e1->start), &(e1->end), &ea);
1158  normalize(&ea);
1159  robust_cross_product(&(e2->start), &(e2->end), &eb);
1160  normalize(&eb);
1161  LWDEBUGF(4, "e1 cross product == POINT(%.12g %.12g %.12g)", ea.x, ea.y, ea.z);
1162  LWDEBUGF(4, "e2 cross product == POINT(%.12g %.12g %.12g)", eb.x, eb.y, eb.z);
1163  LWDEBUGF(4, "fabs(dot_product(ea, eb)) == %.14g", fabs(dot_product(&ea, &eb)));
1164  if ( FP_EQUALS(fabs(dot_product(&ea, &eb)), 1.0) )
1165  {
1166  LWDEBUGF(4, "parallel edges found! dot_product = %.12g", dot_product(&ea, &eb));
1167  /* Parallel (maybe equal) edges! */
1168  /* Hack alert, only returning ONE end of the edge right now, most do better later. */
1169  /* Hack alert #2, returning a value of 2 to indicate a co-linear crossing event. */
1170  if ( edge_contains_point(e1, &(e2->start)) )
1171  {
1172  *g = e2->start;
1173  return 2;
1174  }
1175  if ( edge_contains_point(e1, &(e2->end)) )
1176  {
1177  *g = e2->end;
1178  return 2;
1179  }
1180  if ( edge_contains_point(e2, &(e1->start)) )
1181  {
1182  *g = e1->start;
1183  return 2;
1184  }
1185  if ( edge_contains_point(e2, &(e1->end)) )
1186  {
1187  *g = e1->end;
1188  return 2;
1189  }
1190  }
1191  unit_normal(&ea, &eb, &v);
1192  LWDEBUGF(4, "v == POINT(%.12g %.12g %.12g)", v.x, v.y, v.z);
1193  g->lat = atan2(v.z, sqrt(v.x * v.x + v.y * v.y));
1194  g->lon = atan2(v.y, v.x);
1195  LWDEBUGF(4, "g == GPOINT(%.12g %.12g)", g->lat, g->lon);
1196  LWDEBUGF(4, "g == POINT(%.12g %.12g)", rad2deg(g->lon), rad2deg(g->lat));
1197  if ( edge_contains_point(e1, g) && edge_contains_point(e2, g) )
1198  {
1199  return LW_TRUE;
1200  }
1201  else
1202  {
1203  LWDEBUG(4, "flipping point to other side of sphere");
1204  g->lat = -1.0 * g->lat;
1205  g->lon = g->lon + M_PI;
1206  if ( g->lon > M_PI )
1207  {
1208  g->lon = -1.0 * (2.0 * M_PI - g->lon);
1209  }
1210  if ( edge_contains_point(e1, g) && edge_contains_point(e2, g) )
1211  {
1212  return LW_TRUE;
1213  }
1214  }
1215  return LW_FALSE;
1216 }
1217 
1219 {
1220  double d1 = 1000000000.0, d2, d3, d_nearest;
1221  POINT3D n, p, k;
1222  GEOGRAPHIC_POINT gk, g_nearest;
1223 
1224  /* Zero length edge, */
1225  if ( geographic_point_equals(&(e->start), &(e->end)) )
1226  {
1227  *closest = e->start;
1228  return sphere_distance(&(e->start), gp);
1229  }
1230 
1231  robust_cross_product(&(e->start), &(e->end), &n);
1232  normalize(&n);
1233  geog2cart(gp, &p);
1234  vector_scale(&n, dot_product(&p, &n));
1235  vector_difference(&p, &n, &k);
1236  normalize(&k);
1237  cart2geog(&k, &gk);
1238  if ( edge_contains_point(e, &gk) )
1239  {
1240  d1 = sphere_distance(gp, &gk);
1241  }
1242  d2 = sphere_distance(gp, &(e->start));
1243  d3 = sphere_distance(gp, &(e->end));
1244 
1245  d_nearest = d1;
1246  g_nearest = gk;
1247 
1248  if ( d2 < d_nearest )
1249  {
1250  d_nearest = d2;
1251  g_nearest = e->start;
1252  }
1253  if ( d3 < d_nearest )
1254  {
1255  d_nearest = d3;
1256  g_nearest = e->end;
1257  }
1258  if (closest)
1259  *closest = g_nearest;
1260 
1261  return d_nearest;
1262 }
1263 
1270 {
1271  double d;
1272  GEOGRAPHIC_POINT gcp1s, gcp1e, gcp2s, gcp2e, c1, c2;
1273  double d1s = edge_distance_to_point(e1, &(e2->start), &gcp1s);
1274  double d1e = edge_distance_to_point(e1, &(e2->end), &gcp1e);
1275  double d2s = edge_distance_to_point(e2, &(e1->start), &gcp2s);
1276  double d2e = edge_distance_to_point(e2, &(e1->end), &gcp2e);
1277 
1278  d = d1s;
1279  c1 = gcp1s;
1280  c2 = e2->start;
1281 
1282  if ( d1e < d )
1283  {
1284  d = d1e;
1285  c1 = gcp1e;
1286  c2 = e2->end;
1287  }
1288 
1289  if ( d2s < d )
1290  {
1291  d = d2s;
1292  c1 = e1->start;
1293  c2 = gcp2s;
1294  }
1295 
1296  if ( d2e < d )
1297  {
1298  d = d2e;
1299  c1 = e1->end;
1300  c2 = gcp2e;
1301  }
1302 
1303  if ( closest1 ) *closest1 = c1;
1304  if ( closest2 ) *closest2 = c2;
1305 
1306  return d;
1307 }
1308 
1309 
1314 int sphere_project(const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n)
1315 {
1316  double d = distance;
1317  double lat1 = r->lat;
1318  double lon1 = r->lon;
1319  double lat2, lon2;
1320 
1321  lat2 = asin(sin(lat1)*cos(d) + cos(lat1)*sin(d)*cos(azimuth));
1322 
1323  /* If we're going straight up or straight down, we don't need to calculate the longitude */
1324  /* TODO: this isn't quite true, what if we're going over the pole? */
1325  if ( FP_EQUALS(azimuth, M_PI) || FP_EQUALS(azimuth, 0.0) )
1326  {
1327  lon2 = r->lon;
1328  }
1329  else
1330  {
1331  lon2 = lon1 + atan2(sin(azimuth)*sin(d)*cos(lat1), cos(d)-sin(lat1)*sin(lat2));
1332  }
1333 
1334  if ( isnan(lat2) || isnan(lon2) )
1335  return LW_FAILURE;
1336 
1337  n->lat = lat2;
1338  n->lon = lon2;
1339 
1340  return LW_SUCCESS;
1341 }
1342 
1343 
1345 {
1346  int steps = 1000000;
1347  int i;
1348  double dx, dy, dz;
1349  double distance = sphere_distance(&(e->start), &(e->end));
1350  POINT3D pn, p, start, end;
1351 
1352  /* Edge is zero length, just return the naive box */
1353  if ( FP_IS_ZERO(distance) )
1354  {
1355  LWDEBUG(4, "edge is zero length. returning");
1356  geog2cart(&(e->start), &start);
1357  geog2cart(&(e->end), &end);
1358  gbox_init_point3d(&start, gbox);
1359  gbox_merge_point3d(&end, gbox);
1360  return LW_SUCCESS;
1361  }
1362 
1363  /* Edge is antipodal (one point on each side of the globe),
1364  set the box to contain the whole world and return */
1365  if ( FP_EQUALS(distance, M_PI) )
1366  {
1367  LWDEBUG(4, "edge is antipodal. setting to maximum size box, and returning");
1368  gbox->xmin = gbox->ymin = gbox->zmin = -1.0;
1369  gbox->xmax = gbox->ymax = gbox->zmax = 1.0;
1370  return LW_SUCCESS;
1371  }
1372 
1373  /* Walk along the chord between start and end incrementally,
1374  normalizing at each step. */
1375  geog2cart(&(e->start), &start);
1376  geog2cart(&(e->end), &end);
1377  dx = (end.x - start.x)/steps;
1378  dy = (end.y - start.y)/steps;
1379  dz = (end.z - start.z)/steps;
1380  p = start;
1381  gbox->xmin = gbox->xmax = p.x;
1382  gbox->ymin = gbox->ymax = p.y;
1383  gbox->zmin = gbox->zmax = p.z;
1384  for ( i = 0; i < steps; i++ )
1385  {
1386  p.x += dx;
1387  p.y += dy;
1388  p.z += dz;
1389  pn = p;
1390  normalize(&pn);
1391  gbox_merge_point3d(&pn, gbox);
1392  }
1393  return LW_SUCCESS;
1394 }
1395 
1408 int edge_calculate_gbox(const POINT3D *A1, const POINT3D *A2, GBOX *gbox)
1409 {
1410  POINT2D R1, R2, RX, O;
1411  POINT3D AN, A3;
1412  POINT3D X[6];
1413  int i, o_side;
1414 
1415  /* Initialize the box with the edge end points */
1416  gbox_init_point3d(A1, gbox);
1417  gbox_merge_point3d(A2, gbox);
1418 
1419  /* Zero length edge, just return! */
1420  if ( p3d_same(A1, A2) )
1421  return LW_SUCCESS;
1422 
1423  /* Error out on antipodal edge */
1424  if ( FP_EQUALS(A1->x, -1*A2->x) && FP_EQUALS(A1->y, -1*A2->y) && FP_EQUALS(A1->z, -1*A2->z) )
1425  {
1426  lwerror("Antipodal (180 degrees long) edge detected!");
1427  return LW_FAILURE;
1428  }
1429 
1430  /* Create A3, a vector in the plane of A1/A2, orthogonal to A1 */
1431  unit_normal(A1, A2, &AN);
1432  unit_normal(&AN, A1, &A3);
1433 
1434  /* Project A1 and A2 into the 2-space formed by the plane A1/A3 */
1435  R1.x = 1.0;
1436  R1.y = 0.0;
1437  R2.x = dot_product(A2, A1);
1438  R2.y = dot_product(A2, &A3);
1439 
1440  /* Initialize our 3-space axis points (x+, x-, y+, y-, z+, z-) */
1441  memset(X, 0, sizeof(POINT3D) * 6);
1442  X[0].x = X[2].y = X[4].z = 1.0;
1443  X[1].x = X[3].y = X[5].z = -1.0;
1444 
1445  /* Initialize a 2-space origin point. */
1446  O.x = O.y = 0.0;
1447  /* What side of the line joining R1/R2 is O? */
1448  o_side = lw_segment_side(&R1, &R2, &O);
1449 
1450  /* Add any extrema! */
1451  for ( i = 0; i < 6; i++ )
1452  {
1453  /* Convert 3-space axis points to 2-space unit vectors */
1454  RX.x = dot_product(&(X[i]), A1);
1455  RX.y = dot_product(&(X[i]), &A3);
1456  normalize2d(&RX);
1457 
1458  /* Any axis end on the side of R1/R2 opposite the origin */
1459  /* is an extreme point in the arc, so we add the 3-space */
1460  /* version of the point on R1/R2 to the gbox */
1461  if ( lw_segment_side(&R1, &R2, &RX) != o_side )
1462  {
1463  POINT3D Xn;
1464  Xn.x = RX.x * A1->x + RX.y * A3.x;
1465  Xn.y = RX.x * A1->y + RX.y * A3.y;
1466  Xn.z = RX.x * A1->z + RX.y * A3.z;
1467 
1468  gbox_merge_point3d(&Xn, gbox);
1469  }
1470  }
1471 
1472  return LW_SUCCESS;
1473 }
1474 
1475 /*
1476 * When we have a globe-covering gbox but we still want an outside
1477 * point, we do this Very Bad Hack, which is look at the first two points
1478 * in the ring and then nudge a point to the left of that arc.
1479 * There is an assumption of convexity built in there, as well as that
1480 * the shape doesn't have a sharp reversal in it. It's ugly, but
1481 * it fixes some common cases (large selection polygons) that users
1482 * are generating. At some point all of geodetic needs a clean-room
1483 * rewrite.
1484 * There is also an assumption of CCW exterior ring, which is how the
1485 * GeoJSON spec defined geographic ring orientation.
1486 */
1487 static int lwpoly_pt_outside_hack(const LWPOLY *poly, POINT2D *pt_outside)
1488 {
1489  GEOGRAPHIC_POINT g1, g2, gSum;
1490  POINT4D p1, p2;
1491  POINT3D q1, q2, qMid, qCross, qSum;
1492  POINTARRAY *pa;
1493  if (lwgeom_is_empty((LWGEOM*)poly))
1494  return LW_FAILURE;
1495  if (poly->nrings < 1)
1496  return LW_FAILURE;
1497  pa = poly->rings[0];
1498  if (pa->npoints < 2)
1499  return LW_FAILURE;
1500 
1501  /* First two points of ring */
1502  getPoint4d_p(pa, 0, &p1);
1503  getPoint4d_p(pa, 1, &p2);
1504  /* Convert to XYZ unit vectors */
1505  geographic_point_init(p1.x, p1.y, &g1);
1506  geographic_point_init(p2.x, p2.y, &g2);
1507  geog2cart(&g1, &q1);
1508  geog2cart(&g2, &q2);
1509  /* Mid-point of first two points */
1510  vector_sum(&q1, &q2, &qMid);
1511  normalize(&qMid);
1512  /* Cross product of first two points (perpendicular) */
1513  cross_product(&q1, &q2, &qCross);
1514  normalize(&qCross);
1515  /* Invert it to put it outside, and scale down */
1516  vector_scale(&qCross, -0.2);
1517  /* Project midpoint to the right */
1518  vector_sum(&qMid, &qCross, &qSum);
1519  normalize(&qSum);
1520  /* Convert back to lon/lat */
1521  cart2geog(&qSum, &gSum);
1522  pt_outside->x = rad2deg(gSum.lon);
1523  pt_outside->y = rad2deg(gSum.lat);
1524  return LW_SUCCESS;
1525 }
1526 
1527 int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside)
1528 {
1529  int rv;
1530  /* Make sure we have boxes */
1531  if ( poly->bbox )
1532  {
1533  rv = gbox_pt_outside(poly->bbox, pt_outside);
1534  }
1535  else
1536  {
1537  GBOX gbox;
1538  lwgeom_calculate_gbox_geodetic((LWGEOM*)poly, &gbox);
1539  rv = gbox_pt_outside(&gbox, pt_outside);
1540  }
1541 
1542  if (rv == LW_FALSE)
1543  return lwpoly_pt_outside_hack(poly, pt_outside);
1544 
1545  return rv;
1546 }
1547 
1552 int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside)
1553 {
1554  double grow = M_PI / 180.0 / 60.0; /* one arc-minute */
1555  int i;
1556  GBOX ge;
1557  POINT3D corners[8];
1558  POINT3D pt;
1559  GEOGRAPHIC_POINT g;
1560 
1561  while ( grow < M_PI )
1562  {
1563  /* Assign our box and expand it slightly. */
1564  ge = *gbox;
1565  if ( ge.xmin > -1 ) ge.xmin -= grow;
1566  if ( ge.ymin > -1 ) ge.ymin -= grow;
1567  if ( ge.zmin > -1 ) ge.zmin -= grow;
1568  if ( ge.xmax < 1 ) ge.xmax += grow;
1569  if ( ge.ymax < 1 ) ge.ymax += grow;
1570  if ( ge.zmax < 1 ) ge.zmax += grow;
1571 
1572  /* Build our eight corner points */
1573  corners[0].x = ge.xmin;
1574  corners[0].y = ge.ymin;
1575  corners[0].z = ge.zmin;
1576 
1577  corners[1].x = ge.xmin;
1578  corners[1].y = ge.ymax;
1579  corners[1].z = ge.zmin;
1580 
1581  corners[2].x = ge.xmin;
1582  corners[2].y = ge.ymin;
1583  corners[2].z = ge.zmax;
1584 
1585  corners[3].x = ge.xmax;
1586  corners[3].y = ge.ymin;
1587  corners[3].z = ge.zmin;
1588 
1589  corners[4].x = ge.xmax;
1590  corners[4].y = ge.ymax;
1591  corners[4].z = ge.zmin;
1592 
1593  corners[5].x = ge.xmax;
1594  corners[5].y = ge.ymin;
1595  corners[5].z = ge.zmax;
1596 
1597  corners[6].x = ge.xmin;
1598  corners[6].y = ge.ymax;
1599  corners[6].z = ge.zmax;
1600 
1601  corners[7].x = ge.xmax;
1602  corners[7].y = ge.ymax;
1603  corners[7].z = ge.zmax;
1604 
1605  LWDEBUG(4, "trying to use a box corner point...");
1606  for ( i = 0; i < 8; i++ )
1607  {
1608  normalize(&(corners[i]));
1609  LWDEBUGF(4, "testing corner %d: POINT(%.8g %.8g %.8g)", i, corners[i].x, corners[i].y, corners[i].z);
1610  if ( ! gbox_contains_point3d(gbox, &(corners[i])) )
1611  {
1612  LWDEBUGF(4, "corner %d is outside our gbox", i);
1613  pt = corners[i];
1614  normalize(&pt);
1615  cart2geog(&pt, &g);
1616  pt_outside->x = rad2deg(g.lon);
1617  pt_outside->y = rad2deg(g.lat);
1618  LWDEBUGF(4, "returning POINT(%.8g %.8g) as outside point", pt_outside->x, pt_outside->y);
1619  return LW_SUCCESS;
1620  }
1621  }
1622 
1623  /* Try a wider growth to push the corners outside the original box. */
1624  grow *= 2.0;
1625  }
1626 
1627  /* This should never happen! */
1628  // lwerror("BOOM! Could not generate outside point!");
1629  return LW_FAILURE;
1630 }
1631 
1632 
1634  const POINT3D *p1, const POINT3D *p2, /* 3-space points we are interpolating between */
1635  const POINT4D *v1, const POINT4D *v2, /* real values and z/m values */
1636  double d, double max_seg_length, /* current segment length and segment limit */
1637  POINTARRAY *pa) /* write out results here */
1638 {
1639  GEOGRAPHIC_POINT g;
1640  /* Reached the terminal leaf in recursion. Add */
1641  /* the left-most point to the pointarray here */
1642  /* We recurse down the left side first, so outputs should */
1643  /* end up added to the array in order this way */
1644  if (d <= max_seg_length)
1645  {
1646  POINT4D p;
1647  cart2geog(p1, &g);
1648  p.x = v1->x;
1649  p.y = v1->y;
1650  p.z = v1->z;
1651  p.m = v1->m;
1652  return ptarray_append_point(pa, &p, LW_FALSE);
1653  }
1654  /* Find the mid-point and recurse on the left and then the right */
1655  else
1656  {
1657  /* Calculate mid-point */
1658  POINT3D mid;
1659  mid.x = (p1->x + p2->x) / 2.0;
1660  mid.y = (p1->y + p2->y) / 2.0;
1661  mid.z = (p1->z + p2->z) / 2.0;
1662  normalize(&mid);
1663 
1664  /* Calculate z/m mid-values */
1665  POINT4D midv;
1666  cart2geog(&mid, &g);
1667  midv.x = rad2deg(g.lon);
1668  midv.y = rad2deg(g.lat);
1669  midv.z = (v1->z + v2->z) / 2.0;
1670  midv.m = (v1->m + v2->m) / 2.0;
1671  /* Recurse on the left first */
1672  ptarray_segmentize_sphere_edge_recursive(p1, &mid, v1, &midv, d/2.0, max_seg_length, pa);
1673  ptarray_segmentize_sphere_edge_recursive(&mid, p2, &midv, v2, d/2.0, max_seg_length, pa);
1674  return LW_SUCCESS;
1675  }
1676 }
1677 
1683 static POINTARRAY*
1684 ptarray_segmentize_sphere(const POINTARRAY *pa_in, double max_seg_length)
1685 {
1686  POINTARRAY *pa_out;
1687  int hasz = ptarray_has_z(pa_in);
1688  int hasm = ptarray_has_m(pa_in);
1689  POINT4D p1, p2;
1690  POINT3D q1, q2;
1691  GEOGRAPHIC_POINT g1, g2;
1692  uint32_t i;
1693 
1694  /* Just crap out on crazy input */
1695  if ( ! pa_in )
1696  lwerror("%s: null input pointarray", __func__);
1697  if ( max_seg_length <= 0.0 )
1698  lwerror("%s: maximum segment length must be positive", __func__);
1699 
1700  /* Empty starting array */
1701  pa_out = ptarray_construct_empty(hasz, hasm, pa_in->npoints);
1702 
1703  /* Simple loop per edge */
1704  for (i = 1; i < pa_in->npoints; i++)
1705  {
1706  getPoint4d_p(pa_in, i-1, &p1);
1707  getPoint4d_p(pa_in, i, &p2);
1708  geographic_point_init(p1.x, p1.y, &g1);
1709  geographic_point_init(p2.x, p2.y, &g2);
1710 
1711  /* Skip duplicate points (except in case of 2-point lines!) */
1712  if ((pa_in->npoints > 2) && p4d_same(&p1, &p2))
1713  continue;
1714 
1715  /* How long is this edge? */
1716  double d = sphere_distance(&g1, &g2);
1717 
1718  if (d > max_seg_length)
1719  {
1720  geog2cart(&g1, &q1);
1721  geog2cart(&g2, &q2);
1722  /* 3-d end points, XYZM end point, current edge size, min edge size */
1723  ptarray_segmentize_sphere_edge_recursive(&q1, &q2, &p1, &p2, d, max_seg_length, pa_out);
1724  }
1725  /* If we don't segmentize, we need to add first point manually */
1726  else
1727  {
1728  ptarray_append_point(pa_out, &p1, LW_TRUE);
1729  }
1730  }
1731  /* Always add the last point */
1732  ptarray_append_point(pa_out, &p2, LW_TRUE);
1733  return pa_out;
1734 }
1735 
1742 LWGEOM*
1743 lwgeom_segmentize_sphere(const LWGEOM *lwg_in, double max_seg_length)
1744 {
1745  POINTARRAY *pa_out;
1746  LWLINE *lwline;
1747  LWPOLY *lwpoly_in, *lwpoly_out;
1748  LWCOLLECTION *lwcol_in, *lwcol_out;
1749  uint32_t i;
1750 
1751  /* Reflect NULL */
1752  if ( ! lwg_in )
1753  return NULL;
1754 
1755  /* Clone empty */
1756  if ( lwgeom_is_empty(lwg_in) )
1757  return lwgeom_clone(lwg_in);
1758 
1759  switch (lwg_in->type)
1760  {
1761  case MULTIPOINTTYPE:
1762  case POINTTYPE:
1763  return lwgeom_clone_deep(lwg_in);
1764  break;
1765  case LINETYPE:
1766  lwline = lwgeom_as_lwline(lwg_in);
1767  pa_out = ptarray_segmentize_sphere(lwline->points, max_seg_length);
1768  return lwline_as_lwgeom(lwline_construct(lwg_in->srid, NULL, pa_out));
1769  break;
1770  case POLYGONTYPE:
1771  lwpoly_in = lwgeom_as_lwpoly(lwg_in);
1772  lwpoly_out = lwpoly_construct_empty(lwg_in->srid, lwgeom_has_z(lwg_in), lwgeom_has_m(lwg_in));
1773  for ( i = 0; i < lwpoly_in->nrings; i++ )
1774  {
1775  pa_out = ptarray_segmentize_sphere(lwpoly_in->rings[i], max_seg_length);
1776  lwpoly_add_ring(lwpoly_out, pa_out);
1777  }
1778  return lwpoly_as_lwgeom(lwpoly_out);
1779  break;
1780  case MULTILINETYPE:
1781  case MULTIPOLYGONTYPE:
1782  case COLLECTIONTYPE:
1783  lwcol_in = lwgeom_as_lwcollection(lwg_in);
1784  lwcol_out = lwcollection_construct_empty(lwg_in->type, lwg_in->srid, lwgeom_has_z(lwg_in), lwgeom_has_m(lwg_in));
1785  for ( i = 0; i < lwcol_in->ngeoms; i++ )
1786  {
1787  lwcollection_add_lwgeom(lwcol_out, lwgeom_segmentize_sphere(lwcol_in->geoms[i], max_seg_length));
1788  }
1789  return lwcollection_as_lwgeom(lwcol_out);
1790  break;
1791  default:
1792  lwerror("lwgeom_segmentize_sphere: unsupported input geometry type: %d - %s",
1793  lwg_in->type, lwtype_name(lwg_in->type));
1794  break;
1795  }
1796 
1797  lwerror("lwgeom_segmentize_sphere got to the end of the function, should not happen");
1798  return NULL;
1799 }
1800 
1801 
1806 double
1808 {
1809  uint32_t i;
1810  const POINT2D *p;
1811  GEOGRAPHIC_POINT a, b, c;
1812  double area = 0.0;
1813 
1814  /* Return zero on nonsensical inputs */
1815  if ( ! pa || pa->npoints < 4 )
1816  return 0.0;
1817 
1818  p = getPoint2d_cp(pa, 0);
1819  geographic_point_init(p->x, p->y, &a);
1820  p = getPoint2d_cp(pa, 1);
1821  geographic_point_init(p->x, p->y, &b);
1822 
1823  for ( i = 2; i < pa->npoints-1; i++ )
1824  {
1825  p = getPoint2d_cp(pa, i);
1826  geographic_point_init(p->x, p->y, &c);
1827  area += sphere_signed_area(&a, &b, &c);
1828  b = c;
1829  }
1830 
1831  return fabs(area);
1832 }
1833 
1834 
1835 static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
1836 {
1837  GEOGRAPHIC_EDGE e1, e2;
1838  GEOGRAPHIC_POINT g1, g2;
1839  GEOGRAPHIC_POINT nearest1, nearest2;
1840  POINT3D A1, A2, B1, B2;
1841  const POINT2D *p;
1842  double distance;
1843  uint32_t i, j;
1844  int use_sphere = (s->a == s->b ? 1 : 0);
1845 
1846  /* Make result really big, so that everything will be smaller than it */
1847  distance = FLT_MAX;
1848 
1849  /* Empty point arrays? Return negative */
1850  if ( pa1->npoints == 0 || pa2->npoints == 0 )
1851  return -1.0;
1852 
1853  /* Handle point/point case here */
1854  if ( pa1->npoints == 1 && pa2->npoints == 1 )
1855  {
1856  p = getPoint2d_cp(pa1, 0);
1857  geographic_point_init(p->x, p->y, &g1);
1858  p = getPoint2d_cp(pa2, 0);
1859  geographic_point_init(p->x, p->y, &g2);
1860  /* Sphere special case, axes equal */
1861  distance = s->radius * sphere_distance(&g1, &g2);
1862  if ( use_sphere )
1863  return distance;
1864  /* Below tolerance, actual distance isn't of interest */
1865  else if ( distance < 0.95 * tolerance )
1866  return distance;
1867  /* Close or greater than tolerance, get the real answer to be sure */
1868  else
1869  return spheroid_distance(&g1, &g2, s);
1870  }
1871 
1872  /* Handle point/line case here */
1873  if ( pa1->npoints == 1 || pa2->npoints == 1 )
1874  {
1875  /* Handle one/many case here */
1876  uint32_t i;
1877  const POINTARRAY *pa_one;
1878  const POINTARRAY *pa_many;
1879 
1880  if ( pa1->npoints == 1 )
1881  {
1882  pa_one = pa1;
1883  pa_many = pa2;
1884  }
1885  else
1886  {
1887  pa_one = pa2;
1888  pa_many = pa1;
1889  }
1890 
1891  /* Initialize our point */
1892  p = getPoint2d_cp(pa_one, 0);
1893  geographic_point_init(p->x, p->y, &g1);
1894 
1895  /* Initialize start of line */
1896  p = getPoint2d_cp(pa_many, 0);
1897  geographic_point_init(p->x, p->y, &(e1.start));
1898 
1899  /* Iterate through the edges in our line */
1900  for ( i = 1; i < pa_many->npoints; i++ )
1901  {
1902  double d;
1903  p = getPoint2d_cp(pa_many, i);
1904  geographic_point_init(p->x, p->y, &(e1.end));
1905  /* Get the spherical distance between point and edge */
1906  d = s->radius * edge_distance_to_point(&e1, &g1, &g2);
1907  /* New shortest distance! Record this distance / location */
1908  if ( d < distance )
1909  {
1910  distance = d;
1911  nearest2 = g2;
1912  }
1913  /* We've gotten closer than the tolerance... */
1914  if ( d < tolerance )
1915  {
1916  /* Working on a sphere? The answer is correct, return */
1917  if ( use_sphere )
1918  {
1919  return d;
1920  }
1921  /* Far enough past the tolerance that the spheroid calculation won't change things */
1922  else if ( d < tolerance * 0.95 )
1923  {
1924  return d;
1925  }
1926  /* On a spheroid and near the tolerance? Confirm that we are *actually* closer than tolerance */
1927  else
1928  {
1929  d = spheroid_distance(&g1, &nearest2, s);
1930  /* Yes, closer than tolerance, return! */
1931  if ( d < tolerance )
1932  return d;
1933  }
1934  }
1935  e1.start = e1.end;
1936  }
1937 
1938  /* On sphere, return answer */
1939  if ( use_sphere )
1940  return distance;
1941  /* On spheroid, calculate final answer based on closest approach */
1942  else
1943  return spheroid_distance(&g1, &nearest2, s);
1944 
1945  }
1946 
1947  /* Initialize start of line 1 */
1948  p = getPoint2d_cp(pa1, 0);
1949  geographic_point_init(p->x, p->y, &(e1.start));
1950  geog2cart(&(e1.start), &A1);
1951 
1952 
1953  /* Handle line/line case */
1954  for ( i = 1; i < pa1->npoints; i++ )
1955  {
1956  p = getPoint2d_cp(pa1, i);
1957  geographic_point_init(p->x, p->y, &(e1.end));
1958  geog2cart(&(e1.end), &A2);
1959 
1960  /* Initialize start of line 2 */
1961  p = getPoint2d_cp(pa2, 0);
1962  geographic_point_init(p->x, p->y, &(e2.start));
1963  geog2cart(&(e2.start), &B1);
1964 
1965  for ( j = 1; j < pa2->npoints; j++ )
1966  {
1967  double d;
1968 
1969  p = getPoint2d_cp(pa2, j);
1970  geographic_point_init(p->x, p->y, &(e2.end));
1971  geog2cart(&(e2.end), &B2);
1972 
1973  LWDEBUGF(4, "e1.start == GPOINT(%.6g %.6g) ", e1.start.lat, e1.start.lon);
1974  LWDEBUGF(4, "e1.end == GPOINT(%.6g %.6g) ", e1.end.lat, e1.end.lon);
1975  LWDEBUGF(4, "e2.start == GPOINT(%.6g %.6g) ", e2.start.lat, e2.start.lon);
1976  LWDEBUGF(4, "e2.end == GPOINT(%.6g %.6g) ", e2.end.lat, e2.end.lon);
1977 
1978  if ( check_intersection && edge_intersects(&A1, &A2, &B1, &B2) )
1979  {
1980  LWDEBUG(4,"edge intersection! returning 0.0");
1981  return 0.0;
1982  }
1983  d = s->radius * edge_distance_to_edge(&e1, &e2, &g1, &g2);
1984  LWDEBUGF(4,"got edge_distance_to_edge %.8g", d);
1985 
1986  if ( d < distance )
1987  {
1988  distance = d;
1989  nearest1 = g1;
1990  nearest2 = g2;
1991  }
1992  if ( d < tolerance )
1993  {
1994  if ( use_sphere )
1995  {
1996  return d;
1997  }
1998  else
1999  {
2000  d = spheroid_distance(&nearest1, &nearest2, s);
2001  if ( d < tolerance )
2002  return d;
2003  }
2004  }
2005 
2006  /* Copy end to start to allow a new end value in next iteration */
2007  e2.start = e2.end;
2008  B1 = B2;
2009  }
2010 
2011  /* Copy end to start to allow a new end value in next iteration */
2012  e1.start = e1.end;
2013  A1 = A2;
2014  LW_ON_INTERRUPT(return -1.0);
2015  }
2016  LWDEBUGF(4,"finished all loops, returning %.8g", distance);
2017 
2018  if ( use_sphere )
2019  return distance;
2020  else
2021  return spheroid_distance(&nearest1, &nearest2, s);
2022 }
2023 
2024 
2031 double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid)
2032 {
2033  int type;
2034  double radius2 = spheroid->radius * spheroid->radius;
2035 
2036  assert(lwgeom);
2037 
2038  /* No area in nothing */
2039  if ( lwgeom_is_empty(lwgeom) )
2040  return 0.0;
2041 
2042  /* Read the geometry type number */
2043  type = lwgeom->type;
2044 
2045  /* Anything but polygons and collections returns zero */
2046  if ( ! ( type == POLYGONTYPE || type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE ) )
2047  return 0.0;
2048 
2049  /* Actually calculate area */
2050  if ( type == POLYGONTYPE )
2051  {
2052  LWPOLY *poly = (LWPOLY*)lwgeom;
2053  uint32_t i;
2054  double area = 0.0;
2055 
2056  /* Just in case there's no rings */
2057  if ( poly->nrings < 1 )
2058  return 0.0;
2059 
2060  /* First, the area of the outer ring */
2061  area += radius2 * ptarray_area_sphere(poly->rings[0]);
2062 
2063  /* Subtract areas of inner rings */
2064  for ( i = 1; i < poly->nrings; i++ )
2065  {
2066  area -= radius2 * ptarray_area_sphere(poly->rings[i]);
2067  }
2068  return area;
2069  }
2070 
2071  /* Recurse into sub-geometries to get area */
2072  if ( type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE )
2073  {
2074  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom;
2075  uint32_t i;
2076  double area = 0.0;
2077 
2078  for ( i = 0; i < col->ngeoms; i++ )
2079  {
2080  area += lwgeom_area_sphere(col->geoms[i], spheroid);
2081  }
2082  return area;
2083  }
2084 
2085  /* Shouldn't get here. */
2086  return 0.0;
2087 }
2088 
2089 
2099 LWPOINT* lwgeom_project_spheroid(const LWPOINT *r, const SPHEROID *spheroid, double distance, double azimuth)
2100 {
2101  GEOGRAPHIC_POINT geo_source, geo_dest;
2102  POINT4D pt_dest;
2103  double x, y;
2104  POINTARRAY *pa;
2105  LWPOINT *lwp;
2106 
2107  /* Normalize distance to be positive*/
2108  if ( distance < 0.0 ) {
2109  distance = -distance;
2110  azimuth += M_PI;
2111  }
2112 
2113  /* Normalize azimuth */
2114  azimuth -= 2.0 * M_PI * floor(azimuth / (2.0 * M_PI));
2115 
2116  /* Check the distance validity */
2117  if ( distance > (M_PI * spheroid->radius) )
2118  {
2119  lwerror("Distance must not be greater than %g", M_PI * spheroid->radius);
2120  return NULL;
2121  }
2122 
2123  /* Convert to ta geodetic point */
2124  x = lwpoint_get_x(r);
2125  y = lwpoint_get_y(r);
2126  geographic_point_init(x, y, &geo_source);
2127 
2128  /* Try the projection */
2129  if( spheroid_project(&geo_source, spheroid, distance, azimuth, &geo_dest) == LW_FAILURE )
2130  {
2131  LWDEBUGF(3, "Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance);
2132  lwerror("Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance);
2133  return NULL;
2134  }
2135 
2136  /* Build the output LWPOINT */
2137  pa = ptarray_construct(0, 0, 1);
2138  pt_dest.x = rad2deg(longitude_radians_normalize(geo_dest.lon));
2139  pt_dest.y = rad2deg(latitude_radians_normalize(geo_dest.lat));
2140  pt_dest.z = pt_dest.m = 0.0;
2141  ptarray_set_point4d(pa, 0, &pt_dest);
2142  lwp = lwpoint_construct(r->srid, NULL, pa);
2144  return lwp;
2145 }
2146 
2147 
2156 double lwgeom_azumith_spheroid(const LWPOINT *r, const LWPOINT *s, const SPHEROID *spheroid)
2157 {
2158  GEOGRAPHIC_POINT g1, g2;
2159  double x1, y1, x2, y2;
2160 
2161  /* Convert r to a geodetic point */
2162  x1 = lwpoint_get_x(r);
2163  y1 = lwpoint_get_y(r);
2164  geographic_point_init(x1, y1, &g1);
2165 
2166  /* Convert s to a geodetic point */
2167  x2 = lwpoint_get_x(s);
2168  y2 = lwpoint_get_y(s);
2169  geographic_point_init(x2, y2, &g2);
2170 
2171  /* Same point, return NaN */
2172  if ( FP_EQUALS(x1, x2) && FP_EQUALS(y1, y2) )
2173  {
2174  return NAN;
2175  }
2176 
2177  /* Do the direction calculation */
2178  return spheroid_direction(&g1, &g2, spheroid);
2179 }
2180 
2187 double lwgeom_distance_spheroid(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, const SPHEROID *spheroid, double tolerance)
2188 {
2189  uint8_t type1, type2;
2190  int check_intersection = LW_FALSE;
2191  GBOX gbox1, gbox2;
2192 
2193  gbox_init(&gbox1);
2194  gbox_init(&gbox2);
2195 
2196  assert(lwgeom1);
2197  assert(lwgeom2);
2198 
2199  LWDEBUGF(4, "entered function, tolerance %.8g", tolerance);
2200 
2201  /* What's the distance to an empty geometry? We don't know.
2202  Return a negative number so the caller can catch this case. */
2203  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
2204  {
2205  return -1.0;
2206  }
2207 
2208  type1 = lwgeom1->type;
2209  type2 = lwgeom2->type;
2210 
2211  /* Make sure we have boxes */
2212  if ( lwgeom1->bbox )
2213  gbox1 = *(lwgeom1->bbox);
2214  else
2215  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
2216 
2217  /* Make sure we have boxes */
2218  if ( lwgeom2->bbox )
2219  gbox2 = *(lwgeom2->bbox);
2220  else
2221  lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2);
2222 
2223  /* If the boxes aren't disjoint, we have to check for edge intersections */
2224  if ( gbox_overlaps(&gbox1, &gbox2) )
2225  check_intersection = LW_TRUE;
2226 
2227  /* Point/line combinations can all be handled with simple point array iterations */
2228  if ( ( type1 == POINTTYPE || type1 == LINETYPE ) &&
2229  ( type2 == POINTTYPE || type2 == LINETYPE ) )
2230  {
2231  POINTARRAY *pa1, *pa2;
2232 
2233  if ( type1 == POINTTYPE )
2234  pa1 = ((LWPOINT*)lwgeom1)->point;
2235  else
2236  pa1 = ((LWLINE*)lwgeom1)->points;
2237 
2238  if ( type2 == POINTTYPE )
2239  pa2 = ((LWPOINT*)lwgeom2)->point;
2240  else
2241  pa2 = ((LWLINE*)lwgeom2)->points;
2242 
2243  return ptarray_distance_spheroid(pa1, pa2, spheroid, tolerance, check_intersection);
2244  }
2245 
2246  /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */
2247  if ( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) ||
2248  ( type2 == POLYGONTYPE && type1 == POINTTYPE ) )
2249  {
2250  const POINT2D *p;
2251  LWPOLY *lwpoly;
2252  LWPOINT *lwpt;
2253  double distance = FLT_MAX;
2254  uint32_t i;
2255 
2256  if ( type1 == POINTTYPE )
2257  {
2258  lwpt = (LWPOINT*)lwgeom1;
2259  lwpoly = (LWPOLY*)lwgeom2;
2260  }
2261  else
2262  {
2263  lwpt = (LWPOINT*)lwgeom2;
2264  lwpoly = (LWPOLY*)lwgeom1;
2265  }
2266  p = getPoint2d_cp(lwpt->point, 0);
2267 
2268  /* Point in polygon implies zero distance */
2269  if ( lwpoly_covers_point2d(lwpoly, p) )
2270  {
2271  return 0.0;
2272  }
2273 
2274  /* Not inside, so what's the actual distance? */
2275  for ( i = 0; i < lwpoly->nrings; i++ )
2276  {
2277  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, spheroid, tolerance, check_intersection);
2278  if ( ring_distance < distance )
2279  distance = ring_distance;
2280  if ( distance < tolerance )
2281  return distance;
2282  }
2283  return distance;
2284  }
2285 
2286  /* Line/polygon case, if start point-in-poly, return zero, else return distance. */
2287  if ( ( type1 == POLYGONTYPE && type2 == LINETYPE ) ||
2288  ( type2 == POLYGONTYPE && type1 == LINETYPE ) )
2289  {
2290  const POINT2D *p;
2291  LWPOLY *lwpoly;
2292  LWLINE *lwline;
2293  double distance = FLT_MAX;
2294  uint32_t i;
2295 
2296  if ( type1 == LINETYPE )
2297  {
2298  lwline = (LWLINE*)lwgeom1;
2299  lwpoly = (LWPOLY*)lwgeom2;
2300  }
2301  else
2302  {
2303  lwline = (LWLINE*)lwgeom2;
2304  lwpoly = (LWPOLY*)lwgeom1;
2305  }
2306  p = getPoint2d_cp(lwline->points, 0);
2307 
2308  LWDEBUG(4, "checking if a point of line is in polygon");
2309 
2310  /* Point in polygon implies zero distance */
2311  if ( lwpoly_covers_point2d(lwpoly, p) )
2312  return 0.0;
2313 
2314  LWDEBUG(4, "checking ring distances");
2315 
2316  /* Not contained, so what's the actual distance? */
2317  for ( i = 0; i < lwpoly->nrings; i++ )
2318  {
2319  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, spheroid, tolerance, check_intersection);
2320  LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance);
2321  if ( ring_distance < distance )
2322  distance = ring_distance;
2323  if ( distance < tolerance )
2324  return distance;
2325  }
2326  LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance);
2327  return distance;
2328 
2329  }
2330 
2331  /* Polygon/polygon case, if start point-in-poly, return zero, else
2332  * return distance. */
2333  if (type1 == POLYGONTYPE && type2 == POLYGONTYPE)
2334  {
2335  const POINT2D* p;
2336  LWPOLY* lwpoly1 = (LWPOLY*)lwgeom1;
2337  LWPOLY* lwpoly2 = (LWPOLY*)lwgeom2;
2338  double distance = FLT_MAX;
2339  uint32_t i, j;
2340 
2341  /* Point of 2 in polygon 1 implies zero distance */
2342  p = getPoint2d_cp(lwpoly1->rings[0], 0);
2343  if (lwpoly_covers_point2d(lwpoly2, p)) return 0.0;
2344 
2345  /* Point of 1 in polygon 2 implies zero distance */
2346  p = getPoint2d_cp(lwpoly2->rings[0], 0);
2347  if (lwpoly_covers_point2d(lwpoly1, p)) return 0.0;
2348 
2349  /* Not contained, so what's the actual distance? */
2350  for (i = 0; i < lwpoly1->nrings; i++)
2351  {
2352  for (j = 0; j < lwpoly2->nrings; j++)
2353  {
2354  double ring_distance =
2356  lwpoly1->rings[i],
2357  lwpoly2->rings[j],
2358  spheroid,
2359  tolerance,
2360  check_intersection);
2361  if (ring_distance < distance)
2362  distance = ring_distance;
2363  if (distance < tolerance) return distance;
2364  }
2365  }
2366  return distance;
2367  }
2368 
2369  /* Recurse into collections */
2370  if ( lwtype_is_collection(type1) )
2371  {
2372  uint32_t i;
2373  double distance = FLT_MAX;
2374  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
2375 
2376  for ( i = 0; i < col->ngeoms; i++ )
2377  {
2378  double geom_distance = lwgeom_distance_spheroid(
2379  col->geoms[i], lwgeom2, spheroid, tolerance);
2380  if ( geom_distance < distance )
2381  distance = geom_distance;
2382  if ( distance < tolerance )
2383  return distance;
2384  }
2385  return distance;
2386  }
2387 
2388  /* Recurse into collections */
2389  if ( lwtype_is_collection(type2) )
2390  {
2391  uint32_t i;
2392  double distance = FLT_MAX;
2393  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
2394 
2395  for ( i = 0; i < col->ngeoms; i++ )
2396  {
2397  double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], spheroid, tolerance);
2398  if ( geom_distance < distance )
2399  distance = geom_distance;
2400  if ( distance < tolerance )
2401  return distance;
2402  }
2403  return distance;
2404  }
2405 
2406 
2407  lwerror("arguments include unsupported geometry type (%s, %s)", lwtype_name(type1), lwtype_name(type1));
2408  return -1.0;
2409 
2410 }
2411 
2412 
2413 int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
2414 {
2415  int type1, type2;
2416  GBOX gbox1, gbox2;
2417  gbox1.flags = gbox2.flags = 0;
2418 
2419  assert(lwgeom1);
2420  assert(lwgeom2);
2421 
2422  type1 = lwgeom1->type;
2423  type2 = lwgeom2->type;
2424 
2425  /* dim(geom2) > dim(geom1) always returns false (because geom2 is bigger) */
2426  if ( (type1 == POINTTYPE && type2 == LINETYPE)
2427  || (type1 == POINTTYPE && type2 == POLYGONTYPE)
2428  || (type1 == LINETYPE && type2 == POLYGONTYPE) )
2429  {
2430  LWDEBUG(4, "dimension of geom2 is bigger than geom1");
2431  return LW_FALSE;
2432  }
2433 
2434  /* Make sure we have boxes */
2435  if ( lwgeom1->bbox )
2436  gbox1 = *(lwgeom1->bbox);
2437  else
2438  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
2439 
2440  /* Make sure we have boxes */
2441  if ( lwgeom2->bbox )
2442  gbox2 = *(lwgeom2->bbox);
2443  else
2444  lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2);
2445 
2446 
2447  /* Handle the polygon/point case */
2448  if ( type1 == POLYGONTYPE && type2 == POINTTYPE )
2449  {
2450  POINT2D pt_to_test;
2451  getPoint2d_p(((LWPOINT*)lwgeom2)->point, 0, &pt_to_test);
2452  return lwpoly_covers_point2d((LWPOLY*)lwgeom1, &pt_to_test);
2453  }
2454  else if ( type1 == POLYGONTYPE && type2 == LINETYPE)
2455  {
2456  return lwpoly_covers_lwline((LWPOLY*)lwgeom1, (LWLINE*)lwgeom2);
2457  }
2458  else if ( type1 == POLYGONTYPE && type2 == POLYGONTYPE)
2459  {
2460  return lwpoly_covers_lwpoly((LWPOLY*)lwgeom1, (LWPOLY*)lwgeom2);
2461  }
2462  else if ( type1 == LINETYPE && type2 == POINTTYPE)
2463  {
2464  return lwline_covers_lwpoint((LWLINE*)lwgeom1, (LWPOINT*)lwgeom2);
2465  }
2466  else if ( type1 == LINETYPE && type2 == LINETYPE)
2467  {
2468  return lwline_covers_lwline((LWLINE*)lwgeom1, (LWLINE*)lwgeom2);
2469  }
2470  else if ( type1 == POINTTYPE && type2 == POINTTYPE)
2471  {
2472  return lwpoint_same((LWPOINT*)lwgeom1, (LWPOINT*)lwgeom2);
2473  }
2474 
2475  /* If any of the first argument parts covers the second argument, it's true */
2476  if ( lwtype_is_collection( type1 ) )
2477  {
2478  uint32_t i;
2479  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
2480 
2481  for ( i = 0; i < col->ngeoms; i++ )
2482  {
2483  if ( lwgeom_covers_lwgeom_sphere(col->geoms[i], lwgeom2) )
2484  {
2485  return LW_TRUE;
2486  }
2487  }
2488  return LW_FALSE;
2489  }
2490 
2491  /* Only if all of the second arguments are covered by the first argument is the condition true */
2492  if ( lwtype_is_collection( type2 ) )
2493  {
2494  uint32_t i;
2495  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
2496 
2497  for ( i = 0; i < col->ngeoms; i++ )
2498  {
2499  if ( ! lwgeom_covers_lwgeom_sphere(lwgeom1, col->geoms[i]) )
2500  {
2501  return LW_FALSE;
2502  }
2503  }
2504  return LW_TRUE;
2505  }
2506 
2507  /* Don't get here */
2508  lwerror("lwgeom_covers_lwgeom_sphere: reached end of function without resolution");
2509  return LW_FALSE;
2510 
2511 }
2512 
2518 int lwpoly_covers_point2d(const LWPOLY *poly, const POINT2D *pt_to_test)
2519 {
2520  uint32_t i;
2521  int in_hole_count = 0;
2522  POINT3D p;
2523  GEOGRAPHIC_POINT gpt_to_test;
2524  POINT2D pt_outside;
2525  GBOX gbox;
2526 #if POSTGIS_DEBUG_LEVEL >= 4
2527  char *geom_ewkt;
2528 #endif
2529  gbox.flags = 0;
2530 
2531  /* Nulls and empties don't contain anything! */
2532  if ( ! poly || lwgeom_is_empty((LWGEOM*)poly) )
2533  {
2534  LWDEBUG(4,"returning false, geometry is empty or null");
2535  return LW_FALSE;
2536  }
2537 
2538  /* Make sure we have boxes */
2539  if ( poly->bbox )
2540  gbox = *(poly->bbox);
2541  else
2542  lwgeom_calculate_gbox_geodetic((LWGEOM*)poly, &gbox);
2543 
2544  /* Point not in box? Done! */
2545  geographic_point_init(pt_to_test->x, pt_to_test->y, &gpt_to_test);
2546  geog2cart(&gpt_to_test, &p);
2547  if ( ! gbox_contains_point3d(&gbox, &p) )
2548  {
2549  LWDEBUG(4, "the point is not in the box!");
2550  return LW_FALSE;
2551  }
2552 
2553  /* Calculate our outside point from the gbox */
2554  lwpoly_pt_outside(poly, &pt_outside);
2555 
2556  LWDEBUGF(4, "pt_outside POINT(%.18g %.18g)", pt_outside.x, pt_outside.y);
2557  LWDEBUGF(4, "pt_to_test POINT(%.18g %.18g)", pt_to_test->x, pt_to_test->y);
2558 #if POSTGIS_DEBUG_LEVEL >= 4
2559  geom_ewkt = lwgeom_to_ewkt((LWGEOM*)poly);
2560  LWDEBUGF(4, "polygon %s", geom_ewkt);
2561  lwfree(geom_ewkt);
2562  geom_ewkt = gbox_to_string(&gbox);
2563  LWDEBUGF(4, "gbox %s", geom_ewkt);
2564  lwfree(geom_ewkt);
2565 #endif
2566 
2567  /* Not in outer ring? We're done! */
2568  if ( ! ptarray_contains_point_sphere(poly->rings[0], &pt_outside, pt_to_test) )
2569  {
2570  LWDEBUG(4,"returning false, point is outside ring");
2571  return LW_FALSE;
2572  }
2573 
2574  LWDEBUGF(4, "testing %d rings", poly->nrings);
2575 
2576  /* But maybe point is in a hole... */
2577  for ( i = 1; i < poly->nrings; i++ )
2578  {
2579  LWDEBUGF(4, "ring test loop %d", i);
2580  /* Count up hole containment. Odd => outside boundary. */
2581  if ( ptarray_contains_point_sphere(poly->rings[i], &pt_outside, pt_to_test) )
2582  in_hole_count++;
2583  }
2584 
2585  LWDEBUGF(4, "in_hole_count == %d", in_hole_count);
2586 
2587  if ( in_hole_count % 2 )
2588  {
2589  LWDEBUG(4,"returning false, inner ring containment count is odd");
2590  return LW_FALSE;
2591  }
2592 
2593  LWDEBUG(4,"returning true, inner ring containment count is even");
2594  return LW_TRUE;
2595 }
2596 
2602 int lwpoly_covers_lwpoly(const LWPOLY *poly1, const LWPOLY *poly2)
2603 {
2604  uint32_t i;
2605 
2606  /* Nulls and empties don't contain anything! */
2607  if ( ! poly1 || lwgeom_is_empty((LWGEOM*)poly1) )
2608  {
2609  LWDEBUG(4,"returning false, geometry1 is empty or null");
2610  return LW_FALSE;
2611  }
2612 
2613  /* Nulls and empties don't contain anything! */
2614  if ( ! poly2 || lwgeom_is_empty((LWGEOM*)poly2) )
2615  {
2616  LWDEBUG(4,"returning false, geometry2 is empty or null");
2617  return LW_FALSE;
2618  }
2619 
2620  /* check if all vertices of poly2 are inside poly1 */
2621  for (i = 0; i < poly2->nrings; i++)
2622  {
2623 
2624  /* every other ring is a hole, check if point is inside the actual polygon */
2625  if ( i % 2 == 0)
2626  {
2627  if (LW_FALSE == lwpoly_covers_pointarray(poly1, poly2->rings[i]))
2628  {
2629  LWDEBUG(4,"returning false, geometry2 has point outside of geometry1");
2630  return LW_FALSE;
2631  }
2632  }
2633  else
2634  {
2635  if (LW_TRUE == lwpoly_covers_pointarray(poly1, poly2->rings[i]))
2636  {
2637  LWDEBUG(4,"returning false, geometry2 has point inside a hole of geometry1");
2638  return LW_FALSE;
2639  }
2640  }
2641  }
2642 
2643  /* check for any edge intersections, so nothing is partially outside of poly1 */
2644  for (i = 0; i < poly2->nrings; i++)
2645  {
2646  if (LW_TRUE == lwpoly_intersects_line(poly1, poly2->rings[i]))
2647  {
2648  LWDEBUG(4,"returning false, geometry2 is partially outside of geometry1");
2649  return LW_FALSE;
2650  }
2651  }
2652 
2653  /* no abort condition found, so the poly2 should be completly inside poly1 */
2654  return LW_TRUE;
2655 }
2656 
2660 int lwpoly_covers_lwline(const LWPOLY *poly, const LWLINE *line)
2661 {
2662  /* Nulls and empties don't contain anything! */
2663  if ( ! poly || lwgeom_is_empty((LWGEOM*)poly) )
2664  {
2665  LWDEBUG(4,"returning false, geometry1 is empty or null");
2666  return LW_FALSE;
2667  }
2668 
2669  /* Nulls and empties don't contain anything! */
2670  if ( ! line || lwgeom_is_empty((LWGEOM*)line) )
2671  {
2672  LWDEBUG(4,"returning false, geometry2 is empty or null");
2673  return LW_FALSE;
2674  }
2675 
2676  if (LW_FALSE == lwpoly_covers_pointarray(poly, line->points))
2677  {
2678  LWDEBUG(4,"returning false, geometry2 has point outside of geometry1");
2679  return LW_FALSE;
2680  }
2681 
2682  /* check for any edge intersections, so nothing is partially outside of poly1 */
2683  if (LW_TRUE == lwpoly_intersects_line(poly, line->points))
2684  {
2685  LWDEBUG(4,"returning false, geometry2 is partially outside of geometry1");
2686  return LW_FALSE;
2687  }
2688 
2689  /* no abort condition found, so the poly2 should be completely inside poly1 */
2690  return LW_TRUE;
2691 }
2692 
2696 int lwpoly_covers_pointarray(const LWPOLY* lwpoly, const POINTARRAY* pta)
2697 {
2698  uint32_t i;
2699  for (i = 0; i < pta->npoints; i++) {
2700  const POINT2D* pt_to_test = getPoint2d_cp(pta, i);
2701 
2702  if ( LW_FALSE == lwpoly_covers_point2d(lwpoly, pt_to_test) ) {
2703  LWDEBUG(4,"returning false, geometry2 has point outside of geometry1");
2704  return LW_FALSE;
2705  }
2706  }
2707 
2708  return LW_TRUE;
2709 }
2710 
2715 int lwpoly_intersects_line(const LWPOLY* lwpoly, const POINTARRAY* line)
2716 {
2717  uint32_t i, j, k;
2718  POINT3D pa1, pa2, pb1, pb2;
2719  for (i = 0; i < lwpoly->nrings; i++)
2720  {
2721  for (j = 0; j < lwpoly->rings[i]->npoints - 1; j++)
2722  {
2723  const POINT2D* a1 = getPoint2d_cp(lwpoly->rings[i], j);
2724  const POINT2D* a2 = getPoint2d_cp(lwpoly->rings[i], j+1);
2725 
2726  /* Set up our stab line */
2727  ll2cart(a1, &pa1);
2728  ll2cart(a2, &pa2);
2729 
2730  for (k = 0; k < line->npoints - 1; k++)
2731  {
2732  const POINT2D* b1 = getPoint2d_cp(line, k);
2733  const POINT2D* b2 = getPoint2d_cp(line, k+1);
2734 
2735  /* Set up our stab line */
2736  ll2cart(b1, &pb1);
2737  ll2cart(b2, &pb2);
2738 
2739  int inter = edge_intersects(&pa1, &pa2, &pb1, &pb2);
2740 
2741  /* ignore same edges */
2742  if (inter & PIR_INTERSECTS
2743  && !(inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR) )
2744  {
2745  return LW_TRUE;
2746  }
2747  }
2748  }
2749  }
2750 
2751  return LW_FALSE;
2752 }
2753 
2757 int lwline_covers_lwpoint(const LWLINE* lwline, const LWPOINT* lwpoint)
2758 {
2759  uint32_t i;
2760  GEOGRAPHIC_POINT p;
2761  GEOGRAPHIC_EDGE e;
2762 
2763  for ( i = 0; i < lwline->points->npoints - 1; i++)
2764  {
2765  const POINT2D* a1 = getPoint2d_cp(lwline->points, i);
2766  const POINT2D* a2 = getPoint2d_cp(lwline->points, i+1);
2767 
2768  geographic_point_init(a1->x, a1->y, &(e.start));
2769  geographic_point_init(a2->x, a2->y, &(e.end));
2770 
2771  geographic_point_init(lwpoint_get_x(lwpoint), lwpoint_get_y(lwpoint), &p);
2772 
2773  if ( edge_contains_point(&e, &p) ) {
2774  return LW_TRUE;
2775  }
2776  }
2777 
2778  return LW_FALSE;
2779 }
2780 
2786 int lwline_covers_lwline(const LWLINE* lwline1, const LWLINE* lwline2)
2787 {
2788  uint32_t i, j;
2789  GEOGRAPHIC_EDGE e1, e2;
2790  GEOGRAPHIC_POINT p1, p2;
2791  int start = LW_FALSE;
2792  int changed = LW_FALSE;
2793 
2794  /* first point on line */
2795  if ( ! lwline_covers_lwpoint(lwline1, lwline_get_lwpoint(lwline2, 0)))
2796  {
2797  LWDEBUG(4,"returning false, first point of line2 is not covered by line1");
2798  return LW_FALSE;
2799  }
2800 
2801  /* last point on line */
2802  if ( ! lwline_covers_lwpoint(lwline1, lwline_get_lwpoint(lwline2, lwline2->points->npoints - 1)))
2803  {
2804  LWDEBUG(4,"returning false, last point of line2 is not covered by line1");
2805  return LW_FALSE;
2806  }
2807 
2808  j = 0;
2809  i = 0;
2810  while (i < lwline1->points->npoints - 1 && j < lwline2->points->npoints - 1)
2811  {
2812  changed = LW_FALSE;
2813  const POINT2D* a1 = getPoint2d_cp(lwline1->points, i);
2814  const POINT2D* a2 = getPoint2d_cp(lwline1->points, i+1);
2815  const POINT2D* b1 = getPoint2d_cp(lwline2->points, j);
2816  const POINT2D* b2 = getPoint2d_cp(lwline2->points, j+1);
2817 
2818  geographic_point_init(a1->x, a1->y, &(e1.start));
2819  geographic_point_init(a2->x, a2->y, &(e1.end));
2820  geographic_point_init(b1->x, b1->y, &p2);
2821 
2822  /* we already know, that the last point is on line1, so we're done */
2823  if ( j == lwline2->points->npoints - 1)
2824  {
2825  return LW_TRUE;
2826  }
2827  else if (start == LW_TRUE)
2828  {
2829  /* point is on current line1 edge, check next point in line2 */
2830  if ( edge_contains_point(&e1, &p2)) {
2831  j++;
2832  changed = LW_TRUE;
2833  }
2834 
2835  geographic_point_init(a1->x, a1->y, &(e2.start));
2836  geographic_point_init(a2->x, b2->y, &(e2.end));
2837  geographic_point_init(a1->x, a1->y, &p1);
2838 
2839  /* point is on current line2 edge, check next point in line1 */
2840  if ( edge_contains_point(&e2, &p1)) {
2841  i++;
2842  changed = LW_TRUE;
2843  }
2844 
2845  /* no edge progressed -> point left one line */
2846  if ( changed == LW_FALSE )
2847  {
2848  LWDEBUG(4,"returning false, found point not covered by both lines");
2849  return LW_FALSE;
2850  }
2851  else
2852  {
2853  continue;
2854  }
2855  }
2856 
2857  /* find first edge to cover line2 */
2858  if (edge_contains_point(&e1, &p2))
2859  {
2860  start = LW_TRUE;
2861  }
2862 
2863  /* next line1 edge */
2864  i++;
2865  }
2866 
2867  /* no uncovered point found */
2868  return LW_TRUE;
2869 }
2870 
2875 int getPoint2d_p_ro(const POINTARRAY *pa, uint32_t n, POINT2D **point)
2876 {
2877  uint8_t *pa_ptr = NULL;
2878  assert(pa);
2879  assert(n < pa->npoints);
2880 
2881  pa_ptr = getPoint_internal(pa, n);
2882  /* printf( "pa_ptr[0]: %g\n", *((double*)pa_ptr)); */
2883  *point = (POINT2D*)pa_ptr;
2884 
2885  return LW_SUCCESS;
2886 }
2887 
2888 
2890 {
2891  uint32_t i;
2892  int first = LW_TRUE;
2893  const POINT2D *p;
2894  POINT3D A1, A2;
2895  GBOX edge_gbox;
2896 
2897  assert(gbox);
2898  assert(pa);
2899 
2900  gbox_init(&edge_gbox);
2901  edge_gbox.flags = gbox->flags;
2902 
2903  if ( pa->npoints == 0 ) return LW_FAILURE;
2904 
2905  if ( pa->npoints == 1 )
2906  {
2907  p = getPoint2d_cp(pa, 0);
2908  ll2cart(p, &A1);
2909  gbox->xmin = gbox->xmax = A1.x;
2910  gbox->ymin = gbox->ymax = A1.y;
2911  gbox->zmin = gbox->zmax = A1.z;
2912  return LW_SUCCESS;
2913  }
2914 
2915  p = getPoint2d_cp(pa, 0);
2916  ll2cart(p, &A1);
2917 
2918  for ( i = 1; i < pa->npoints; i++ )
2919  {
2920 
2921  p = getPoint2d_cp(pa, i);
2922  ll2cart(p, &A2);
2923 
2924  edge_calculate_gbox(&A1, &A2, &edge_gbox);
2925 
2926  /* Initialize the box */
2927  if ( first )
2928  {
2929  gbox_duplicate(&edge_gbox, gbox);
2930  first = LW_FALSE;
2931  }
2932  /* Expand the box where necessary */
2933  else
2934  {
2935  gbox_merge(&edge_gbox, gbox);
2936  }
2937 
2938  A1 = A2;
2939  }
2940 
2941  return LW_SUCCESS;
2942 }
2943 
2944 static int lwpoint_calculate_gbox_geodetic(const LWPOINT *point, GBOX *gbox)
2945 {
2946  assert(point);
2947  return ptarray_calculate_gbox_geodetic(point->point, gbox);
2948 }
2949 
2950 static int lwline_calculate_gbox_geodetic(const LWLINE *line, GBOX *gbox)
2951 {
2952  assert(line);
2953  return ptarray_calculate_gbox_geodetic(line->points, gbox);
2954 }
2955 
2956 static int lwpolygon_calculate_gbox_geodetic(const LWPOLY *poly, GBOX *gbox)
2957 {
2958  GBOX ringbox;
2959  uint32_t i;
2960  int first = LW_TRUE;
2961  assert(poly);
2962  if ( poly->nrings == 0 )
2963  return LW_FAILURE;
2964  ringbox.flags = gbox->flags;
2965  for ( i = 0; i < poly->nrings; i++ )
2966  {
2967  if ( ptarray_calculate_gbox_geodetic(poly->rings[i], &ringbox) == LW_FAILURE )
2968  return LW_FAILURE;
2969  if ( first )
2970  {
2971  gbox_duplicate(&ringbox, gbox);
2972  first = LW_FALSE;
2973  }
2974  else
2975  {
2976  gbox_merge(&ringbox, gbox);
2977  }
2978  }
2979 
2980  /* If the box wraps a poly, push that axis to the absolute min/max as appropriate */
2981  gbox_check_poles(gbox);
2982 
2983  return LW_SUCCESS;
2984 }
2985 
2986 static int lwtriangle_calculate_gbox_geodetic(const LWTRIANGLE *triangle, GBOX *gbox)
2987 {
2988  assert(triangle);
2989  return ptarray_calculate_gbox_geodetic(triangle->points, gbox);
2990 }
2991 
2992 
2994 {
2995  GBOX subbox;
2996  uint32_t i;
2997  int result = LW_FAILURE;
2998  int first = LW_TRUE;
2999  assert(coll);
3000  if ( coll->ngeoms == 0 )
3001  return LW_FAILURE;
3002 
3003  subbox.flags = gbox->flags;
3004 
3005  for ( i = 0; i < coll->ngeoms; i++ )
3006  {
3007  if ( lwgeom_calculate_gbox_geodetic((LWGEOM*)(coll->geoms[i]), &subbox) == LW_SUCCESS )
3008  {
3009  /* Keep a copy of the sub-bounding box for later */
3010  if ( coll->geoms[i]->bbox )
3011  lwfree(coll->geoms[i]->bbox);
3012  coll->geoms[i]->bbox = gbox_copy(&subbox);
3013  if ( first )
3014  {
3015  gbox_duplicate(&subbox, gbox);
3016  first = LW_FALSE;
3017  }
3018  else
3019  {
3020  gbox_merge(&subbox, gbox);
3021  }
3022  result = LW_SUCCESS;
3023  }
3024  }
3025  return result;
3026 }
3027 
3029 {
3030  int result = LW_FAILURE;
3031  LWDEBUGF(4, "got type %d", geom->type);
3032 
3033  /* Add a geodetic flag to the incoming gbox */
3034  gbox->flags = gflags(FLAGS_GET_Z(geom->flags),FLAGS_GET_M(geom->flags),1);
3035 
3036  switch (geom->type)
3037  {
3038  case POINTTYPE:
3039  result = lwpoint_calculate_gbox_geodetic((LWPOINT*)geom, gbox);
3040  break;
3041  case LINETYPE:
3042  result = lwline_calculate_gbox_geodetic((LWLINE *)geom, gbox);
3043  break;
3044  case POLYGONTYPE:
3045  result = lwpolygon_calculate_gbox_geodetic((LWPOLY *)geom, gbox);
3046  break;
3047  case TRIANGLETYPE:
3048  result = lwtriangle_calculate_gbox_geodetic((LWTRIANGLE *)geom, gbox);
3049  break;
3050  case MULTIPOINTTYPE:
3051  case MULTILINETYPE:
3052  case MULTIPOLYGONTYPE:
3053  case POLYHEDRALSURFACETYPE:
3054  case TINTYPE:
3055  case COLLECTIONTYPE:
3056  result = lwcollection_calculate_gbox_geodetic((LWCOLLECTION *)geom, gbox);
3057  break;
3058  default:
3059  lwerror("lwgeom_calculate_gbox_geodetic: unsupported input geometry type: %d - %s",
3060  geom->type, lwtype_name(geom->type));
3061  break;
3062  }
3063  return result;
3064 }
3065 
3066 
3067 
3068 static int ptarray_check_geodetic(const POINTARRAY *pa)
3069 {
3070  uint32_t t;
3071  POINT2D pt;
3072 
3073  assert(pa);
3074 
3075  for (t=0; t<pa->npoints; t++)
3076  {
3077  getPoint2d_p(pa, t, &pt);
3078  /* printf( "%d (%g, %g)\n", t, pt.x, pt.y); */
3079  if ( pt.x < -180.0 || pt.y < -90.0 || pt.x > 180.0 || pt.y > 90.0 )
3080  return LW_FALSE;
3081  }
3082 
3083  return LW_TRUE;
3084 }
3085 
3086 static int lwpoint_check_geodetic(const LWPOINT *point)
3087 {
3088  assert(point);
3089  return ptarray_check_geodetic(point->point);
3090 }
3091 
3092 static int lwline_check_geodetic(const LWLINE *line)
3093 {
3094  assert(line);
3095  return ptarray_check_geodetic(line->points);
3096 }
3097 
3098 static int lwpoly_check_geodetic(const LWPOLY *poly)
3099 {
3100  uint32_t i = 0;
3101  assert(poly);
3102 
3103  for ( i = 0; i < poly->nrings; i++ )
3104  {
3105  if ( ptarray_check_geodetic(poly->rings[i]) == LW_FALSE )
3106  return LW_FALSE;
3107  }
3108  return LW_TRUE;
3109 }
3110 
3111 static int lwtriangle_check_geodetic(const LWTRIANGLE *triangle)
3112 {
3113  assert(triangle);
3114  return ptarray_check_geodetic(triangle->points);
3115 }
3116 
3117 
3119 {
3120  uint32_t i = 0;
3121  assert(col);
3122 
3123  for ( i = 0; i < col->ngeoms; i++ )
3124  {
3125  if ( lwgeom_check_geodetic(col->geoms[i]) == LW_FALSE )
3126  return LW_FALSE;
3127  }
3128  return LW_TRUE;
3129 }
3130 
3132 {
3133  if ( lwgeom_is_empty(geom) )
3134  return LW_TRUE;
3135 
3136  switch (geom->type)
3137  {
3138  case POINTTYPE:
3139  return lwpoint_check_geodetic((LWPOINT *)geom);
3140  case LINETYPE:
3141  return lwline_check_geodetic((LWLINE *)geom);
3142  case POLYGONTYPE:
3143  return lwpoly_check_geodetic((LWPOLY *)geom);
3144  case TRIANGLETYPE:
3145  return lwtriangle_check_geodetic((LWTRIANGLE *)geom);
3146  case MULTIPOINTTYPE:
3147  case MULTILINETYPE:
3148  case MULTIPOLYGONTYPE:
3149  case POLYHEDRALSURFACETYPE:
3150  case TINTYPE:
3151  case COLLECTIONTYPE:
3152  return lwcollection_check_geodetic((LWCOLLECTION *)geom);
3153  default:
3154  lwerror("lwgeom_check_geodetic: unsupported input geometry type: %d - %s",
3155  geom->type, lwtype_name(geom->type));
3156  }
3157  return LW_FALSE;
3158 }
3159 
3161 {
3162  uint32_t t;
3163  int changed = LW_FALSE;
3164  POINT4D pt;
3165 
3166  assert(pa);
3167 
3168  for ( t=0; t < pa->npoints; t++ )
3169  {
3170  getPoint4d_p(pa, t, &pt);
3171  if ( pt.x < -180.0 || pt.x > 180.0 || pt.y < -90.0 || pt.y > 90.0 )
3172  {
3173  pt.x = longitude_degrees_normalize(pt.x);
3174  pt.y = latitude_degrees_normalize(pt.y);
3175  ptarray_set_point4d(pa, t, &pt);
3176  changed = LW_TRUE;
3177  }
3178  }
3179  return changed;
3180 }
3181 
3183 {
3184  assert(point);
3185  return ptarray_force_geodetic(point->point);
3186 }
3187 
3189 {
3190  assert(line);
3191  return ptarray_force_geodetic(line->points);
3192 }
3193 
3195 {
3196  uint32_t i = 0;
3197  int changed = LW_FALSE;
3198  assert(poly);
3199 
3200  for ( i = 0; i < poly->nrings; i++ )
3201  {
3202  if ( ptarray_force_geodetic(poly->rings[i]) == LW_TRUE )
3203  changed = LW_TRUE;
3204  }
3205  return changed;
3206 }
3207 
3209 {
3210  uint32_t i = 0;
3211  int changed = LW_FALSE;
3212  assert(col);
3213 
3214  for ( i = 0; i < col->ngeoms; i++ )
3215  {
3216  if ( lwgeom_force_geodetic(col->geoms[i]) == LW_TRUE )
3217  changed = LW_TRUE;
3218  }
3219  return changed;
3220 }
3221 
3223 {
3224  switch ( lwgeom_get_type(geom) )
3225  {
3226  case POINTTYPE:
3227  return lwpoint_force_geodetic((LWPOINT *)geom);
3228  case LINETYPE:
3229  return lwline_force_geodetic((LWLINE *)geom);
3230  case POLYGONTYPE:
3231  return lwpoly_force_geodetic((LWPOLY *)geom);
3232  case MULTIPOINTTYPE:
3233  case MULTILINETYPE:
3234  case MULTIPOLYGONTYPE:
3235  case COLLECTIONTYPE:
3236  return lwcollection_force_geodetic((LWCOLLECTION *)geom);
3237  default:
3238  lwerror("unsupported input geometry type: %d", lwgeom_get_type(geom));
3239  }
3240  return LW_FALSE;
3241 }
3242 
3243 
3245 {
3246  GEOGRAPHIC_POINT a, b;
3247  double za = 0.0, zb = 0.0;
3248  POINT4D p;
3249  uint32_t i;
3250  int hasz = LW_FALSE;
3251  double length = 0.0;
3252  double seglength = 0.0;
3253 
3254  /* Return zero on non-sensical inputs */
3255  if ( ! pa || pa->npoints < 2 )
3256  return 0.0;
3257 
3258  /* See if we have a third dimension */
3259  hasz = FLAGS_GET_Z(pa->flags);
3260 
3261  /* Initialize first point */
3262  getPoint4d_p(pa, 0, &p);
3263  geographic_point_init(p.x, p.y, &a);
3264  if ( hasz )
3265  za = p.z;
3266 
3267  /* Loop and sum the length for each segment */
3268  for ( i = 1; i < pa->npoints; i++ )
3269  {
3270  seglength = 0.0;
3271  getPoint4d_p(pa, i, &p);
3272  geographic_point_init(p.x, p.y, &b);
3273  if ( hasz )
3274  zb = p.z;
3275 
3276  /* Special sphere case */
3277  if ( s->a == s->b )
3278  seglength = s->radius * sphere_distance(&a, &b);
3279  /* Spheroid case */
3280  else
3281  seglength = spheroid_distance(&a, &b, s);
3282 
3283  /* Add in the vertical displacement if we're in 3D */
3284  if ( hasz )
3285  seglength = sqrt( (zb-za)*(zb-za) + seglength*seglength );
3286 
3287  /* Add this segment length to the total */
3288  length += seglength;
3289 
3290  /* B gets incremented in the next loop, so we save the value here */
3291  a = b;
3292  za = zb;
3293  }
3294  return length;
3295 }
3296 
3297 double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
3298 {
3299  int type;
3300  uint32_t i = 0;
3301  double length = 0.0;
3302 
3303  assert(geom);
3304 
3305  /* No area in nothing */
3306  if ( lwgeom_is_empty(geom) )
3307  return 0.0;
3308 
3309  type = geom->type;
3310 
3311  if ( type == POINTTYPE || type == MULTIPOINTTYPE )
3312  return 0.0;
3313 
3314  if ( type == LINETYPE )
3315  return ptarray_length_spheroid(((LWLINE*)geom)->points, s);
3316 
3317  if ( type == POLYGONTYPE )
3318  {
3319  LWPOLY *poly = (LWPOLY*)geom;
3320  for ( i = 0; i < poly->nrings; i++ )
3321  {
3322  length += ptarray_length_spheroid(poly->rings[i], s);
3323  }
3324  return length;
3325  }
3326 
3327  if ( type == TRIANGLETYPE )
3328  return ptarray_length_spheroid(((LWTRIANGLE*)geom)->points, s);
3329 
3330  if ( lwtype_is_collection( type ) )
3331  {
3332  LWCOLLECTION *col = (LWCOLLECTION*)geom;
3333 
3334  for ( i = 0; i < col->ngeoms; i++ )
3335  {
3336  length += lwgeom_length_spheroid(col->geoms[i], s);
3337  }
3338  return length;
3339  }
3340 
3341  lwerror("unsupported type passed to lwgeom_length_sphere");
3342  return 0.0;
3343 }
3344 
3351 static int
3353 {
3354 
3355  uint32_t i;
3356  POINT4D p;
3357  int altered = LW_FALSE;
3358  int rv = LW_FALSE;
3359  static double tolerance = 1e-10;
3360 
3361  if ( ! pa )
3362  lwerror("ptarray_nudge_geodetic called with null input");
3363 
3364  for(i = 0; i < pa->npoints; i++ )
3365  {
3366  getPoint4d_p(pa, i, &p);
3367  if ( p.x < -180.0 && (-180.0 - p.x < tolerance) )
3368  {
3369  p.x = -180.0;
3370  altered = LW_TRUE;
3371  }
3372  if ( p.x > 180.0 && (p.x - 180.0 < tolerance) )
3373  {
3374  p.x = 180.0;
3375  altered = LW_TRUE;
3376  }
3377  if ( p.y < -90.0 && (-90.0 - p.y < tolerance) )
3378  {
3379  p.y = -90.0;
3380  altered = LW_TRUE;
3381  }
3382  if ( p.y > 90.0 && (p.y - 90.0 < tolerance) )
3383  {
3384  p.y = 90.0;
3385  altered = LW_TRUE;
3386  }
3387  if ( altered == LW_TRUE )
3388  {
3389  ptarray_set_point4d(pa, i, &p);
3390  altered = LW_FALSE;
3391  rv = LW_TRUE;
3392  }
3393  }
3394  return rv;
3395 }
3396 
3403 int
3405 {
3406  int type;
3407  uint32_t i = 0;
3408  int rv = LW_FALSE;
3409 
3410  assert(geom);
3411 
3412  /* No points in nothing */
3413  if ( lwgeom_is_empty(geom) )
3414  return LW_FALSE;
3415 
3416  type = geom->type;
3417 
3418  if ( type == POINTTYPE )
3419  return ptarray_nudge_geodetic(((LWPOINT*)geom)->point);
3420 
3421  if ( type == LINETYPE )
3422  return ptarray_nudge_geodetic(((LWLINE*)geom)->points);
3423 
3424  if ( type == POLYGONTYPE )
3425  {
3426  LWPOLY *poly = (LWPOLY*)geom;
3427  for ( i = 0; i < poly->nrings; i++ )
3428  {
3429  int n = ptarray_nudge_geodetic(poly->rings[i]);
3430  rv = (rv == LW_TRUE ? rv : n);
3431  }
3432  return rv;
3433  }
3434 
3435  if ( type == TRIANGLETYPE )
3436  return ptarray_nudge_geodetic(((LWTRIANGLE*)geom)->points);
3437 
3438  if ( lwtype_is_collection( type ) )
3439  {
3440  LWCOLLECTION *col = (LWCOLLECTION*)geom;
3441 
3442  for ( i = 0; i < col->ngeoms; i++ )
3443  {
3444  int n = lwgeom_nudge_geodetic(col->geoms[i]);
3445  rv = (rv == LW_TRUE ? rv : n);
3446  }
3447  return rv;
3448  }
3449 
3450  lwerror("unsupported type (%s) passed to lwgeom_nudge_geodetic", lwtype_name(type));
3451  return rv;
3452 }
3453 
3454 
3458 static int
3459 point_in_cone(const POINT3D *A1, const POINT3D *A2, const POINT3D *P)
3460 {
3461  POINT3D AC; /* Center point of A1/A2 */
3462  double min_similarity, similarity;
3463 
3464  /* Boundary case */
3465  if (point3d_equals(A1, P) || point3d_equals(A2, P))
3466  return LW_TRUE;
3467 
3468  /* The normalized sum bisects the angle between start and end. */
3469  vector_sum(A1, A2, &AC);
3470  normalize(&AC);
3471 
3472  /* The projection of start onto the center defines the minimum similarity */
3473  min_similarity = dot_product(A1, &AC);
3474 
3475  /* If the edge is sufficiently curved, use the dot product test */
3476  if (fabs(1.0 - min_similarity) > 1e-10)
3477  {
3478  /* The projection of candidate p onto the center */
3479  similarity = dot_product(P, &AC);
3480 
3481  /* If the projection of the candidate is larger than */
3482  /* the projection of the start point, the candidate */
3483  /* must be closer to the center than the start, so */
3484  /* therefor inside the cone */
3485  if (similarity > min_similarity)
3486  {
3487  return LW_TRUE;
3488  }
3489  else
3490  {
3491  return LW_FALSE;
3492  }
3493  }
3494  else
3495  {
3496  /* Where the edge is very narrow, the dot product test */
3497  /* fails, but we can use the almost-planar nature of the */
3498  /* problem space then to test if the vector from the */
3499  /* candidate to the start point in a different direction */
3500  /* to the vector from candidate to end point */
3501  /* If so, then candidate is between start and end */
3502  POINT3D PA1, PA2;
3503  vector_difference(P, A1, &PA1);
3504  vector_difference(P, A2, &PA2);
3505  normalize(&PA1);
3506  normalize(&PA2);
3507  if (dot_product(&PA1, &PA2) < 0.0)
3508  {
3509  return LW_TRUE;
3510  }
3511  else
3512  {
3513  return LW_FALSE;
3514  }
3515  }
3516  return LW_FALSE;
3517 }
3518 
3519 
3524 static int
3525 dot_product_side(const POINT3D *p, const POINT3D *q)
3526 {
3527  double dp = dot_product(p, q);
3528 
3529  if ( FP_IS_ZERO(dp) )
3530  return 0;
3531 
3532  return dp < 0.0 ? -1 : 1;
3533 }
3534 
3539 uint32_t
3540 edge_intersects(const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2)
3541 {
3542  POINT3D AN, BN, VN; /* Normals to plane A and plane B */
3543  double ab_dot;
3544  int a1_side, a2_side, b1_side, b2_side;
3545  int rv = PIR_NO_INTERACT;
3546 
3547  /* Normals to the A-plane and B-plane */
3548  unit_normal(A1, A2, &AN);
3549  unit_normal(B1, B2, &BN);
3550 
3551  /* Are A-plane and B-plane basically the same? */
3552  ab_dot = dot_product(&AN, &BN);
3553  if ( FP_EQUALS(fabs(ab_dot), 1.0) )
3554  {
3555  /* Co-linear case */
3556  if ( point_in_cone(A1, A2, B1) || point_in_cone(A1, A2, B2) ||
3557  point_in_cone(B1, B2, A1) || point_in_cone(B1, B2, A2) )
3558  {
3559  rv |= PIR_INTERSECTS;
3560  rv |= PIR_COLINEAR;
3561  }
3562  return rv;
3563  }
3564 
3565  /* What side of plane-A and plane-B do the end points */
3566  /* of A and B fall? */
3567  a1_side = dot_product_side(&BN, A1);
3568  a2_side = dot_product_side(&BN, A2);
3569  b1_side = dot_product_side(&AN, B1);
3570  b2_side = dot_product_side(&AN, B2);
3571 
3572  /* Both ends of A on the same side of plane B. */
3573  if ( a1_side == a2_side && a1_side != 0 )
3574  {
3575  /* No intersection. */
3576  return PIR_NO_INTERACT;
3577  }
3578 
3579  /* Both ends of B on the same side of plane A. */
3580  if ( b1_side == b2_side && b1_side != 0 )
3581  {
3582  /* No intersection. */
3583  return PIR_NO_INTERACT;
3584  }
3585 
3586  /* A straddles B and B straddles A, so... */
3587  if ( a1_side != a2_side && (a1_side + a2_side) == 0 &&
3588  b1_side != b2_side && (b1_side + b2_side) == 0 )
3589  {
3590  /* Have to check if intersection point is inside both arcs */
3591  unit_normal(&AN, &BN, &VN);
3592  if ( point_in_cone(A1, A2, &VN) && point_in_cone(B1, B2, &VN) )
3593  {
3594  return PIR_INTERSECTS;
3595  }
3596 
3597  /* Have to check if intersection point is inside both arcs */
3598  vector_scale(&VN, -1);
3599  if ( point_in_cone(A1, A2, &VN) && point_in_cone(B1, B2, &VN) )
3600  {
3601  return PIR_INTERSECTS;
3602  }
3603 
3604  return PIR_NO_INTERACT;
3605  }
3606 
3607  /* The rest are all intersects variants... */
3608  rv |= PIR_INTERSECTS;
3609 
3610  /* A touches B */
3611  if ( a1_side == 0 )
3612  {
3613  /* Touches at A1, A2 is on what side? */
3614  rv |= (a2_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT);
3615  }
3616  else if ( a2_side == 0 )
3617  {
3618  /* Touches at A2, A1 is on what side? */
3619  rv |= (a1_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT);
3620  }
3621 
3622  /* B touches A */
3623  if ( b1_side == 0 )
3624  {
3625  /* Touches at B1, B2 is on what side? */
3626  rv |= (b2_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT);
3627  }
3628  else if ( b2_side == 0 )
3629  {
3630  /* Touches at B2, B1 is on what side? */
3631  rv |= (b1_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT);
3632  }
3633 
3634  return rv;
3635 }
3636 
3645 int ptarray_contains_point_sphere(const POINTARRAY *pa, const POINT2D *pt_outside, const POINT2D *pt_to_test)
3646 {
3647  POINT3D S1, S2; /* Stab line end points */
3648  POINT3D E1, E2; /* Edge end points (3-space) */
3649  POINT2D p; /* Edge end points (lon/lat) */
3650  uint32_t count = 0, i, inter;
3651 
3652  /* Null input, not enough points for a ring? You ain't closed! */
3653  if ( ! pa || pa->npoints < 4 )
3654  return LW_FALSE;
3655 
3656  /* Set up our stab line */
3657  ll2cart(pt_to_test, &S1);
3658  ll2cart(pt_outside, &S2);
3659 
3660  /* Initialize first point */
3661  getPoint2d_p(pa, 0, &p);
3662  ll2cart(&p, &E1);
3663 
3664  /* Walk every edge and see if the stab line hits it */
3665  for ( i = 1; i < pa->npoints; i++ )
3666  {
3667  LWDEBUGF(4, "testing edge (%d)", i);
3668  LWDEBUGF(4, " start point == POINT(%.12g %.12g)", p.x, p.y);
3669 
3670  /* Read next point. */
3671  getPoint2d_p(pa, i, &p);
3672  ll2cart(&p, &E2);
3673 
3674  /* Skip over too-short edges. */
3675  if ( point3d_equals(&E1, &E2) )
3676  {
3677  continue;
3678  }
3679 
3680  /* Our test point is on an edge end! Point is "in ring" by our definition */
3681  if ( point3d_equals(&S1, &E1) )
3682  {
3683  return LW_TRUE;
3684  }
3685 
3686  /* Calculate relationship between stab line and edge */
3687  inter = edge_intersects(&S1, &S2, &E1, &E2);
3688 
3689  /* We have some kind of interaction... */
3690  if ( inter & PIR_INTERSECTS )
3691  {
3692  /* If the stabline is touching the edge, that implies the test point */
3693  /* is on the edge, so we're done, the point is in (on) the ring. */
3694  if ( (inter & PIR_A_TOUCH_RIGHT) || (inter & PIR_A_TOUCH_LEFT) )
3695  {
3696  return LW_TRUE;
3697  }
3698 
3699  /* It's a touching interaction, disregard all the left-side ones. */
3700  /* It's a co-linear intersection, ignore those. */
3701  if ( inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR )
3702  {
3703  /* Do nothing, to avoid double counts. */
3704  LWDEBUGF(4," edge (%d) crossed, disregarding to avoid double count", i, count);
3705  }
3706  else
3707  {
3708  /* Increment crossingn count. */
3709  count++;
3710  LWDEBUGF(4," edge (%d) crossed, count == %d", i, count);
3711  }
3712  }
3713  else
3714  {
3715  LWDEBUGF(4," edge (%d) did not cross", i);
3716  }
3717 
3718  /* Increment to next edge */
3719  E1 = E2;
3720  }
3721 
3722  LWDEBUGF(4,"final count == %d", count);
3723 
3724  /* An odd number of crossings implies containment! */
3725  if ( count % 2 )
3726  {
3727  return LW_TRUE;
3728  }
3729 
3730  return LW_FALSE;
3731 }
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
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: g_box.c:264
void gbox_duplicate(const GBOX *original, GBOX *duplicate)
Copy the values of original GBOX into duplicate.
Definition: g_box.c:440
int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt)
Return true if the point is inside the gbox.
Definition: g_box.c:254
int gbox_merge_point3d(const POINT3D *p, GBOX *gbox)
Update the GBOX to be large enough to include itself and the new point.
Definition: g_box.c:235
int gbox_overlaps(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps, LW_FALSE otherwise.
Definition: g_box.c:290
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition: g_box.c:47
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
Definition: g_box.c:433
int gbox_init_point3d(const POINT3D *p, GBOX *gbox)
Initialize a GBOX using the values of the point.
Definition: g_box.c:246
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
Definition: g_box.c:399
uint8_t gflags(int hasz, int hasm, int geodetic)
Construct a new flags char.
Definition: g_util.c:145
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:170
void lwgeom_set_geodetic(LWGEOM *geom, int value)
Set the FLAGS geodetic bit on geometry an all sub-geometries and pointlists.
Definition: lwgeom.c:952
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:330
#define LW_FALSE
Definition: liblwgeom.h:77
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:300
#define COLLECTIONTYPE
Definition: liblwgeom.h:91
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
Definition: lwcollection.c:94
#define LW_FAILURE
Definition: liblwgeom.h:79
#define MULTILINETYPE
Definition: liblwgeom.h:89
#define LINETYPE
Definition: liblwgeom.h:86
uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition: ptarray.c:1750
#define LW_SUCCESS
Definition: liblwgeom.h:80
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:320
#define MULTIPOINTTYPE
Definition: liblwgeom.h:88
double lwpoint_get_x(const LWPOINT *point)
Definition: lwpoint.c:63
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwgeom.c:923
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:520
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:348
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
Definition: ptarray.c:62
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:930
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:1093
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:85
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an alloced string.
Definition: lwgeom.c:556
#define FLAGS_GET_Z(flags)
Macros for manipulating the 'flags' byte.
Definition: liblwgeom.h:140
#define TINTYPE
Definition: liblwgeom.h:99
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:90
void lwfree(void *mem)
Definition: lwutil.c:244
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:335
LWPOINT * lwpoint_construct(int srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:129
#define POLYGONTYPE
Definition: liblwgeom.h:87
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:97
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:70
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:218
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:141
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwgeom.c:1393
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:123
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition: lwgeom.c:482
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:156
#define TRIANGLETYPE
Definition: liblwgeom.h:98
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:187
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:224
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:76
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:206
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:937
LWLINE * lwline_construct(int srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:435
LWPOLY * lwpoly_construct_empty(int srid, char hasz, char hasm)
Definition: lwpoly.c:161
double lwpoint_get_y(const LWPOINT *point)
Definition: lwpoint.c:76
const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwgeom_api.c:374
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:318
int p4d_same(const POINT4D *p1, const POINT4D *p2)
Definition: lwalgorithm.c:31
int p3d_same(const POINT3D *p1, const POINT3D *p2)
Definition: lwalgorithm.c:40
#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:36
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:64
int ptarray_has_m(const POINTARRAY *pa)
Definition: ptarray.c:43
#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:1101
static int lwline_check_geodetic(const LWLINE *line)
Definition: lwgeodetic.c:3092
static int lwcollection_check_geodetic(const LWCOLLECTION *col)
Definition: lwgeodetic.c:3118
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:2518
void normalize(POINT3D *p)
Normalize to a unit vector.
Definition: lwgeodetic.c:615
static int lwpoly_check_geodetic(const LWPOLY *poly)
Definition: lwgeodetic.c:3098
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:2757
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:2715
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:1076
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:2099
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:3194
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:1743
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:2187
static int lwcollection_force_geodetic(LWCOLLECTION *col)
Definition: lwgeodetic.c:3208
static int lwtriangle_check_geodetic(const LWTRIANGLE *triangle)
Definition: lwgeodetic.c:3111
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:1552
double sphere_distance_cartesian(const POINT3D *s, const POINT3D *e)
Given two unit vectors, calculate their distance apart in radians.
Definition: lwgeodetic.c:967
static int ptarray_force_geodetic(POINTARRAY *pa)
Definition: lwgeodetic.c:3160
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:3188
static int lwcollection_calculate_gbox_geodetic(const LWCOLLECTION *coll, GBOX *gbox)
Definition: lwgeodetic.c:2993
double ptarray_area_sphere(const POINTARRAY *pa)
Returns the area of the ring (ring must be closed) in square radians (surface of the sphere is 4*PI).
Definition: lwgeodetic.c:1807
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:3459
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:2602
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:2696
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:3645
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:3028
static int ptarray_check_geodetic(const POINTARRAY *pa)
Definition: lwgeodetic.c:3068
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:1684
static int lwpoint_check_geodetic(const LWPOINT *point)
Definition: lwgeodetic.c:3086
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:775
static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
Definition: lwgeodetic.c:1835
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:3131
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:2660
int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
Calculate covers predicate for two lwgeoms on the sphere.
Definition: lwgeodetic.c:2413
double gbox_angular_width(const GBOX *gbox)
Returns the angular width (longitudinal span) of the box in radians.
Definition: lwgeodetic.c:215
static int lwpoint_force_geodetic(LWPOINT *point)
Definition: lwgeodetic.c:3182
int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside)
Definition: lwgeodetic.c:1527
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:1314
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:2950
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:788
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:1049
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:1487
int ptarray_calculate_gbox_geodetic(const POINTARRAY *pa, GBOX *gbox)
Calculate geodetic (x/y/z) box and add values to gbox.
Definition: lwgeodetic.c:2889
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:3525
double ptarray_length_spheroid(const POINTARRAY *pa, const SPHEROID *s)
Definition: lwgeodetic.c:3244
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:3222
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:1633
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:3404
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:948
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:975
int edge_calculate_gbox_slow(const GEOGRAPHIC_EDGE *e, GBOX *gbox)
Definition: lwgeodetic.c:1344
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:1127
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:2986
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:3540
double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
Calculate the geodetic length of a lwgeom on the unit sphere.
Definition: lwgeodetic.c:3297
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:1408
double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest)
Definition: lwgeodetic.c:1218
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 double sphere_signed_area(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c)
Computes the spherical area of a triangle.
Definition: lwgeodetic.c:741
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:2786
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:1034
static double sphere_angle(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c)
Returns the angle in radians at point B of the triangle formed by A-B-C.
Definition: lwgeodetic.c:721
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:1269
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:3352
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:2156
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:2944
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:835
double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid)
Calculate the area of an LWGEOM.
Definition: lwgeodetic.c:2031
static int lwpolygon_calculate_gbox_geodetic(const LWPOLY *poly, GBOX *gbox)
Definition: lwgeodetic.c:2956
int getPoint2d_p_ro(const POINTARRAY *pa, uint32_t n, POINT2D **point)
This function can only be used on LWGEOM that is built on top of GSERIALIZED, otherwise alignment err...
Definition: lwgeodetic.c:2875
#define rad2deg(r)
Definition: lwgeodetic.h:80
#define POW2(x)
Definition: lwgeodetic.h:47
#define PIR_A_TOUCH_LEFT
Definition: lwgeodetic.h:90
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:191
#define PIR_COLINEAR
Definition: lwgeodetic.h:88
#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:364
#define PIR_INTERSECTS
Definition: lwgeodetic.h:87
double spheroid_direction(const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid)
Computes the direction of the geodesic joining two points on the spheroid.
Definition: lwspheroid.c:287
#define deg2rad(d)
Conversion functions.
Definition: lwgeodetic.h:79
#define PIR_A_TOUCH_RIGHT
Definition: lwgeodetic.h:89
#define PIR_B_TOUCH_RIGHT
Definition: lwgeodetic.h:91
#define PIR_B_TOUCH_LEFT
Definition: lwgeodetic.h:92
#define PIR_NO_INTERACT
Bitmask elements for edge_intersects() return value.
Definition: lwgeodetic.h:86
Datum distance(PG_FUNCTION_ARGS)
Datum area(PG_FUNCTION_ARGS)
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
int count
Definition: genraster.py:56
type
Definition: ovdump.py:41
double ymax
Definition: liblwgeom.h:298
double zmax
Definition: liblwgeom.h:300
double xmax
Definition: liblwgeom.h:296
double zmin
Definition: liblwgeom.h:299
double ymin
Definition: liblwgeom.h:297
double xmin
Definition: liblwgeom.h:295
uint8_t flags
Definition: liblwgeom.h:294
GEOGRAPHIC_POINT start
Definition: lwgeodetic.h:63
GEOGRAPHIC_POINT end
Definition: lwgeodetic.h:64
Two-point great circle segment from a to b.
Definition: lwgeodetic.h:62
Point in spherical coordinates on the world.
Definition: lwgeodetic.h:53
uint32_t ngeoms
Definition: liblwgeom.h:510
LWGEOM ** geoms
Definition: liblwgeom.h:512
uint8_t type
Definition: liblwgeom.h:399
GBOX * bbox
Definition: liblwgeom.h:401
uint8_t flags
Definition: liblwgeom.h:400
int32_t srid
Definition: liblwgeom.h:402
POINTARRAY * points
Definition: liblwgeom.h:425
POINTARRAY * point
Definition: liblwgeom.h:414
uint8_t type
Definition: liblwgeom.h:410
POINTARRAY ** rings
Definition: liblwgeom.h:460
uint32_t nrings
Definition: liblwgeom.h:458
GBOX * bbox
Definition: liblwgeom.h:456
POINTARRAY * points
Definition: liblwgeom.h:436
double y
Definition: liblwgeom.h:331
double x
Definition: liblwgeom.h:331
double z
Definition: liblwgeom.h:343
double x
Definition: liblwgeom.h:343
double y
Definition: liblwgeom.h:343
double m
Definition: liblwgeom.h:355
double x
Definition: liblwgeom.h:355
double z
Definition: liblwgeom.h:355
double y
Definition: liblwgeom.h:355
uint32_t npoints
Definition: liblwgeom.h:374
uint8_t flags
Definition: liblwgeom.h:372
double radius
Definition: liblwgeom.h:321
unsigned int uint32_t
Definition: uthash.h:78
unsigned char uint8_t
Definition: uthash.h:79