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